vovk-cli 0.0.1-draft.33 → 0.0.1-draft.330
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +29 -1
- package/client-templates/cjs/index.cjs.ejs +19 -0
- package/client-templates/cjs/index.d.cts.ejs +25 -0
- package/client-templates/mixins/mixins.d.ts.ejs +64 -0
- package/client-templates/mixins/mixins.json.ejs +1 -0
- package/client-templates/mjs/index.d.mts.ejs +25 -0
- package/client-templates/mjs/index.mjs.ejs +23 -0
- package/client-templates/packageJson/package.json.ejs +1 -0
- package/client-templates/readme/README.md.ejs +38 -0
- package/client-templates/schemaCjs/schema.cjs.ejs +26 -0
- package/client-templates/schemaCjs/schema.d.cts.ejs +10 -0
- package/client-templates/schemaJson/schema.json.ejs +1 -0
- package/client-templates/schemaTs/schema.ts.ejs +35 -0
- package/client-templates/ts/index.ts.ejs +33 -0
- package/dist/bundle/index.d.mts +8 -0
- package/dist/bundle/index.mjs +90 -0
- package/dist/dev/diffSegmentSchema.d.mts +36 -0
- package/dist/dev/{diffSchema.mjs → diffSegmentSchema.mjs} +3 -11
- package/dist/dev/ensureSchemaFiles.d.mts +3 -0
- package/dist/dev/ensureSchemaFiles.mjs +15 -31
- package/dist/dev/index.d.mts +5 -1
- package/dist/dev/index.mjs +181 -78
- package/dist/dev/logDiffResult.d.mts +1 -1
- package/dist/dev/logDiffResult.mjs +6 -43
- package/dist/dev/writeMetaJson.d.mts +2 -0
- package/dist/dev/writeMetaJson.mjs +17 -0
- package/dist/dev/writeOneSegmentSchemaFile.d.mts +12 -0
- package/dist/dev/{writeOneSchemaFile.mjs → writeOneSegmentSchemaFile.mjs} +10 -6
- package/dist/generate/ensureClient.d.mts +3 -0
- package/dist/generate/ensureClient.mjs +32 -0
- package/dist/generate/generate.d.mts +16 -0
- package/dist/generate/generate.mjs +295 -0
- package/dist/generate/getClientTemplateFiles.d.mts +20 -0
- package/dist/generate/getClientTemplateFiles.mjs +81 -0
- package/dist/generate/getProjectFullSchema.d.mts +7 -0
- package/dist/generate/getProjectFullSchema.mjs +65 -0
- package/dist/generate/getTemplateClientImports.d.mts +18 -0
- package/dist/generate/getTemplateClientImports.mjs +38 -0
- package/dist/generate/index.d.mts +33 -0
- package/dist/generate/index.mjs +189 -0
- package/dist/generate/mergePackages.d.mts +7 -0
- package/dist/generate/mergePackages.mjs +55 -0
- package/dist/generate/writeOneClientFile.d.mts +37 -0
- package/dist/generate/writeOneClientFile.mjs +122 -0
- package/dist/getProjectInfo/getConfig/getConfigAbsolutePaths.d.mts +5 -0
- package/dist/getProjectInfo/{getConfigAbsolutePaths.mjs → getConfig/getConfigAbsolutePaths.mjs} +4 -1
- package/dist/getProjectInfo/{getRelativeSrcRoot.d.mts → getConfig/getRelativeSrcRoot.d.mts} +1 -1
- package/dist/getProjectInfo/{getRelativeSrcRoot.mjs → getConfig/getRelativeSrcRoot.mjs} +2 -2
- package/dist/getProjectInfo/getConfig/getTemplateDefs.d.mts +22 -0
- package/dist/getProjectInfo/getConfig/getTemplateDefs.mjs +145 -0
- package/dist/getProjectInfo/{getUserConfig.d.mts → getConfig/getUserConfig.d.mts} +3 -2
- package/dist/getProjectInfo/{getUserConfig.mjs → getConfig/getUserConfig.mjs} +6 -4
- package/dist/getProjectInfo/{importUncachedModule.mjs → getConfig/importUncachedModule.mjs} +1 -4
- package/dist/getProjectInfo/getConfig/index.d.mts +122 -0
- package/dist/getProjectInfo/getConfig/index.mjs +98 -0
- package/dist/getProjectInfo/index.d.mts +12 -9
- package/dist/getProjectInfo/index.mjs +21 -22
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +102 -36
- package/dist/init/createConfig.d.mts +2 -2
- package/dist/init/createConfig.mjs +14 -12
- package/dist/init/getTemplateFilesFromPackage.mjs +10 -5
- package/dist/init/index.d.mts +2 -2
- package/dist/init/index.mjs +81 -60
- package/dist/init/installDependencies.mjs +4 -2
- package/dist/init/logUpdateDependenciesError.d.mts +3 -1
- package/dist/init/logUpdateDependenciesError.mjs +7 -1
- package/dist/init/updateDependenciesWithoutInstalling.mjs +39 -9
- package/dist/init/updateNPMScripts.d.mts +4 -1
- package/dist/init/updateNPMScripts.mjs +14 -7
- package/dist/init/updateTypeScriptConfig.d.mts +4 -1
- package/dist/init/updateTypeScriptConfig.mjs +11 -7
- package/dist/initProgram.d.mts +1 -1
- package/dist/initProgram.mjs +16 -16
- package/dist/locateSegments.d.mts +8 -1
- package/dist/locateSegments.mjs +14 -4
- package/dist/new/addClassToSegmentCode.d.mts +1 -2
- package/dist/new/addClassToSegmentCode.mjs +3 -3
- package/dist/new/index.d.mts +1 -1
- package/dist/new/index.mjs +3 -2
- package/dist/new/newModule.d.mts +2 -1
- package/dist/new/newModule.mjs +18 -16
- package/dist/new/newSegment.d.mts +2 -1
- package/dist/new/newSegment.mjs +19 -10
- package/dist/new/render.d.mts +7 -3
- package/dist/new/render.mjs +29 -8
- package/dist/types.d.mts +51 -40
- package/dist/utils/compileJSONSchemaToTypeScriptType.d.mts +5 -0
- package/dist/utils/compileJSONSchemaToTypeScriptType.mjs +9 -0
- package/dist/utils/compileTs.d.mts +12 -0
- package/dist/utils/compileTs.mjs +261 -0
- package/dist/utils/debounceWithArgs.d.mts +2 -2
- package/dist/utils/debounceWithArgs.mjs +24 -6
- package/dist/utils/formatLoggedSegmentName.d.mts +3 -1
- package/dist/utils/formatLoggedSegmentName.mjs +3 -2
- package/dist/utils/getPackageJson.d.mts +3 -0
- package/dist/utils/getPackageJson.mjs +22 -0
- package/dist/utils/getPublicModuleNameFromPath.d.mts +4 -0
- package/dist/utils/getPublicModuleNameFromPath.mjs +9 -0
- package/dist/utils/normalizeOpenAPIMixins.d.mts +7 -0
- package/dist/utils/normalizeOpenAPIMixins.mjs +67 -0
- package/dist/utils/pickSegmentFullSchema.d.mts +3 -0
- package/dist/utils/pickSegmentFullSchema.mjs +15 -0
- package/dist/utils/removeUnlistedDirectories.d.mts +10 -0
- package/dist/utils/removeUnlistedDirectories.mjs +61 -0
- package/dist/utils/resolveAbsoluteModulePath.d.mts +2 -0
- package/dist/utils/resolveAbsoluteModulePath.mjs +32 -0
- package/module-templates/controller.ts.ejs +56 -0
- package/module-templates/service.ts.ejs +28 -0
- package/package.json +35 -22
- package/dist/dev/diffSchema.d.mts +0 -43
- package/dist/dev/ensureClient.d.mts +0 -5
- package/dist/dev/ensureClient.mjs +0 -31
- package/dist/dev/isMetadataEmpty.d.mts +0 -2
- package/dist/dev/isMetadataEmpty.mjs +0 -4
- package/dist/dev/writeOneSchemaFile.d.mts +0 -11
- package/dist/generateClient.d.mts +0 -7
- package/dist/generateClient.mjs +0 -97
- package/dist/getProjectInfo/getConfig.d.mts +0 -11
- package/dist/getProjectInfo/getConfig.mjs +0 -29
- package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +0 -4
- package/dist/postinstall.d.mts +0 -1
- package/dist/postinstall.mjs +0 -24
- package/templates/controller.ejs +0 -51
- package/templates/service.ejs +0 -27
- package/templates/worker.ejs +0 -24
- /package/dist/getProjectInfo/{importUncachedModule.d.mts → getConfig/importUncachedModule.d.mts} +0 -0
- /package/dist/getProjectInfo/{importUncachedModuleWorker.d.mts → getConfig/importUncachedModuleWorker.d.mts} +0 -0
- /package/dist/getProjectInfo/{importUncachedModuleWorker.mjs → getConfig/importUncachedModuleWorker.mjs} +0 -0
package/dist/new/newModule.mjs
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
|
+
import { getTsconfig } from 'get-tsconfig';
|
|
3
4
|
import render from './render.mjs';
|
|
4
5
|
import addClassToSegmentCode from './addClassToSegmentCode.mjs';
|
|
5
6
|
import getProjectInfo from '../getProjectInfo/index.mjs';
|
|
6
|
-
import locateSegments from '../locateSegments.mjs';
|
|
7
7
|
import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
|
|
8
8
|
import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
|
|
9
9
|
import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
|
|
10
10
|
import prettify from '../utils/prettify.mjs';
|
|
11
|
+
import resolveAbsoluteModulePath from '../utils/resolveAbsoluteModulePath.mjs';
|
|
12
|
+
import { locateSegments } from '../locateSegments.mjs';
|
|
11
13
|
function splitByLast(str, delimiter = '/') {
|
|
12
14
|
const index = str.lastIndexOf(delimiter);
|
|
13
15
|
if (index === -1) {
|
|
@@ -18,19 +20,19 @@ function splitByLast(str, delimiter = '/') {
|
|
|
18
20
|
const after = str.substring(index + delimiter.length);
|
|
19
21
|
return [before, after];
|
|
20
22
|
}
|
|
21
|
-
export default async function newModule({ what, moduleNameWithOptionalSegment, dryRun, dir: dirFlag, templates: templatesFlag, noSegmentUpdate, overwrite, }) {
|
|
22
|
-
const { config, log,
|
|
23
|
-
|
|
23
|
+
export default async function newModule({ what, moduleNameWithOptionalSegment, dryRun, dir: dirFlag, templates: templatesFlag, noSegmentUpdate, overwrite, empty, }) {
|
|
24
|
+
const { config, log, cwd, apiDirAbsolutePath } = await getProjectInfo();
|
|
25
|
+
const segments = await locateSegments({ dir: apiDirAbsolutePath, config, log });
|
|
26
|
+
const isNodeNextResolution = ['node16', 'nodenext'].includes((await getTsconfig(cwd)?.config?.compilerOptions?.moduleResolution?.toLowerCase()) ?? '');
|
|
27
|
+
let templates = config.moduleTemplates;
|
|
24
28
|
const [segmentName, moduleName] = splitByLast(moduleNameWithOptionalSegment);
|
|
25
|
-
// replace c by controller, s by service,
|
|
29
|
+
// replace c by controller, s by service, everything else keeps the same
|
|
26
30
|
what = what.map((s) => {
|
|
27
31
|
switch (s) {
|
|
28
32
|
case 'c':
|
|
29
33
|
return 'controller';
|
|
30
34
|
case 's':
|
|
31
35
|
return 'service';
|
|
32
|
-
case 'w':
|
|
33
|
-
return 'worker';
|
|
34
36
|
default:
|
|
35
37
|
return s;
|
|
36
38
|
}
|
|
@@ -49,16 +51,13 @@ export default async function newModule({ what, moduleNameWithOptionalSegment, d
|
|
|
49
51
|
throw new Error(`Template for "${type}" not found`);
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
|
-
const segments = await locateSegments(apiDir);
|
|
53
54
|
const segment = segments.find((s) => s.segmentName === segmentName);
|
|
54
55
|
if (!segment) {
|
|
55
56
|
throw new Error(`Unable to create module. Segment "${segmentName}" not found. Run "vovk new segment ${segmentName}" to create it`);
|
|
56
57
|
}
|
|
57
58
|
for (const type of what) {
|
|
58
59
|
const templatePath = templates[type];
|
|
59
|
-
const templateAbsolutePath = templatePath
|
|
60
|
-
? path.resolve(cwd, templatePath)
|
|
61
|
-
: path.resolve(cwd, './node_modules', templatePath);
|
|
60
|
+
const templateAbsolutePath = resolveAbsoluteModulePath(templatePath, cwd);
|
|
62
61
|
const templateCode = await fs.readFile(templateAbsolutePath, 'utf-8');
|
|
63
62
|
const { dir: renderedDir, fileName, sourceName, compiledName, code, } = await render(templateCode, {
|
|
64
63
|
cwd,
|
|
@@ -66,6 +65,9 @@ export default async function newModule({ what, moduleNameWithOptionalSegment, d
|
|
|
66
65
|
withService: what.includes('service'),
|
|
67
66
|
segmentName,
|
|
68
67
|
moduleName,
|
|
68
|
+
empty,
|
|
69
|
+
templateFileName: templateAbsolutePath,
|
|
70
|
+
isNodeNextResolution,
|
|
69
71
|
});
|
|
70
72
|
const dir = dirFlag || renderedDir;
|
|
71
73
|
if (!dir) {
|
|
@@ -84,24 +86,24 @@ export default async function newModule({ what, moduleNameWithOptionalSegment, d
|
|
|
84
86
|
else {
|
|
85
87
|
await fs.mkdir(absoluteModuleDir, { recursive: true });
|
|
86
88
|
await fs.writeFile(absoluteModulePath, prettiedCode);
|
|
87
|
-
log.info(`Created ${chalkHighlightThing(
|
|
89
|
+
log.info(`Created${empty ? ' empty' : ''} ${chalkHighlightThing(absoluteModulePath)} using ${chalkHighlightThing(`"${type}"`)} template for ${formatLoggedSegmentName(segmentName)}`);
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
|
-
if (type === 'controller'
|
|
92
|
+
if (type === 'controller') {
|
|
91
93
|
if (!sourceName) {
|
|
92
94
|
throw new Error(`The template for "${type}" does not provide a sourceName`);
|
|
93
95
|
}
|
|
94
96
|
if (!compiledName) {
|
|
95
|
-
throw new Error(
|
|
97
|
+
throw new Error(`The template for "${type}" does not provide a compiledName`);
|
|
96
98
|
}
|
|
97
99
|
const { routeFilePath } = segment;
|
|
98
100
|
const segmentSourceCode = await fs.readFile(routeFilePath, 'utf-8');
|
|
99
|
-
|
|
101
|
+
let importPath = path.relative(path.dirname(routeFilePath) + '/', absoluteModulePath).replace(/\.(ts|tsx)$/, '');
|
|
102
|
+
importPath += isNodeNextResolution ? '.ts' : '';
|
|
100
103
|
if (!noSegmentUpdate) {
|
|
101
104
|
const newSegmentCode = await prettify(addClassToSegmentCode(segmentSourceCode, {
|
|
102
105
|
sourceName,
|
|
103
106
|
compiledName,
|
|
104
|
-
type,
|
|
105
107
|
importPath,
|
|
106
108
|
}), routeFilePath);
|
|
107
109
|
if (!dryRun) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export default function newSegment({ segmentName, overwrite, dryRun, }: {
|
|
1
|
+
export default function newSegment({ segmentName, isStaticSegment, overwrite, dryRun, }: {
|
|
2
2
|
segmentName: string;
|
|
3
|
+
isStaticSegment?: boolean;
|
|
3
4
|
overwrite?: boolean;
|
|
4
5
|
dryRun?: boolean;
|
|
5
6
|
}): Promise<void>;
|
package/dist/new/newSegment.mjs
CHANGED
|
@@ -5,23 +5,30 @@ import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
|
|
|
5
5
|
import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
|
|
6
6
|
import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
|
|
7
7
|
import prettify from '../utils/prettify.mjs';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
export default async function newSegment({ segmentName, isStaticSegment, overwrite, dryRun, }) {
|
|
10
|
+
const { apiDirAbsolutePath, log, config } = await getProjectInfo();
|
|
11
|
+
if (!apiDirAbsolutePath) {
|
|
12
|
+
throw new Error('No API directory found. Please ensure you are in a Nest.js project.');
|
|
13
|
+
}
|
|
14
|
+
const absoluteSegmentRoutePath = path.join(apiDirAbsolutePath, segmentName, '[[...vovk]]/route.ts');
|
|
11
15
|
if (!overwrite && (await getFileSystemEntryType(absoluteSegmentRoutePath))) {
|
|
12
16
|
throw new Error(`Unable to create new segment. ${formatLoggedSegmentName(segmentName, { upperFirst: true })} already exists.`);
|
|
13
17
|
}
|
|
14
|
-
const code = await prettify(`import {
|
|
18
|
+
const code = await prettify(`import { initSegment${isStaticSegment ? ', generateStaticAPI' : ''} } from 'vovk';
|
|
15
19
|
|
|
16
20
|
const controllers = {};
|
|
17
|
-
const workers = {};
|
|
18
21
|
|
|
19
22
|
export type Controllers = typeof controllers;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export
|
|
23
|
+
${isStaticSegment
|
|
24
|
+
? `
|
|
25
|
+
export function generateStaticParams() {
|
|
26
|
+
return generateStaticAPI(controllers);
|
|
27
|
+
}
|
|
28
|
+
`
|
|
29
|
+
: ''}
|
|
30
|
+
export const { GET${isStaticSegment ? '' : ', POST, PATCH, PUT, HEAD, OPTIONS, DELETE'} } = initSegment({
|
|
23
31
|
${segmentName ? ` segmentName: '${segmentName}',\n` : ''} emitSchema: true,
|
|
24
|
-
workers,
|
|
25
32
|
controllers,
|
|
26
33
|
});
|
|
27
34
|
`, absoluteSegmentRoutePath);
|
|
@@ -29,5 +36,7 @@ ${segmentName ? ` segmentName: '${segmentName}',\n` : ''} emitSchema: true,
|
|
|
29
36
|
await fs.mkdir(path.dirname(absoluteSegmentRoutePath), { recursive: true });
|
|
30
37
|
await fs.writeFile(absoluteSegmentRoutePath, code);
|
|
31
38
|
}
|
|
32
|
-
log.info(`${formatLoggedSegmentName(segmentName, { upperFirst: true })} created at ${absoluteSegmentRoutePath}
|
|
39
|
+
log.info(`${formatLoggedSegmentName(segmentName, { upperFirst: true, isStatic: isStaticSegment })} created at ${absoluteSegmentRoutePath}.`);
|
|
40
|
+
const dir = chalk.cyanBright([segmentName, 'thing'].filter(Boolean).join('/'));
|
|
41
|
+
log.info(`Run ${chalkHighlightThing(`npx vovk new service controller ${dir}`)} to create a new controller with a service at ${path.join(config.modulesDir, dir)} folder for this segment`);
|
|
33
42
|
}
|
package/dist/new/render.d.mts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { VovkStrictConfig } from 'vovk';
|
|
2
|
+
import type { VovkModuleRenderResult } from '../types.mjs';
|
|
3
|
+
export default function render(codeTemplate: string, { config, withService, segmentName, moduleName, empty, templateFileName, isNodeNextResolution, }: {
|
|
3
4
|
cwd: string;
|
|
4
|
-
config:
|
|
5
|
+
config: VovkStrictConfig;
|
|
5
6
|
withService: boolean;
|
|
6
7
|
segmentName: string;
|
|
7
8
|
moduleName: string;
|
|
9
|
+
empty?: boolean;
|
|
10
|
+
templateFileName: string;
|
|
11
|
+
isNodeNextResolution: boolean;
|
|
8
12
|
}): Promise<VovkModuleRenderResult>;
|
package/dist/new/render.mjs
CHANGED
|
@@ -4,25 +4,46 @@ import _ from 'lodash';
|
|
|
4
4
|
import pluralize from 'pluralize';
|
|
5
5
|
import addCommonTerms from './addCommonTerms.mjs';
|
|
6
6
|
addCommonTerms();
|
|
7
|
-
export default async function render(codeTemplate, { config, withService, segmentName, moduleName, }) {
|
|
7
|
+
export default async function render(codeTemplate, { config, withService, segmentName, moduleName, empty, templateFileName, isNodeNextResolution, }) {
|
|
8
8
|
const getModuleDirName = (givenSegmentName, givenModuleName) => [config.modulesDir, givenSegmentName || config.rootSegmentModulesDirName, _.camelCase(givenModuleName)]
|
|
9
9
|
.filter(Boolean)
|
|
10
10
|
.join('/');
|
|
11
|
-
const
|
|
12
|
-
|
|
11
|
+
const theThing = _.camelCase(moduleName);
|
|
12
|
+
const TheThing = _.upperFirst(theThing);
|
|
13
|
+
const the_thing = _.snakeCase(moduleName);
|
|
14
|
+
const THE_THING = _.toUpper(the_thing);
|
|
15
|
+
const the__thing = _.kebabCase(moduleName);
|
|
16
|
+
const t = {
|
|
17
|
+
// module name variations
|
|
18
|
+
moduleName,
|
|
19
|
+
theThing,
|
|
20
|
+
theThings: pluralize(theThing),
|
|
21
|
+
TheThing,
|
|
22
|
+
TheThings: pluralize(TheThing),
|
|
23
|
+
the_thing,
|
|
24
|
+
the_things: pluralize(the_thing),
|
|
25
|
+
THE_THING,
|
|
26
|
+
THE_THINGS: pluralize(THE_THING),
|
|
27
|
+
'the-thing': the__thing,
|
|
28
|
+
'the-things': pluralize(the__thing),
|
|
29
|
+
// data
|
|
13
30
|
config,
|
|
14
31
|
withService,
|
|
15
32
|
segmentName,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
33
|
+
nodeNextResolutionExt: {
|
|
34
|
+
ts: isNodeNextResolution ? '.ts' : '',
|
|
35
|
+
js: isNodeNextResolution ? '.js' : '',
|
|
36
|
+
cjs: isNodeNextResolution ? '.cjs' : '',
|
|
37
|
+
mjs: isNodeNextResolution ? '.mjs' : '',
|
|
38
|
+
},
|
|
39
|
+
defaultDir: getModuleDirName(segmentName, theThing),
|
|
19
40
|
// libraries
|
|
20
41
|
_, // lodash
|
|
21
42
|
pluralize,
|
|
22
43
|
};
|
|
23
|
-
const parsed = matter((await ejs.render(codeTemplate,
|
|
44
|
+
const parsed = matter((await ejs.render(codeTemplate, { t }, { async: true, filename: templateFileName })).trim());
|
|
24
45
|
const { dir, fileName, sourceName, compiledName } = parsed.data;
|
|
25
|
-
const code = parsed.content;
|
|
46
|
+
const code = empty ? (sourceName ? `export default class ${sourceName} {}` : '') : parsed.content;
|
|
26
47
|
return {
|
|
27
48
|
dir,
|
|
28
49
|
fileName,
|
package/dist/types.d.mts
CHANGED
|
@@ -1,42 +1,5 @@
|
|
|
1
1
|
import type { LogLevelNames } from 'loglevel';
|
|
2
|
-
|
|
3
|
-
export type VovkDevEnv = {
|
|
4
|
-
PORT?: string;
|
|
5
|
-
VOVK_CLIENT_OUT_DIR?: string;
|
|
6
|
-
VOVK_SCHEMA_OUT_DIR?: string;
|
|
7
|
-
VOVK_FETCHER?: string;
|
|
8
|
-
VOVK_VALIDATE_ON_CLIENT?: string;
|
|
9
|
-
VOVK_MODULES_DIR?: string;
|
|
10
|
-
VOVK_VALIDATION_LIBRARY?: string;
|
|
11
|
-
VOVK_ORIGIN?: string;
|
|
12
|
-
VOVK_ROOT_ENTRY?: string;
|
|
13
|
-
VOVK_API_ENTRY_POINT?: string;
|
|
14
|
-
VOVK_ROOT_SEGMENT_MODULES_DIR_NAME?: string;
|
|
15
|
-
VOVK_LOG_LEVEL?: LogLevelNames;
|
|
16
|
-
VOVK_PRETTIFY_CLIENT?: string;
|
|
17
|
-
VOVK_DEV_HTTPS?: string;
|
|
18
|
-
__VOVK_START_WATCHER_IN_STANDALONE_MODE__?: 'true';
|
|
19
|
-
};
|
|
20
|
-
export type VovkConfig = {
|
|
21
|
-
clientOutDir?: string;
|
|
22
|
-
schemaOutDir?: string;
|
|
23
|
-
fetcher?: string;
|
|
24
|
-
validateOnClient?: string | null;
|
|
25
|
-
modulesDir?: string;
|
|
26
|
-
validationLibrary?: string | null;
|
|
27
|
-
rootEntry?: string;
|
|
28
|
-
origin?: string;
|
|
29
|
-
rootSegmentModulesDirName?: string;
|
|
30
|
-
logLevel?: LogLevelNames;
|
|
31
|
-
prettifyClient?: boolean;
|
|
32
|
-
devHttps?: boolean;
|
|
33
|
-
templates?: {
|
|
34
|
-
service?: string;
|
|
35
|
-
controller?: string;
|
|
36
|
-
worker?: string;
|
|
37
|
-
[key: string]: string | undefined;
|
|
38
|
-
};
|
|
39
|
-
};
|
|
2
|
+
import type { VovkStrictConfig } from 'vovk';
|
|
40
3
|
export type VovkModuleRenderResult = {
|
|
41
4
|
fileName: string;
|
|
42
5
|
dir: string;
|
|
@@ -45,10 +8,44 @@ export type VovkModuleRenderResult = {
|
|
|
45
8
|
code: string;
|
|
46
9
|
};
|
|
47
10
|
export interface DevOptions {
|
|
11
|
+
schemaOut?: string;
|
|
48
12
|
nextDev?: boolean;
|
|
13
|
+
exit?: boolean;
|
|
14
|
+
devHttps?: boolean;
|
|
49
15
|
}
|
|
50
16
|
export interface GenerateOptions {
|
|
51
|
-
|
|
17
|
+
prettify?: boolean;
|
|
18
|
+
configPath?: string;
|
|
19
|
+
schemaPath?: string;
|
|
20
|
+
origin?: string;
|
|
21
|
+
openapiSpec?: string[];
|
|
22
|
+
openapiGetModuleName?: string[];
|
|
23
|
+
openapiGetMethodName?: string[];
|
|
24
|
+
openapiRootUrl?: string[];
|
|
25
|
+
openapiMixinName?: string[];
|
|
26
|
+
openapiFallback?: string[];
|
|
27
|
+
watch?: boolean | string;
|
|
28
|
+
composedFrom?: string[];
|
|
29
|
+
composedOut?: string;
|
|
30
|
+
composedOnly?: boolean;
|
|
31
|
+
composedIncludeSegments?: string[];
|
|
32
|
+
composedExcludeSegments?: string[];
|
|
33
|
+
segmentedFrom?: string[];
|
|
34
|
+
segmentedOut?: string;
|
|
35
|
+
segmentedOnly?: boolean;
|
|
36
|
+
segmentedIncludeSegments?: string[];
|
|
37
|
+
segmentedExcludeSegments?: string[];
|
|
38
|
+
}
|
|
39
|
+
export interface BundleOptions extends Partial<Pick<VovkStrictConfig['bundle'], 'prebundleOutDir' | 'keepPrebundleDir' | 'includeSegments' | 'excludeSegments'>> {
|
|
40
|
+
config?: string;
|
|
41
|
+
schema?: string;
|
|
42
|
+
outDir?: string;
|
|
43
|
+
origin?: string;
|
|
44
|
+
openapiSpec?: string[];
|
|
45
|
+
openapiGetModuleName?: string[];
|
|
46
|
+
openapiGetMethodName?: string[];
|
|
47
|
+
openapiRootUrl?: string[];
|
|
48
|
+
openapiMixinName?: string[];
|
|
52
49
|
}
|
|
53
50
|
export interface InitOptions {
|
|
54
51
|
yes?: boolean;
|
|
@@ -61,8 +58,8 @@ export interface InitOptions {
|
|
|
61
58
|
updateTsConfig?: boolean;
|
|
62
59
|
updateScripts?: 'implicit' | 'explicit';
|
|
63
60
|
validationLibrary?: string | null;
|
|
64
|
-
validateOnClient?: boolean;
|
|
65
61
|
dryRun?: boolean;
|
|
62
|
+
lang?: string[];
|
|
66
63
|
channel?: 'latest' | 'beta' | 'draft';
|
|
67
64
|
}
|
|
68
65
|
export interface NewOptions {
|
|
@@ -71,4 +68,18 @@ export interface NewOptions {
|
|
|
71
68
|
dir?: string;
|
|
72
69
|
overwrite?: boolean;
|
|
73
70
|
noSegmentUpdate?: boolean;
|
|
71
|
+
empty?: boolean;
|
|
72
|
+
static?: boolean;
|
|
74
73
|
}
|
|
74
|
+
export type VovkEnv = {
|
|
75
|
+
PORT?: string;
|
|
76
|
+
VOVK_SCHEMA_OUT_DIR?: string;
|
|
77
|
+
VOVK_ORIGIN?: string;
|
|
78
|
+
VOVK_ROOT_ENTRY?: string;
|
|
79
|
+
VOVK_API_ENTRY_POINT?: string;
|
|
80
|
+
VOVK_LOG_LEVEL?: LogLevelNames;
|
|
81
|
+
__VOVK_START_WATCHER_IN_STANDALONE_MODE__?: 'true';
|
|
82
|
+
__VOVK_SCHEMA_OUT_FLAG__?: string;
|
|
83
|
+
__VOVK_DEV_HTTPS_FLAG__?: 'true' | 'false';
|
|
84
|
+
__VOVK_EXIT__?: 'true' | 'false';
|
|
85
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { OpenAPIObject } from 'openapi3-ts/oas31';
|
|
2
|
+
import type { JSONSchema7 } from 'json-schema';
|
|
3
|
+
export declare function compileJSONSchemaToTypeScriptType(schema: JSONSchema7, typeName: string, components?: NonNullable<OpenAPIObject['components']>, options?: {
|
|
4
|
+
dontCreateRefTypes?: boolean;
|
|
5
|
+
}): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { compileTs } from './compileTs.mjs';
|
|
2
|
+
export function compileJSONSchemaToTypeScriptType(schema, typeName, components = {}, options = {}) {
|
|
3
|
+
if (!schema)
|
|
4
|
+
return '';
|
|
5
|
+
if ('tsType' in schema && typeof schema.tsType === 'string')
|
|
6
|
+
return `export type ${typeName} = ${schema.tsType};\n`;
|
|
7
|
+
const tsType = compileTs({ schema: { ...schema, components }, name: typeName, ...options });
|
|
8
|
+
return tsType;
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { JSONSchema7 } from 'json-schema';
|
|
2
|
+
import { OpenAPIObject } from 'openapi3-ts/oas31';
|
|
3
|
+
interface CompileOptions {
|
|
4
|
+
name: string;
|
|
5
|
+
schema: JSONSchema7 & {
|
|
6
|
+
components?: OpenAPIObject['components'];
|
|
7
|
+
};
|
|
8
|
+
refs?: Map<string, JSONSchema7>;
|
|
9
|
+
dontCreateRefTypes?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function compileTs(options: CompileOptions): string;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import upperFirst from 'lodash/upperFirst.js';
|
|
2
|
+
import camelCase from 'lodash/camelCase.js';
|
|
3
|
+
export function compileTs(options) {
|
|
4
|
+
const context = {
|
|
5
|
+
refs: options.refs || new Map(),
|
|
6
|
+
compiledRefs: new Map(),
|
|
7
|
+
refsInProgress: new Set(),
|
|
8
|
+
};
|
|
9
|
+
// Collect all definitions from the schema
|
|
10
|
+
collectDefinitions(options.schema, context.refs);
|
|
11
|
+
// Ensure the main type name is valid
|
|
12
|
+
const mainTypeName = sanitizeTypeName(options.name);
|
|
13
|
+
const mainType = compileSchema(options.schema, mainTypeName, context);
|
|
14
|
+
// Compile all referenced types, unless dontCreateRefTypes is set
|
|
15
|
+
const compiledRefs = options.dontCreateRefTypes
|
|
16
|
+
? ''
|
|
17
|
+
: Array.from(context.compiledRefs.entries())
|
|
18
|
+
.map(([, typeDecl]) => typeDecl)
|
|
19
|
+
.join('\n\n');
|
|
20
|
+
return compiledRefs
|
|
21
|
+
? `${compiledRefs}\n\n${options.schema.description ? `/** ${escapeJSDocComment(options.schema.description)} */\n` : ''}export type ${mainTypeName} = ${mainType};`
|
|
22
|
+
: `${options.schema.description ? `/** ${escapeJSDocComment(options.schema.description)} */\n` : ''}export type ${mainTypeName} = ${mainType};`;
|
|
23
|
+
}
|
|
24
|
+
function collectDefinitions(schema, refs) {
|
|
25
|
+
// Collect from $defs
|
|
26
|
+
if (schema.$defs) {
|
|
27
|
+
Object.entries(schema.$defs).forEach(([key, def]) => {
|
|
28
|
+
if (typeof def === 'object') {
|
|
29
|
+
refs.set(`#/$defs/${key}`, def);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
// Collect from definitions (older spec)
|
|
34
|
+
if (schema.definitions) {
|
|
35
|
+
Object.entries(schema.definitions).forEach(([key, def]) => {
|
|
36
|
+
if (typeof def === 'object') {
|
|
37
|
+
refs.set(`#/definitions/${key}`, def);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Collect from components/schemas (OpenAPI spec)
|
|
42
|
+
if (schema?.components?.schemas) {
|
|
43
|
+
Object.entries(schema?.components?.schemas ?? {}).forEach(([key, def]) => {
|
|
44
|
+
if (typeof def === 'object') {
|
|
45
|
+
refs.set(`#/components/schemas/${key}`, def);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// Recursively collect from nested schemas
|
|
50
|
+
const schemasToProcess = [];
|
|
51
|
+
if (schema.properties) {
|
|
52
|
+
schemasToProcess.push(...Object.values(schema.properties).filter(isSchema));
|
|
53
|
+
}
|
|
54
|
+
if (schema.items) {
|
|
55
|
+
if (Array.isArray(schema.items)) {
|
|
56
|
+
schemasToProcess.push(...schema.items.filter(isSchema));
|
|
57
|
+
}
|
|
58
|
+
else if (isSchema(schema.items)) {
|
|
59
|
+
schemasToProcess.push(schema.items);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (schema.additionalProperties && isSchema(schema.additionalProperties)) {
|
|
63
|
+
schemasToProcess.push(schema.additionalProperties);
|
|
64
|
+
}
|
|
65
|
+
if (schema.allOf)
|
|
66
|
+
schemasToProcess.push(...schema.allOf.filter(isSchema));
|
|
67
|
+
if (schema.anyOf)
|
|
68
|
+
schemasToProcess.push(...schema.anyOf.filter(isSchema));
|
|
69
|
+
if (schema.oneOf)
|
|
70
|
+
schemasToProcess.push(...schema.oneOf.filter(isSchema));
|
|
71
|
+
schemasToProcess.forEach((s) => collectDefinitions(s, refs));
|
|
72
|
+
}
|
|
73
|
+
function isSchema(value) {
|
|
74
|
+
return typeof value === 'object' && !Array.isArray(value);
|
|
75
|
+
}
|
|
76
|
+
function compileSchema(schema, name, context) {
|
|
77
|
+
if (typeof schema === 'boolean') {
|
|
78
|
+
return schema ? 'any' : 'never';
|
|
79
|
+
}
|
|
80
|
+
// Handle x-tsType extension
|
|
81
|
+
if ('x-tsType' in schema && typeof schema['x-tsType'] === 'string') {
|
|
82
|
+
return schema['x-tsType'];
|
|
83
|
+
}
|
|
84
|
+
// Handle $ref
|
|
85
|
+
if (schema.$ref) {
|
|
86
|
+
return handleRef(schema.$ref, context);
|
|
87
|
+
}
|
|
88
|
+
// Handle combinators
|
|
89
|
+
if (schema.allOf) {
|
|
90
|
+
return handleAllOf(schema.allOf, name, context);
|
|
91
|
+
}
|
|
92
|
+
if (schema.anyOf) {
|
|
93
|
+
return handleAnyOf(schema.anyOf, name, context);
|
|
94
|
+
}
|
|
95
|
+
if (schema.oneOf) {
|
|
96
|
+
return handleOneOf(schema.oneOf, name, context);
|
|
97
|
+
}
|
|
98
|
+
// Handle type-specific compilation
|
|
99
|
+
if (schema.enum) {
|
|
100
|
+
return handleEnum(schema.enum);
|
|
101
|
+
}
|
|
102
|
+
if (schema.const !== undefined) {
|
|
103
|
+
return handleConst(schema.const);
|
|
104
|
+
}
|
|
105
|
+
if (!schema.type) {
|
|
106
|
+
// No type specified, could be object
|
|
107
|
+
if (schema.properties || schema.additionalProperties) {
|
|
108
|
+
return handleObject(schema, name, context);
|
|
109
|
+
}
|
|
110
|
+
return 'any';
|
|
111
|
+
}
|
|
112
|
+
if (Array.isArray(schema.type)) {
|
|
113
|
+
return schema.type.map((t) => compileSchemaWithType({ ...schema, type: t }, name, context)).join(' | ');
|
|
114
|
+
}
|
|
115
|
+
return compileSchemaWithType(schema, name, context);
|
|
116
|
+
}
|
|
117
|
+
function compileSchemaWithType(schema, name, context) {
|
|
118
|
+
switch (schema.type) {
|
|
119
|
+
case 'null':
|
|
120
|
+
return 'null';
|
|
121
|
+
case 'boolean':
|
|
122
|
+
return 'boolean';
|
|
123
|
+
case 'string':
|
|
124
|
+
return 'string';
|
|
125
|
+
case 'number':
|
|
126
|
+
return 'number';
|
|
127
|
+
case 'integer':
|
|
128
|
+
return 'number';
|
|
129
|
+
case 'array':
|
|
130
|
+
return handleArray(schema, name, context);
|
|
131
|
+
case 'object':
|
|
132
|
+
return handleObject(schema, name, context);
|
|
133
|
+
default:
|
|
134
|
+
return 'any';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function handleRef(ref, context) {
|
|
138
|
+
const typeName = refToTypeName(ref);
|
|
139
|
+
// Check if we're already compiling this ref (circular reference)
|
|
140
|
+
if (context.refsInProgress.has(ref)) {
|
|
141
|
+
return typeName;
|
|
142
|
+
}
|
|
143
|
+
// Check if already compiled
|
|
144
|
+
if (context.compiledRefs.has(ref)) {
|
|
145
|
+
return typeName;
|
|
146
|
+
}
|
|
147
|
+
// Find the referenced schema
|
|
148
|
+
const referencedSchema = context.refs.get(ref);
|
|
149
|
+
if (!referencedSchema) {
|
|
150
|
+
return 'any'; // Reference not found
|
|
151
|
+
}
|
|
152
|
+
// Mark as in progress
|
|
153
|
+
context.refsInProgress.add(ref);
|
|
154
|
+
// Compile the referenced schema
|
|
155
|
+
const compiledType = compileSchema(referencedSchema, typeName, context);
|
|
156
|
+
const description = referencedSchema.description
|
|
157
|
+
? `/** ${escapeJSDocComment(referencedSchema.description)} */\n`
|
|
158
|
+
: '';
|
|
159
|
+
context.compiledRefs.set(ref, `${description}export type ${typeName} = ${compiledType};`);
|
|
160
|
+
// Mark as completed
|
|
161
|
+
context.refsInProgress.delete(ref);
|
|
162
|
+
return typeName;
|
|
163
|
+
}
|
|
164
|
+
function handleAllOf(schemas, name, context) {
|
|
165
|
+
const types = schemas.map((s, i) => compileSchema(s, sanitizeTypeName(`${name}-all-of-${i}`), context));
|
|
166
|
+
// For allOf, we need to intersect types
|
|
167
|
+
// If they're all objects, we can merge them properly
|
|
168
|
+
const objectTypes = types.filter((t) => t.startsWith('{') && t.endsWith('}'));
|
|
169
|
+
if (objectTypes.length === types.length) {
|
|
170
|
+
// Merge object types
|
|
171
|
+
const merged = objectTypes.map((t) => t.slice(1, -1).trim()).filter((t) => t.length > 0);
|
|
172
|
+
return merged.length > 0 ? `{ ${merged.join('; ')} }` : '{}';
|
|
173
|
+
}
|
|
174
|
+
return types.join(' & ');
|
|
175
|
+
}
|
|
176
|
+
function handleAnyOf(schemas, name, context) {
|
|
177
|
+
const types = schemas.map((s, i) => compileSchema(s, sanitizeTypeName(`${name}-any-of-${i}`), context));
|
|
178
|
+
return types.join(' | ');
|
|
179
|
+
}
|
|
180
|
+
function handleOneOf(schemas, name, context) {
|
|
181
|
+
// For TypeScript, oneOf behaves like anyOf
|
|
182
|
+
const types = schemas.map((s, i) => compileSchema(s, sanitizeTypeName(`${name}-one-of-${i}`), context));
|
|
183
|
+
return types.join(' | ');
|
|
184
|
+
}
|
|
185
|
+
function handleEnum(enumValues) {
|
|
186
|
+
return enumValues.map((v) => JSON.stringify(v)).join(' | ');
|
|
187
|
+
}
|
|
188
|
+
function handleConst(value) {
|
|
189
|
+
return JSON.stringify(value);
|
|
190
|
+
}
|
|
191
|
+
function handleArray(schema, name, context) {
|
|
192
|
+
if (!schema.items) {
|
|
193
|
+
return 'any[]';
|
|
194
|
+
}
|
|
195
|
+
if (Array.isArray(schema.items)) {
|
|
196
|
+
// Tuple (ignoring min/max as requested)
|
|
197
|
+
const types = schema.items.map((item, i) => compileSchema(item, `${name}Item${i}`, context));
|
|
198
|
+
return `[${types.join(', ')}]`;
|
|
199
|
+
}
|
|
200
|
+
const itemType = compileSchema(schema.items, `${name}Item`, context);
|
|
201
|
+
return `${wrapUnionType(itemType)}[]`;
|
|
202
|
+
}
|
|
203
|
+
function handleObject(schema, name, context) {
|
|
204
|
+
const props = [];
|
|
205
|
+
// Handle known properties
|
|
206
|
+
if (schema.properties) {
|
|
207
|
+
const required = new Set(schema.required || []);
|
|
208
|
+
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
209
|
+
if (!isSchema(propSchema))
|
|
210
|
+
continue;
|
|
211
|
+
const isRequired = required.has(propName);
|
|
212
|
+
// Ensure the generated type name for nested properties is valid
|
|
213
|
+
const nestedTypeName = sanitizeTypeName(`${name}-${propName}`);
|
|
214
|
+
const propType = compileSchema(propSchema, nestedTypeName, context);
|
|
215
|
+
const safePropName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(propName) ? propName : `"${propName}"`;
|
|
216
|
+
// Add JSDoc comment if description is present
|
|
217
|
+
const comment = propSchema.description ? `\n/** ${escapeJSDocComment(propSchema.description)} */\n` : '';
|
|
218
|
+
props.push(`${comment}${safePropName}${isRequired ? '' : '?'}: ${propType}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Handle additional properties
|
|
222
|
+
if (schema.additionalProperties === true) {
|
|
223
|
+
props.push('[key: string]: any');
|
|
224
|
+
}
|
|
225
|
+
else if (schema.additionalProperties && isSchema(schema.additionalProperties)) {
|
|
226
|
+
const additionalTypeName = sanitizeTypeName(`${name}-additional`);
|
|
227
|
+
const additionalType = compileSchema(schema.additionalProperties, additionalTypeName, context);
|
|
228
|
+
props.push(`[key: string]: ${additionalType}`);
|
|
229
|
+
}
|
|
230
|
+
// Handle pattern properties
|
|
231
|
+
if (schema.patternProperties) {
|
|
232
|
+
// For simplicity, treat pattern properties as string index signature
|
|
233
|
+
const patternTypes = Object.values(schema.patternProperties)
|
|
234
|
+
.filter(isSchema)
|
|
235
|
+
.map((s, i) => compileSchema(s, sanitizeTypeName(`${name}-pattern-${i}`), context));
|
|
236
|
+
if (patternTypes.length > 0) {
|
|
237
|
+
props.push(`[key: string]: ${patternTypes.join(' | ')}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return props.length > 0 ? `{ ${props.join('; ')} }` : '{}';
|
|
241
|
+
}
|
|
242
|
+
function refToTypeName(ref) {
|
|
243
|
+
// Extract the last part of the reference as the type name
|
|
244
|
+
const parts = ref.split('/');
|
|
245
|
+
const name = parts[parts.length - 1];
|
|
246
|
+
// Convert kebab-case to PascalCase
|
|
247
|
+
return upperFirst(camelCase(name));
|
|
248
|
+
}
|
|
249
|
+
function wrapUnionType(type) {
|
|
250
|
+
// Wrap union types in parentheses for array types
|
|
251
|
+
return type.includes('|') ? `(${type})` : type;
|
|
252
|
+
}
|
|
253
|
+
function sanitizeTypeName(name) {
|
|
254
|
+
return upperFirst(camelCase(name));
|
|
255
|
+
}
|
|
256
|
+
// Utility function to escape JSDoc comment terminators in descriptions
|
|
257
|
+
function escapeJSDocComment(description) {
|
|
258
|
+
if (!description)
|
|
259
|
+
return '';
|
|
260
|
+
return description.replace(/\*\//g, '*\\/');
|
|
261
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { KnownAny } from '
|
|
2
|
-
export default function debounceWithArgs<
|
|
1
|
+
import type { KnownAny } from 'vovk';
|
|
2
|
+
export default function debounceWithArgs<Callback extends (...args: KnownAny[]) => KnownAny>(callback: Callback, wait: number): (...args: Parameters<Callback>) => Promise<Awaited<ReturnType<Callback>>>;
|