vovk-cli 0.0.1-draft.202 → 0.0.1-draft.203
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/client-templates/schemaCjs/schema.cjs.ejs +6 -1
- package/client-templates/schemaTs/schema.ts.ejs +7 -2
- package/client-templates/standaloneTypesTs/types.d.ts.ejs +12 -0
- package/dist/bundle/index.mjs +2 -2
- package/dist/dev/index.mjs +8 -6
- package/dist/generate/generate.mjs +4 -1
- package/dist/generate/index.mjs +2 -2
- package/dist/generate/writeOneClientFile.d.mts +2 -1
- package/dist/generate/writeOneClientFile.mjs +4 -1
- package/dist/getProjectInfo/getConfig/getTemplateDefs.d.mts +1 -0
- package/dist/getProjectInfo/getConfig/getTemplateDefs.mjs +14 -8
- package/dist/getProjectInfo/index.d.mts +1 -1
- package/dist/getProjectInfo/index.mjs +2 -2
- package/dist/index.mjs +1 -0
- package/dist/locateSegments.d.mts +1 -1
- package/dist/locateSegments.mjs +2 -0
- package/dist/new/newModule.mjs +2 -2
- package/dist/new/newSegment.mjs +5 -2
- package/dist/types.d.mts +1 -0
- package/dist/utils/convertJSONSchemaToTypeScriptDef.d.mts +2 -0
- package/dist/utils/convertJSONSchemaToTypeScriptDef.mjs +150 -0
- package/package.json +3 -2
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
<%- `// auto-generated by Vovk.ts ${new Date().toISOString()}` %>
|
|
2
|
+
<% if(t.isTsStandalone) { %>
|
|
3
|
+
const schema = require('./schema.json');
|
|
4
|
+
module.exports.schema = schema;
|
|
5
|
+
<% } else { %>
|
|
2
6
|
const meta = require('./<%= t.schemaOutDir %>/_meta.json');
|
|
3
7
|
const segments = {<% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment) => { %>
|
|
4
8
|
'<%= segment.segmentName %>': require('./<%= t.schemaOutDir %>/<%= segment.segmentName || t.ROOT_SEGMENT_FILE_NAME %>.json'),<% }) %>
|
|
@@ -7,9 +11,10 @@ const schema = {
|
|
|
7
11
|
$schema: '<%- t.VovkSchemaIdEnum.SCHEMA %>',
|
|
8
12
|
segments,
|
|
9
13
|
meta: {
|
|
10
|
-
apiRoot: '<%= t.apiRoot %>',
|
|
14
|
+
apiRoot: '<%= t.apiRoot %>', // for debugging purposes
|
|
11
15
|
...meta
|
|
12
16
|
}
|
|
13
17
|
};
|
|
14
18
|
|
|
15
19
|
module.exports.schema = schema;
|
|
20
|
+
<% } %>
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
<%- `// auto-generated by Vovk.ts ${new Date().toISOString()}` %>
|
|
2
|
+
<% if(t.isTsStandalone) { %>
|
|
3
|
+
import schema from './schema.json' with { type: "json" };
|
|
4
|
+
export { schema };
|
|
5
|
+
<% } else { %>
|
|
2
6
|
import meta from './<%= t.schemaOutDir %>/_meta.json' with { type: "json" };
|
|
3
7
|
<% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
|
|
4
8
|
import segment<%= i %> from './<%= t.schemaOutDir %>/<%= segment.segmentName || t.ROOT_SEGMENT_FILE_NAME %>.json' with { type: "json" };
|
|
@@ -11,7 +15,8 @@ export const schema = {
|
|
|
11
15
|
$schema: '<%- t.VovkSchemaIdEnum.SCHEMA %>',
|
|
12
16
|
segments,
|
|
13
17
|
meta: {
|
|
14
|
-
apiRoot: '<%= t.apiRoot %>',
|
|
18
|
+
apiRoot: '<%= t.apiRoot %>', // for debugging purposes
|
|
15
19
|
...meta,
|
|
16
20
|
}
|
|
17
|
-
};
|
|
21
|
+
};
|
|
22
|
+
<% } %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { VovkRequest, VovkStreamAsyncIterable, KnownAny } from 'vovk';
|
|
2
|
+
<% Object.entries(t.schema.segments).forEach(([segmentName, segment], i) => {
|
|
3
|
+
Object.values(segment.controllers).forEach((controllerSchema) => { %>
|
|
4
|
+
|
|
5
|
+
export type Segment<%= i %><%= controllerSchema.originalControllerName ?? controllerSchema.rpcModuleName + 'Controller' %> = {
|
|
6
|
+
<% Object.entries(controllerSchema.handlers).forEach(([handlerName, handler]) => { %>
|
|
7
|
+
<%= handlerName %>: (req: VovkRequest<<%- t.convertJSONSchemaToTypeScriptDef(handler.validation?.body) ?? 'KnownAny' %>, <%- t.convertJSONSchemaToTypeScriptDef(handler.validation?.query) ?? 'KnownAny' %>, <%- t.convertJSONSchemaToTypeScriptDef(handler.validation?.params ?? 'KnownAny') %>>): <%= validation?.output ? `Promise<${t.convertJSONSchemaToTypeScriptDef(handler.validation?.body)}>` : validation?.iteration ? `Promise<VovkStreamAsyncIterable<${t.convertJSONSchemaToTypeScriptDef(handler.validation?.iteration)}>>` : 'Promise<KnownAny>' %>:
|
|
8
|
+
<% }) %>
|
|
9
|
+
};
|
|
10
|
+
<% }) %>
|
|
11
|
+
<% }) %>
|
|
12
|
+
|
package/dist/bundle/index.mjs
CHANGED
|
@@ -7,8 +7,8 @@ import { BuiltInTemplateName } from '../getProjectInfo/getConfig/getTemplateDefs
|
|
|
7
7
|
import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
|
|
8
8
|
import { locateSegments } from '../locateSegments.mjs';
|
|
9
9
|
export async function bundle({ projectInfo, fullSchema, cliBundleOptions, }) {
|
|
10
|
-
const { config, log, cwd,
|
|
11
|
-
const locatedSegments = await locateSegments({ dir:
|
|
10
|
+
const { config, log, cwd, apiDirAbsolutePath } = projectInfo;
|
|
11
|
+
const locatedSegments = await locateSegments({ dir: apiDirAbsolutePath, config, log });
|
|
12
12
|
const { bundle: bundleConfig } = config;
|
|
13
13
|
const tsFullClientOutAbsoluteDirInput = path.join(cwd, bundleConfig.tsClientOutDir);
|
|
14
14
|
const tsClientOutDir = cliBundleOptions?.tsClientOutDir ?? bundleConfig.tsClientOutDir;
|
package/dist/dev/index.mjs
CHANGED
|
@@ -41,9 +41,11 @@ export class VovkDev {
|
|
|
41
41
|
}
|
|
42
42
|
#watchSegments = (callback) => {
|
|
43
43
|
const segmentReg = /\/?\[\[\.\.\.[a-zA-Z-_]+\]\]\/route.ts$/;
|
|
44
|
-
const { cwd, log, config,
|
|
44
|
+
const { cwd, log, config, apiDirAbsolutePath } = this.#projectInfo;
|
|
45
|
+
if (!apiDirAbsolutePath) {
|
|
46
|
+
throw new Error('Unable to watch segments. It looks like CWD is not a Next.js app.');
|
|
47
|
+
}
|
|
45
48
|
const schemaOutAbsolutePath = path.resolve(cwd, this.#schemaOut ?? config.schemaOutDir);
|
|
46
|
-
const apiDirAbsolutePath = path.join(cwd, apiDir);
|
|
47
49
|
const getSegmentName = (filePath) => path.relative(apiDirAbsolutePath, filePath).replace(segmentReg, '');
|
|
48
50
|
log.debug(`Watching segments at ${apiDirAbsolutePath}`);
|
|
49
51
|
this.#segmentWatcher = chokidar
|
|
@@ -161,8 +163,8 @@ export class VovkDev {
|
|
|
161
163
|
let isReady = false;
|
|
162
164
|
const handle = debounce(async () => {
|
|
163
165
|
this.#projectInfo = await getProjectInfo();
|
|
164
|
-
const { config,
|
|
165
|
-
this.#segments = await locateSegments({ dir:
|
|
166
|
+
const { config, apiDirAbsolutePath } = this.#projectInfo;
|
|
167
|
+
this.#segments = await locateSegments({ dir: apiDirAbsolutePath, config, log });
|
|
166
168
|
await this.#modulesWatcher?.close();
|
|
167
169
|
await this.#segmentWatcher?.close();
|
|
168
170
|
await Promise.all([
|
|
@@ -316,8 +318,8 @@ export class VovkDev {
|
|
|
316
318
|
async start({ exit }) {
|
|
317
319
|
const now = Date.now();
|
|
318
320
|
this.#projectInfo = await getProjectInfo();
|
|
319
|
-
const { log, config, cwd,
|
|
320
|
-
this.#segments = await locateSegments({ dir:
|
|
321
|
+
const { log, config, cwd, apiDirAbsolutePath } = this.#projectInfo;
|
|
322
|
+
this.#segments = await locateSegments({ dir: apiDirAbsolutePath, config, log });
|
|
321
323
|
log.info('Starting...');
|
|
322
324
|
if (exit) {
|
|
323
325
|
this.#onFirstTimeGenerate = once(() => {
|
|
@@ -56,8 +56,9 @@ export async function generate({ isEnsuringClient = false, projectInfo, forceNot
|
|
|
56
56
|
// sort segments by name to avoid unnecessary rendering
|
|
57
57
|
segments: Object.fromEntries(Object.entries(fullSchema.segments).sort(([a], [b]) => a.localeCompare(b))),
|
|
58
58
|
};
|
|
59
|
-
const { config, cwd, log } = projectInfo;
|
|
59
|
+
const { config, cwd, log, srcRoot } = projectInfo;
|
|
60
60
|
const isNodeNextResolution = ['node16', 'nodenext'].includes((await getTsconfig(cwd)?.config?.compilerOptions?.moduleResolution?.toLowerCase()) ?? '');
|
|
61
|
+
const isTsStandalone = cliGenerateOptions?.forceTsStandalone ?? !srcRoot;
|
|
61
62
|
const isComposedEnabled = cliGenerateOptions?.composedOnly ||
|
|
62
63
|
!!cliGenerateOptions?.composedFrom ||
|
|
63
64
|
!!cliGenerateOptions?.composedOut ||
|
|
@@ -110,6 +111,7 @@ export async function generate({ isEnsuringClient = false, projectInfo, forceNot
|
|
|
110
111
|
templateDef,
|
|
111
112
|
locatedSegments,
|
|
112
113
|
isNodeNextResolution,
|
|
114
|
+
isTsStandalone,
|
|
113
115
|
});
|
|
114
116
|
const outAbsoluteDir = path.join(cwd, outCwdRelativeDir);
|
|
115
117
|
return {
|
|
@@ -181,6 +183,7 @@ export async function generate({ isEnsuringClient = false, projectInfo, forceNot
|
|
|
181
183
|
templateDef,
|
|
182
184
|
locatedSegments,
|
|
183
185
|
isNodeNextResolution,
|
|
186
|
+
isTsStandalone,
|
|
184
187
|
});
|
|
185
188
|
return {
|
|
186
189
|
written,
|
package/dist/generate/index.mjs
CHANGED
|
@@ -30,8 +30,8 @@ export class VovkGenerate {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async generate({ fullSchema }) {
|
|
33
|
-
const { log, config,
|
|
34
|
-
const locatedSegments = await locateSegments({ dir:
|
|
33
|
+
const { log, config, apiDirAbsolutePath } = this.#projectInfo;
|
|
34
|
+
const locatedSegments = await locateSegments({ dir: apiDirAbsolutePath, config, log });
|
|
35
35
|
await generate({
|
|
36
36
|
projectInfo: this.#projectInfo,
|
|
37
37
|
fullSchema,
|
|
@@ -4,7 +4,7 @@ import type { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
|
4
4
|
import type { ClientTemplateFile } from './getClientTemplateFiles.mjs';
|
|
5
5
|
import type { ClientImports } from './getTemplateClientImports.mjs';
|
|
6
6
|
import type { Segment } from '../locateSegments.mjs';
|
|
7
|
-
export default function writeOneClientFile({ cwd, projectInfo, clientTemplateFile, fullSchema, prettifyClient, segmentName, imports, templateContent, matterResult: { data, content }, package: packageJson, isEnsuringClient, outCwdRelativeDir, origin, templateDef, locatedSegments, isNodeNextResolution, }: {
|
|
7
|
+
export default function writeOneClientFile({ cwd, projectInfo, clientTemplateFile, fullSchema, prettifyClient, segmentName, imports, templateContent, matterResult: { data, content }, package: packageJson, isEnsuringClient, outCwdRelativeDir, origin, templateDef, locatedSegments, isNodeNextResolution, isTsStandalone, }: {
|
|
8
8
|
cwd: string;
|
|
9
9
|
projectInfo: ProjectInfo;
|
|
10
10
|
clientTemplateFile: ClientTemplateFile;
|
|
@@ -26,6 +26,7 @@ export default function writeOneClientFile({ cwd, projectInfo, clientTemplateFil
|
|
|
26
26
|
templateDef: VovkStrictConfig['clientTemplateDefs'][string];
|
|
27
27
|
locatedSegments: Segment[];
|
|
28
28
|
isNodeNextResolution: boolean;
|
|
29
|
+
isTsStandalone: boolean;
|
|
29
30
|
}): Promise<{
|
|
30
31
|
written: boolean;
|
|
31
32
|
}>;
|
|
@@ -7,7 +7,8 @@ import * as YAML from 'yaml';
|
|
|
7
7
|
import TOML from '@iarna/toml';
|
|
8
8
|
import prettify from '../utils/prettify.mjs';
|
|
9
9
|
import { ROOT_SEGMENT_FILE_NAME } from '../dev/writeOneSegmentSchemaFile.mjs';
|
|
10
|
-
|
|
10
|
+
import { convertJSONSchemaToTypeScriptDef } from '../utils/convertJSONSchemaToTypeScriptDef.mjs';
|
|
11
|
+
export default async function writeOneClientFile({ cwd, projectInfo, clientTemplateFile, fullSchema, prettifyClient, segmentName, imports, templateContent, matterResult: { data, content }, package: packageJson, isEnsuringClient, outCwdRelativeDir, origin, templateDef, locatedSegments, isNodeNextResolution, isTsStandalone, }) {
|
|
11
12
|
const { config, apiRoot } = projectInfo;
|
|
12
13
|
const { templateFilePath, relativeDir } = clientTemplateFile;
|
|
13
14
|
const locatedSegmentsByName = _.keyBy(locatedSegments, 'segmentName');
|
|
@@ -19,6 +20,7 @@ export default async function writeOneClientFile({ cwd, projectInfo, clientTempl
|
|
|
19
20
|
// Data for the EJS templates:
|
|
20
21
|
const t = {
|
|
21
22
|
_, // lodash
|
|
23
|
+
isTsStandalone,
|
|
22
24
|
package: packageJson,
|
|
23
25
|
ROOT_SEGMENT_FILE_NAME,
|
|
24
26
|
apiRoot: origin ? `${origin}/${config.rootEntry}` : apiRoot,
|
|
@@ -26,6 +28,7 @@ export default async function writeOneClientFile({ cwd, projectInfo, clientTempl
|
|
|
26
28
|
schema: fullSchema,
|
|
27
29
|
VovkSchemaIdEnum,
|
|
28
30
|
createCodeExamples,
|
|
31
|
+
convertJSONSchemaToTypeScriptDef,
|
|
29
32
|
YAML,
|
|
30
33
|
TOML,
|
|
31
34
|
nodeNextResolutionExt: {
|
|
@@ -8,6 +8,8 @@ export var BuiltInTemplateName;
|
|
|
8
8
|
BuiltInTemplateName["schemaTs"] = "schemaTs";
|
|
9
9
|
BuiltInTemplateName["schemaCjs"] = "schemaCjs";
|
|
10
10
|
BuiltInTemplateName["schemaJson"] = "schemaJson";
|
|
11
|
+
// types
|
|
12
|
+
BuiltInTemplateName["standaloneTypesTs"] = "standaloneTypesTs";
|
|
11
13
|
// misc
|
|
12
14
|
BuiltInTemplateName["readme"] = "readme";
|
|
13
15
|
BuiltInTemplateName["packageJson"] = "packageJson";
|
|
@@ -30,12 +32,6 @@ export default function getTemplateDefs(userTemplateDefs = {}) {
|
|
|
30
32
|
templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.mjs}/`,
|
|
31
33
|
requires: { [BuiltInTemplateName.schemaCjs]: '.' },
|
|
32
34
|
},
|
|
33
|
-
[BuiltInTemplateName.readme]: {
|
|
34
|
-
templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.readme}/`,
|
|
35
|
-
},
|
|
36
|
-
[BuiltInTemplateName.packageJson]: {
|
|
37
|
-
templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.packageJson}/`,
|
|
38
|
-
},
|
|
39
35
|
[BuiltInTemplateName.schemaTs]: {
|
|
40
36
|
templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.schemaTs}/`,
|
|
41
37
|
},
|
|
@@ -45,13 +41,23 @@ export default function getTemplateDefs(userTemplateDefs = {}) {
|
|
|
45
41
|
[BuiltInTemplateName.schemaJson]: {
|
|
46
42
|
templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.schemaJson}/`,
|
|
47
43
|
},
|
|
44
|
+
[BuiltInTemplateName.standaloneTypesTs]: {
|
|
45
|
+
templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.standaloneTypesTs}/`,
|
|
46
|
+
requires: { [BuiltInTemplateName.schemaJson]: '.' },
|
|
47
|
+
},
|
|
48
|
+
[BuiltInTemplateName.readme]: {
|
|
49
|
+
templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.readme}/`,
|
|
50
|
+
},
|
|
51
|
+
[BuiltInTemplateName.packageJson]: {
|
|
52
|
+
templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.packageJson}/`,
|
|
53
|
+
},
|
|
48
54
|
[BuiltInTemplateName.rs]: {
|
|
49
55
|
templatePath: 'vovk-rust-client/template/',
|
|
50
56
|
composedClient: {
|
|
51
57
|
outDir: 'dist_rust',
|
|
52
58
|
},
|
|
53
59
|
requires: {
|
|
54
|
-
schemaJson: './data',
|
|
60
|
+
[BuiltInTemplateName.schemaJson]: './data',
|
|
55
61
|
},
|
|
56
62
|
},
|
|
57
63
|
[BuiltInTemplateName.py]: {
|
|
@@ -60,7 +66,7 @@ export default function getTemplateDefs(userTemplateDefs = {}) {
|
|
|
60
66
|
outDir: 'dist_python',
|
|
61
67
|
},
|
|
62
68
|
requires: {
|
|
63
|
-
schemaJson: './data',
|
|
69
|
+
[BuiltInTemplateName.schemaJson]: './data',
|
|
64
70
|
},
|
|
65
71
|
},
|
|
66
72
|
};
|
|
@@ -8,7 +8,7 @@ export default function getProjectInfo({ port: givenPort, cwd, configPath, srcRo
|
|
|
8
8
|
cwd: string;
|
|
9
9
|
port: string;
|
|
10
10
|
apiRoot: string;
|
|
11
|
-
|
|
11
|
+
apiDirAbsolutePath: string | null;
|
|
12
12
|
srcRoot: string | null;
|
|
13
13
|
config: import("vovk").VovkStrictConfig;
|
|
14
14
|
log: {
|
|
@@ -12,7 +12,7 @@ export default async function getProjectInfo({ port: givenPort, cwd = process.cw
|
|
|
12
12
|
throw new Error(`Could not find app router directory at ${cwd}. Check Next.js docs for more info.`);
|
|
13
13
|
}
|
|
14
14
|
const apiRoot = `${config.origin ?? ''}/${config.rootEntry}`;
|
|
15
|
-
const
|
|
15
|
+
const apiDirAbsolutePath = srcRoot ? path.resolve(cwd, srcRoot, 'app', config.rootEntry) : null;
|
|
16
16
|
if (configAbsolutePaths.length > 1) {
|
|
17
17
|
log.warn(`Multiple config files found. Using the first one: ${configAbsolutePaths[0]}`);
|
|
18
18
|
}
|
|
@@ -20,7 +20,7 @@ export default async function getProjectInfo({ port: givenPort, cwd = process.cw
|
|
|
20
20
|
cwd,
|
|
21
21
|
port,
|
|
22
22
|
apiRoot,
|
|
23
|
-
|
|
23
|
+
apiDirAbsolutePath,
|
|
24
24
|
srcRoot,
|
|
25
25
|
config,
|
|
26
26
|
log,
|
package/dist/index.mjs
CHANGED
|
@@ -88,6 +88,7 @@ program
|
|
|
88
88
|
.option('--prettify', 'prettify output files')
|
|
89
89
|
.option('--schema, --schema-path <path>', 'path to schema folder (default: ./.vovk-schema)')
|
|
90
90
|
.option('--config, --config-path <config>', 'path to config file')
|
|
91
|
+
.option('--force-ts-standalone', 'force TypeScript standalone mode (Next.js environment will be ignored, by default it\'s "true" for non-Next.js directories)')
|
|
91
92
|
.option('--watch <s>', 'watch for changes in schema or openapi spec and regenerate client; accepts a number in seconds to throttle the watcher or make an HTTP request to the OpenAPI spec URL')
|
|
92
93
|
.option('--openapi, --openapi-spec <openapi_path_or_url>', 'use OpenAPI schema instead of Vovk schema')
|
|
93
94
|
.action(async (cliGenerateOptions) => {
|
package/dist/locateSegments.mjs
CHANGED
|
@@ -3,6 +3,8 @@ import path from 'node:path';
|
|
|
3
3
|
import getFileSystemEntryType from './utils/getFileSystemEntryType.mjs';
|
|
4
4
|
export async function locateSegments({ dir, rootDir, config, log, }) {
|
|
5
5
|
let results = [];
|
|
6
|
+
if (!dir)
|
|
7
|
+
return results; // If dir is null, return empty results because this isn't a Next.js app
|
|
6
8
|
rootDir = rootDir ?? dir;
|
|
7
9
|
let list = [];
|
|
8
10
|
// Read the contents of the directory
|
package/dist/new/newModule.mjs
CHANGED
|
@@ -21,8 +21,8 @@ function splitByLast(str, delimiter = '/') {
|
|
|
21
21
|
return [before, after];
|
|
22
22
|
}
|
|
23
23
|
export default async function newModule({ what, moduleNameWithOptionalSegment, dryRun, dir: dirFlag, templates: templatesFlag, noSegmentUpdate, overwrite, empty, }) {
|
|
24
|
-
const { config, log, cwd,
|
|
25
|
-
const segments = await locateSegments({ dir:
|
|
24
|
+
const { config, log, cwd, apiDirAbsolutePath } = await getProjectInfo();
|
|
25
|
+
const segments = await locateSegments({ dir: apiDirAbsolutePath, config, log });
|
|
26
26
|
const isNodeNextResolution = ['node16', 'nodenext'].includes((await getTsconfig(cwd)?.config?.compilerOptions?.moduleResolution?.toLowerCase()) ?? '');
|
|
27
27
|
let templates = config.moduleTemplates;
|
|
28
28
|
const [segmentName, moduleName] = splitByLast(moduleNameWithOptionalSegment);
|
package/dist/new/newSegment.mjs
CHANGED
|
@@ -7,8 +7,11 @@ import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
|
|
|
7
7
|
import prettify from '../utils/prettify.mjs';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
export default async function newSegment({ segmentName, isStaticSegment, overwrite, dryRun, }) {
|
|
10
|
-
const {
|
|
11
|
-
|
|
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');
|
|
12
15
|
if (!overwrite && (await getFileSystemEntryType(absoluteSegmentRoutePath))) {
|
|
13
16
|
throw new Error(`Unable to create new segment. ${formatLoggedSegmentName(segmentName, { upperFirst: true })} already exists.`);
|
|
14
17
|
}
|
package/dist/types.d.mts
CHANGED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export function convertJSONSchemaToTypeScriptDef(schema) {
|
|
2
|
+
if (!schema)
|
|
3
|
+
return null;
|
|
4
|
+
// Helper function to get JSDoc from schema
|
|
5
|
+
const getJSDoc = (schema, indentation = '') => {
|
|
6
|
+
if (typeof schema === 'boolean') {
|
|
7
|
+
return `${indentation}/**\n${indentation} * none\n${indentation} */`;
|
|
8
|
+
}
|
|
9
|
+
const description = schema.description || schema.title || 'none';
|
|
10
|
+
return `${indentation}/**\n${indentation} * ${description}\n${indentation} */`;
|
|
11
|
+
};
|
|
12
|
+
// Helper function to convert schema to TypeScript type
|
|
13
|
+
const schemaToType = (schema, indentation = ' ') => {
|
|
14
|
+
if (typeof schema === 'boolean') {
|
|
15
|
+
return schema ? 'KnownAny' : 'never';
|
|
16
|
+
}
|
|
17
|
+
if (schema.enum) {
|
|
18
|
+
return schema.enum
|
|
19
|
+
.map((value) => {
|
|
20
|
+
if (typeof value === 'string') {
|
|
21
|
+
return `'${value}'`;
|
|
22
|
+
}
|
|
23
|
+
else if (value === null) {
|
|
24
|
+
return 'null';
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
return String(value);
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
.join(' | ');
|
|
31
|
+
}
|
|
32
|
+
if (schema.const !== undefined) {
|
|
33
|
+
if (typeof schema.const === 'string') {
|
|
34
|
+
return `'${schema.const}'`;
|
|
35
|
+
}
|
|
36
|
+
else if (schema.const === null) {
|
|
37
|
+
return 'null';
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
return String(schema.const);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (schema.oneOf) {
|
|
44
|
+
return schema.oneOf.map((s) => schemaToType(s, indentation)).join(' | ');
|
|
45
|
+
}
|
|
46
|
+
if (schema.anyOf) {
|
|
47
|
+
return schema.anyOf.map((s) => schemaToType(s, indentation)).join(' | ');
|
|
48
|
+
}
|
|
49
|
+
if (schema.allOf) {
|
|
50
|
+
return schema.allOf.map((s) => schemaToType(s, indentation)).join(' & ');
|
|
51
|
+
}
|
|
52
|
+
if (schema.type === 'object' || schema.properties) {
|
|
53
|
+
const properties = schema.properties || {};
|
|
54
|
+
const required = schema.required || [];
|
|
55
|
+
const propertyEntries = Object.entries(properties);
|
|
56
|
+
if (propertyEntries.length === 0) {
|
|
57
|
+
// Handle additional properties
|
|
58
|
+
if (schema.additionalProperties) {
|
|
59
|
+
if (typeof schema.additionalProperties === 'boolean') {
|
|
60
|
+
return schema.additionalProperties ? 'Record<string, KnownAny>' : '{}';
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const valueType = schemaToType(schema.additionalProperties, indentation);
|
|
64
|
+
return `Record<string, ${valueType}>`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return '{}';
|
|
68
|
+
}
|
|
69
|
+
const props = propertyEntries
|
|
70
|
+
.map(([propName, propSchema]) => {
|
|
71
|
+
if (typeof propSchema === 'boolean') {
|
|
72
|
+
const type = propSchema ? 'KnownAny' : 'never';
|
|
73
|
+
const isOptional = !required.includes(propName);
|
|
74
|
+
const jsDoc = getJSDoc(propSchema, indentation);
|
|
75
|
+
return `${jsDoc}\n${indentation}${propName}${isOptional ? '?' : ''}: ${type};`;
|
|
76
|
+
}
|
|
77
|
+
const isOptional = !required.includes(propName);
|
|
78
|
+
const defaultValue = propSchema.default !== undefined ? ` // default: ${JSON.stringify(propSchema.default)}` : '';
|
|
79
|
+
const jsDoc = getJSDoc(propSchema, indentation);
|
|
80
|
+
const propType = schemaToType(propSchema, indentation + ' ');
|
|
81
|
+
return `${jsDoc}\n${indentation}${propName}${isOptional ? '?' : ''}: ${propType};${defaultValue}`;
|
|
82
|
+
})
|
|
83
|
+
.join('\n');
|
|
84
|
+
// Handle additional properties
|
|
85
|
+
let additionalPropsType = '';
|
|
86
|
+
if (schema.additionalProperties) {
|
|
87
|
+
if (typeof schema.additionalProperties === 'boolean') {
|
|
88
|
+
if (schema.additionalProperties) {
|
|
89
|
+
additionalPropsType = `\n${indentation}[key: string]: KnownAny;`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
const valueType = schemaToType(schema.additionalProperties, indentation + ' ');
|
|
94
|
+
additionalPropsType = `\n${indentation}[key: string]: ${valueType};`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return `{\n${props}${additionalPropsType}\n${indentation.slice(2)}}`;
|
|
98
|
+
}
|
|
99
|
+
if (schema.type === 'array' && schema.items) {
|
|
100
|
+
if (Array.isArray(schema.items)) {
|
|
101
|
+
// Tuple
|
|
102
|
+
const tupleTypes = schema.items.map((item) => schemaToType(item, indentation));
|
|
103
|
+
let tupleType = `[${tupleTypes.join(', ')}]`;
|
|
104
|
+
// Handle additional items
|
|
105
|
+
if (schema.additionalItems === true) {
|
|
106
|
+
tupleType += ' & KnownAny[]';
|
|
107
|
+
}
|
|
108
|
+
else if (typeof schema.additionalItems === 'object') {
|
|
109
|
+
const additionalType = schemaToType(schema.additionalItems, indentation);
|
|
110
|
+
tupleType += ` & ${additionalType}[]`;
|
|
111
|
+
}
|
|
112
|
+
return tupleType;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// Array
|
|
116
|
+
return `${schemaToType(schema.items, indentation)}[]`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Handle multiple types
|
|
120
|
+
if (Array.isArray(schema.type)) {
|
|
121
|
+
return schema.type
|
|
122
|
+
.map((t) => {
|
|
123
|
+
const singleTypeSchema = { ...schema, type: t };
|
|
124
|
+
singleTypeSchema.type = t;
|
|
125
|
+
return schemaToType(singleTypeSchema, indentation);
|
|
126
|
+
})
|
|
127
|
+
.join(' | ');
|
|
128
|
+
}
|
|
129
|
+
// Handle primitive types
|
|
130
|
+
switch (schema.type) {
|
|
131
|
+
case 'string':
|
|
132
|
+
return 'string';
|
|
133
|
+
case 'number':
|
|
134
|
+
case 'integer':
|
|
135
|
+
return 'number';
|
|
136
|
+
case 'boolean':
|
|
137
|
+
return 'boolean';
|
|
138
|
+
case 'null':
|
|
139
|
+
return 'null';
|
|
140
|
+
case 'array':
|
|
141
|
+
return 'KnownAny[]'; // For arrays with no items defined
|
|
142
|
+
default:
|
|
143
|
+
return 'KnownAny';
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
// Generate the interface
|
|
147
|
+
const jsDoc = getJSDoc(schema);
|
|
148
|
+
const interfaceBody = schemaToType(schema);
|
|
149
|
+
return `${jsDoc}\n${interfaceBody}`;
|
|
150
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vovk-cli",
|
|
3
|
-
"version": "0.0.1-draft.
|
|
3
|
+
"version": "0.0.1-draft.203",
|
|
4
4
|
"bin": {
|
|
5
5
|
"vovk": "./dist/index.mjs"
|
|
6
6
|
},
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://vovk.dev",
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"vovk": "^3.0.0-draft.
|
|
38
|
+
"vovk": "^3.0.0-draft.181"
|
|
39
39
|
},
|
|
40
40
|
"optionalDependencies": {
|
|
41
41
|
"vovk-python-client": "^0.0.1-draft.37"
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"@rollup/plugin-json": "^6.1.0",
|
|
49
49
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
50
50
|
"@rollup/plugin-typescript": "^12.1.2",
|
|
51
|
+
"@types/json-schema": "^7.0.15",
|
|
51
52
|
"chalk": "^5.4.1",
|
|
52
53
|
"chokidar": "^4.0.3",
|
|
53
54
|
"commander": "^13.1.0",
|