vovk-cli 0.0.1-draft.99 → 0.0.3
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 +24 -16
- package/client-templates/jsBase/index.d.ts.ejs +21 -0
- package/client-templates/jsBase/index.js.ejs +18 -0
- package/client-templates/mixins/mixins.d.ts.ejs +64 -0
- package/client-templates/mixins/mixins.json.ejs +1 -0
- package/client-templates/openapiJs/openapi.d.ts.ejs +4 -0
- package/client-templates/openapiJs/openapi.js.ejs +4 -0
- package/client-templates/openapiJson/openapi.json.ejs +1 -0
- package/client-templates/openapiTs/openapi.ts.ejs +4 -0
- package/client-templates/packageJson/package.json.ejs +1 -0
- package/client-templates/readme/README.md.ejs +39 -0
- package/client-templates/schemaJs/schema.d.ts.ejs +10 -0
- package/client-templates/schemaJs/schema.js.ejs +29 -0
- package/client-templates/schemaJson/schema.json.ejs +1 -0
- package/client-templates/schemaTs/schema.ts.ejs +28 -0
- package/client-templates/tsBase/index.ts.ejs +27 -0
- package/dist/bundle/index.d.mts +8 -0
- package/dist/bundle/index.mjs +76 -0
- package/dist/dev/{diffSchema.d.mts → diffSegmentSchema.d.mts} +3 -3
- package/dist/dev/{diffSchema.mjs → diffSegmentSchema.mjs} +1 -1
- package/dist/dev/ensureSchemaFiles.d.mts +2 -2
- package/dist/dev/ensureSchemaFiles.mjs +16 -47
- package/dist/dev/index.d.mts +2 -0
- package/dist/dev/index.mjs +113 -64
- package/dist/dev/logDiffResult.d.mts +2 -2
- package/dist/dev/logDiffResult.mjs +6 -6
- package/dist/dev/writeMetaJson.d.mts +2 -0
- package/dist/dev/writeMetaJson.mjs +19 -0
- package/dist/dev/writeOneSegmentSchemaFile.d.mts +12 -0
- package/dist/dev/{writeOneSchemaFile.mjs → writeOneSegmentSchemaFile.mjs} +11 -8
- package/dist/generate/ensureClient.d.mts +2 -4
- package/dist/generate/ensureClient.mjs +26 -28
- package/dist/generate/generate.d.mts +13 -0
- package/dist/generate/generate.mjs +307 -0
- package/dist/generate/getClientTemplateFiles.d.mts +20 -0
- package/dist/generate/getClientTemplateFiles.mjs +85 -0
- package/dist/generate/getProjectFullSchema.d.mts +9 -0
- package/dist/generate/getProjectFullSchema.mjs +64 -0
- package/dist/generate/getTemplateClientImports.d.mts +18 -0
- package/dist/generate/getTemplateClientImports.mjs +36 -0
- package/dist/generate/index.d.mts +31 -11
- package/dist/generate/index.mjs +177 -85
- package/dist/generate/writeOneClientFile.d.mts +43 -0
- package/dist/generate/writeOneClientFile.mjs +150 -0
- package/dist/getProjectInfo/getConfig/getConfigAbsolutePaths.d.mts +5 -0
- package/dist/getProjectInfo/{getConfigAbsolutePaths.mjs → getConfig/getConfigAbsolutePaths.mjs} +4 -1
- package/dist/getProjectInfo/getConfig/getRelativeSrcRoot.d.mts +3 -0
- package/dist/getProjectInfo/{getRelativeSrcRoot.mjs → getConfig/getRelativeSrcRoot.mjs} +3 -3
- package/dist/getProjectInfo/getConfig/getTemplateDefs.d.mts +25 -0
- package/dist/getProjectInfo/getConfig/getTemplateDefs.mjs +168 -0
- package/dist/getProjectInfo/getConfig/getUserConfig.d.mts +9 -0
- package/dist/getProjectInfo/getConfig/getUserConfig.mjs +142 -0
- package/dist/getProjectInfo/getConfig/index.d.mts +15 -0
- package/dist/getProjectInfo/getConfig/index.mjs +92 -0
- package/dist/getProjectInfo/getMetaSchema.d.mts +4 -0
- package/dist/getProjectInfo/getMetaSchema.mjs +12 -0
- package/dist/getProjectInfo/index.d.mts +12 -16
- package/dist/getProjectInfo/index.mjs +23 -29
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +119 -40
- package/dist/init/checkTSConfigForExperimentalDecorators.d.mts +1 -1
- package/dist/init/checkTSConfigForExperimentalDecorators.mjs +2 -2
- package/dist/init/createConfig.d.mts +5 -3
- package/dist/init/createConfig.mjs +77 -27
- package/dist/init/index.d.mts +2 -2
- package/dist/init/index.mjs +118 -103
- package/dist/init/installDependencies.d.mts +8 -6
- package/dist/init/installDependencies.mjs +7 -5
- package/dist/init/logUpdateDependenciesError.d.mts +6 -6
- package/dist/init/logUpdateDependenciesError.mjs +8 -4
- package/dist/init/updateDependenciesWithoutInstalling.d.mts +3 -3
- package/dist/init/updateDependenciesWithoutInstalling.mjs +41 -11
- package/dist/init/updateNPMScripts.d.mts +7 -2
- package/dist/init/updateNPMScripts.mjs +9 -5
- package/dist/init/updateTypeScriptConfig.d.mts +4 -1
- package/dist/init/updateTypeScriptConfig.mjs +12 -8
- package/dist/new/addClassToSegmentCode.d.mts +1 -1
- package/dist/new/addClassToSegmentCode.mjs +3 -3
- package/dist/new/addCommonTerms.d.mts +1 -1
- package/dist/new/addCommonTerms.mjs +1 -1
- package/dist/new/index.d.mts +2 -1
- package/dist/new/index.mjs +6 -5
- package/dist/new/newModule.d.mts +5 -3
- package/dist/new/newModule.mjs +34 -27
- package/dist/new/newSegment.d.mts +5 -2
- package/dist/new/newSegment.mjs +23 -16
- package/dist/new/render.d.mts +6 -3
- package/dist/new/render.mjs +15 -14
- package/dist/types.d.mts +64 -61
- package/dist/utils/chalkHighlightThing.d.mts +1 -1
- package/dist/utils/chalkHighlightThing.mjs +1 -1
- 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 +263 -0
- package/dist/utils/debounceWithArgs.d.mts +3 -2
- package/dist/utils/debounceWithArgs.mjs +1 -1
- package/dist/utils/formatLoggedSegmentName.d.mts +3 -1
- package/dist/utils/formatLoggedSegmentName.mjs +4 -3
- package/dist/utils/generateFnName.d.mts +23 -0
- package/dist/utils/generateFnName.mjs +78 -0
- package/dist/utils/getAvailablePort.d.mts +1 -2
- package/dist/utils/getAvailablePort.mjs +1 -2
- package/dist/utils/getFileSystemEntryType.d.mts +1 -1
- package/dist/utils/getFileSystemEntryType.mjs +1 -1
- package/dist/utils/getLogger.d.mts +1 -1
- package/dist/utils/getLogger.mjs +1 -1
- package/dist/utils/getNPMPackageMetadata.d.mts +2 -3
- package/dist/utils/getNPMPackageMetadata.mjs +1 -1
- package/dist/utils/getPackageJson.d.mts +3 -0
- package/dist/utils/getPackageJson.mjs +23 -0
- package/dist/utils/getPublicModuleNameFromPath.d.mts +4 -0
- package/dist/utils/getPublicModuleNameFromPath.mjs +9 -0
- package/dist/utils/locateSegments.d.mts +12 -0
- package/dist/{locateSegments.mjs → utils/locateSegments.mjs} +14 -7
- package/dist/utils/normalizeOpenAPIMixin.d.mts +15 -0
- package/dist/utils/normalizeOpenAPIMixin.mjs +96 -0
- package/dist/utils/pickSegmentFullSchema.d.mts +3 -0
- package/dist/utils/pickSegmentFullSchema.mjs +15 -0
- package/dist/utils/prettify.d.mts +1 -1
- package/dist/utils/prettify.mjs +1 -1
- package/dist/utils/removeUnlistedDirectories.d.mts +9 -0
- package/dist/utils/removeUnlistedDirectories.mjs +60 -0
- package/dist/utils/resolveAbsoluteModulePath.d.mts +2 -0
- package/dist/utils/resolveAbsoluteModulePath.mjs +32 -0
- package/dist/utils/updateConfigProperty.d.mts +2 -0
- package/dist/utils/updateConfigProperty.mjs +132 -0
- package/module-templates/arktype/controller.ts.ejs +90 -0
- package/module-templates/type/controller.ts.ejs +80 -0
- package/module-templates/type/service.ts.ejs +43 -0
- package/module-templates/valibot/controller.ts.ejs +91 -0
- package/module-templates/zod/controller.ts.ejs +98 -0
- package/package.json +50 -25
- package/client-templates/main/main.cjs.ejs +0 -15
- package/client-templates/main/main.d.cts.ejs +0 -14
- package/client-templates/module/module.d.mts.ejs +0 -14
- package/client-templates/module/module.mjs.ejs +0 -24
- package/client-templates/python/__init__.py +0 -276
- package/client-templates/ts/index.ts.ejs +0 -25
- package/dist/dev/isSchemaEmpty.d.mts +0 -2
- package/dist/dev/isSchemaEmpty.mjs +0 -4
- package/dist/dev/writeOneSchemaFile.d.mts +0 -12
- package/dist/generate/getClientTemplates.d.mts +0 -16
- package/dist/generate/getClientTemplates.mjs +0 -42
- package/dist/getProjectInfo/getConfig.d.mts +0 -11
- package/dist/getProjectInfo/getConfig.mjs +0 -35
- package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +0 -4
- package/dist/getProjectInfo/getRelativeSrcRoot.d.mts +0 -3
- package/dist/getProjectInfo/getUserConfig.d.mts +0 -9
- package/dist/getProjectInfo/getUserConfig.mjs +0 -27
- package/dist/getProjectInfo/importUncachedModule.d.mts +0 -3
- package/dist/getProjectInfo/importUncachedModule.mjs +0 -40
- package/dist/getProjectInfo/importUncachedModuleWorker.d.mts +0 -1
- package/dist/getProjectInfo/importUncachedModuleWorker.mjs +0 -25
- package/dist/init/getTemplateFilesFromPackage.d.mts +0 -7
- package/dist/init/getTemplateFilesFromPackage.mjs +0 -59
- package/dist/initProgram.d.mts +0 -2
- package/dist/initProgram.mjs +0 -22
- package/dist/locateSegments.d.mts +0 -11
- package/dist/postinstall.d.mts +0 -1
- package/dist/postinstall.mjs +0 -21
- package/templates/controller.ejs +0 -50
- package/templates/service.ejs +0 -27
package/dist/generate/index.mjs
CHANGED
|
@@ -1,94 +1,186 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
2
|
+
import * as chokidar from 'chokidar';
|
|
3
|
+
import { getProjectFullSchema } from './getProjectFullSchema.mjs';
|
|
4
|
+
import { generate } from './generate.mjs';
|
|
5
|
+
import { locateSegments } from '../utils/locateSegments.mjs';
|
|
6
|
+
import { chalkHighlightThing } from '../utils/chalkHighlightThing.mjs';
|
|
7
|
+
const THROTTLE_DELAY = 5000;
|
|
8
|
+
export class VovkGenerate {
|
|
9
|
+
#cliGenerateOptions;
|
|
10
|
+
#projectInfo;
|
|
11
|
+
#forceNothingWrittenLog;
|
|
12
|
+
constructor({ cliGenerateOptions, projectInfo, forceNothingWrittenLog, }) {
|
|
13
|
+
this.#cliGenerateOptions = cliGenerateOptions;
|
|
14
|
+
this.#projectInfo = projectInfo;
|
|
15
|
+
this.#forceNothingWrittenLog = forceNothingWrittenLog ?? true;
|
|
16
|
+
}
|
|
17
|
+
start() {
|
|
18
|
+
const { watch } = this.#cliGenerateOptions;
|
|
19
|
+
if (watch) {
|
|
20
|
+
const throttleDelay = typeof watch === 'boolean' ? THROTTLE_DELAY : parseFloat(watch) * 1e3 || THROTTLE_DELAY;
|
|
21
|
+
this.watch({ throttleDelay });
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
this.generate();
|
|
20
25
|
}
|
|
21
|
-
if (!schema.emitSchema)
|
|
22
|
-
continue;
|
|
23
26
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
: await Promise.all(templateFiles.map(async ({ templatePath, outPath, templateName }) => {
|
|
36
|
-
// Read the EJS template
|
|
37
|
-
const templateContent = await fs.readFile(templatePath, 'utf-8');
|
|
38
|
-
// Render the template
|
|
39
|
-
let rendered = templatePath.endsWith('.ejs')
|
|
40
|
-
? ejs.render(templateContent, { t }, {
|
|
41
|
-
filename: templatePath,
|
|
42
|
-
})
|
|
43
|
-
: templateContent;
|
|
44
|
-
// Optionally prettify
|
|
45
|
-
if (prettifyClient || config.prettifyClient) {
|
|
46
|
-
rendered = await prettify(rendered, outPath);
|
|
47
|
-
}
|
|
48
|
-
// Read existing file content to compare
|
|
49
|
-
const existingContent = await fs.readFile(outPath, 'utf-8').catch(() => '');
|
|
50
|
-
// Determine if we need to rewrite the file, ignore 1st line
|
|
51
|
-
const needsWriting = existingContent.trim().split('\n').slice(1).join('\n') !== rendered.trim().split('\n').slice(1).join('\n');
|
|
52
|
-
return {
|
|
53
|
-
outPath,
|
|
54
|
-
rendered,
|
|
55
|
-
needsWriting,
|
|
56
|
-
templateName,
|
|
57
|
-
};
|
|
58
|
-
}));
|
|
59
|
-
const usedTemplateNames = uniq(processedTemplates.filter(({ needsWriting }) => needsWriting).map(({ templateName }) => templateName));
|
|
60
|
-
let fullSchemaNames = [];
|
|
61
|
-
const DEFAULT_NAME = 'full-schema.json';
|
|
62
|
-
if (fullSchema) {
|
|
63
|
-
fullSchemaNames.push(typeof fullSchema === 'string' ? fullSchema : DEFAULT_NAME);
|
|
64
|
-
fullSchemaNames.push(...templateFiles
|
|
65
|
-
.filter(({ fullSchema }) => fullSchema)
|
|
66
|
-
.map(({ fullSchema }) => (typeof fullSchema === 'string' ? fullSchema : DEFAULT_NAME)));
|
|
27
|
+
async generate() {
|
|
28
|
+
const fullSchema = await this.getFullSchema();
|
|
29
|
+
const { log, config, apiDirAbsolutePath } = this.#projectInfo;
|
|
30
|
+
const locatedSegments = await locateSegments({ dir: apiDirAbsolutePath, config, log });
|
|
31
|
+
await generate({
|
|
32
|
+
projectInfo: this.#projectInfo,
|
|
33
|
+
fullSchema,
|
|
34
|
+
forceNothingWrittenLog: this.#forceNothingWrittenLog,
|
|
35
|
+
cliGenerateOptions: this.#cliGenerateOptions,
|
|
36
|
+
locatedSegments,
|
|
37
|
+
});
|
|
67
38
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
await
|
|
39
|
+
async getFullSchema() {
|
|
40
|
+
const { log, config, cwd, isNextInstalled } = this.#projectInfo;
|
|
41
|
+
const { schemaPath } = this.#cliGenerateOptions;
|
|
42
|
+
const fullSchema = await getProjectFullSchema({
|
|
43
|
+
schemaOutAbsolutePath: path.resolve(cwd, schemaPath ?? config.schemaOutDir),
|
|
44
|
+
isNextInstalled,
|
|
45
|
+
log,
|
|
46
|
+
config,
|
|
47
|
+
});
|
|
48
|
+
return fullSchema;
|
|
72
49
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
log.
|
|
78
|
-
|
|
50
|
+
watch({ throttleDelay }) {
|
|
51
|
+
const { openapiSpec, schemaPath } = this.#cliGenerateOptions;
|
|
52
|
+
const { log, cwd, config } = this.#projectInfo;
|
|
53
|
+
if (openapiSpec) {
|
|
54
|
+
log.debug(`Watching OpenAPI spec: ${openapiSpec}`);
|
|
55
|
+
this.watchOpenApiSpec({ openApiSpec: openapiSpec, throttleDelay });
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const resolvedSchemaPath = path.resolve(cwd, schemaPath ?? config.schemaOutDir);
|
|
59
|
+
log.debug(`Watching schema directory: ${resolvedSchemaPath}`);
|
|
60
|
+
this.watchSchema({ schemaPath: resolvedSchemaPath, throttleDelay });
|
|
61
|
+
}
|
|
79
62
|
}
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
63
|
+
watchSchema({ schemaPath, throttleDelay }) {
|
|
64
|
+
const { log } = this.#projectInfo;
|
|
65
|
+
let lastGenerationTime = 0;
|
|
66
|
+
chokidar
|
|
67
|
+
.watch(schemaPath, {
|
|
68
|
+
persistent: true,
|
|
69
|
+
ignoreInitial: true,
|
|
70
|
+
})
|
|
71
|
+
.on('all', (event, path) => {
|
|
72
|
+
if (event === 'change' || event === 'add' || event === 'ready' || event === 'unlink') {
|
|
73
|
+
log.debug(`Schema file ${event}: ${path}`);
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
const shouldGenerateImmediately = now - lastGenerationTime > throttleDelay;
|
|
76
|
+
const generateCode = async () => {
|
|
77
|
+
try {
|
|
78
|
+
lastGenerationTime = Date.now();
|
|
79
|
+
await this.generate();
|
|
80
|
+
log.debug(`Regenerated from schema changes`);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
log.error(`Failed to regenerate from schema: ${error instanceof Error ? error.message : String(error)}`);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
// Generate immediately if it's been a while since the last generation
|
|
87
|
+
if (shouldGenerateImmediately) {
|
|
88
|
+
generateCode();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
84
92
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
93
|
+
watchOpenApiSpec({ openApiSpec, throttleDelay }) {
|
|
94
|
+
const fileSpecs = openApiSpec.filter((spec) => !spec.startsWith('http://') && !spec.startsWith('https://'));
|
|
95
|
+
const remoteSpecs = openApiSpec.filter((spec) => spec.startsWith('http://') || spec.startsWith('https://'));
|
|
96
|
+
if (fileSpecs.length) {
|
|
97
|
+
this.watchOpenApiSpecLocal({
|
|
98
|
+
openApiSpecPaths: fileSpecs,
|
|
99
|
+
throttleDelay,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (remoteSpecs.length) {
|
|
103
|
+
remoteSpecs.forEach((spec) => {
|
|
104
|
+
this.watchOpenApiSpecRemote({
|
|
105
|
+
openApiSpecUrl: spec,
|
|
106
|
+
throttleDelay,
|
|
107
|
+
});
|
|
108
|
+
});
|
|
89
109
|
}
|
|
90
|
-
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
|
|
110
|
+
}
|
|
111
|
+
watchOpenApiSpecLocal({ openApiSpecPaths, throttleDelay }) {
|
|
112
|
+
const { log, cwd } = this.#projectInfo;
|
|
113
|
+
let lastGenerationTime = 0;
|
|
114
|
+
chokidar
|
|
115
|
+
.watch(openApiSpecPaths, {
|
|
116
|
+
cwd,
|
|
117
|
+
persistent: true,
|
|
118
|
+
ignoreInitial: false,
|
|
119
|
+
})
|
|
120
|
+
.on('all', (event, path) => {
|
|
121
|
+
if (event === 'change' || event === 'add' || event === 'ready' || event === 'unlink') {
|
|
122
|
+
log.debug(`OpenAPI spec file changed: ${path}`);
|
|
123
|
+
const now = Date.now();
|
|
124
|
+
const shouldGenerateImmediately = now - lastGenerationTime > throttleDelay;
|
|
125
|
+
const generateCode = async () => {
|
|
126
|
+
try {
|
|
127
|
+
lastGenerationTime = Date.now();
|
|
128
|
+
await this.generate();
|
|
129
|
+
log.debug(`Regenerated from OpenAPI spec changes`);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
log.error(`Failed to regenerate from OpenAPI spec: ${error instanceof Error ? error.message : String(error)}`);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
// Generate immediately if it's been a while since the last generation
|
|
136
|
+
if (shouldGenerateImmediately) {
|
|
137
|
+
generateCode();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
watchOpenApiSpecRemote({ openApiSpecUrl, throttleDelay }) {
|
|
143
|
+
const { log } = this.#projectInfo;
|
|
144
|
+
let lastContent = null;
|
|
145
|
+
let isPolling = false;
|
|
146
|
+
log.info(`Polling remote OpenAPI spec at ${chalkHighlightThing(openApiSpecUrl)} every ${throttleDelay}ms`);
|
|
147
|
+
const pollRemoteSpec = async () => {
|
|
148
|
+
if (isPolling)
|
|
149
|
+
return;
|
|
150
|
+
isPolling = true;
|
|
151
|
+
try {
|
|
152
|
+
const response = await fetch(openApiSpecUrl, {
|
|
153
|
+
headers: {
|
|
154
|
+
Accept: 'application/json, application/yaml',
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
if (!response.ok) {
|
|
158
|
+
log.error(`Failed to fetch OpenAPI spec: ${response.status} ${response.statusText}`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const content = await response.text();
|
|
162
|
+
if (content !== lastContent) {
|
|
163
|
+
log.info(`Remote OpenAPI spec changed at ${chalkHighlightThing(openApiSpecUrl)}`);
|
|
164
|
+
lastContent = content;
|
|
165
|
+
try {
|
|
166
|
+
await this.generate();
|
|
167
|
+
log.debug(`Regenerated from remote OpenAPI spec changes`);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
log.error(`Failed to regenerate from OpenAPI spec: ${error instanceof Error ? error.message : String(error)}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
log.error(`Error polling OpenAPI spec: ${error instanceof Error ? error.message : String(error)}`);
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
isPolling = false;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
// Initial fetch
|
|
182
|
+
pollRemoteSpec();
|
|
183
|
+
// Set up polling
|
|
184
|
+
setInterval(pollRemoteSpec, throttleDelay);
|
|
185
|
+
}
|
|
94
186
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { VovkSchema } from 'vovk';
|
|
2
|
+
import { type VovkReadmeConfig, type VovkSamplesConfig, type VovkStrictConfig } from 'vovk/internal';
|
|
3
|
+
import type { PackageJson } from 'type-fest';
|
|
4
|
+
import type { OpenAPIObject } from 'openapi3-ts/oas31';
|
|
5
|
+
import type { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
6
|
+
import type { ClientTemplateFile } from './getClientTemplateFiles.mjs';
|
|
7
|
+
import type { Segment } from '../utils/locateSegments.mjs';
|
|
8
|
+
export declare function normalizeOutTemplatePath(out: string, packageJson: PackageJson): string;
|
|
9
|
+
export declare function writeOneClientFile({ cwd, projectInfo, clientTemplateFile, fullSchema, prettifyClient, segmentName, templateContent, matterResult: { data, content }, openAPIObject, package: packageJson, readme, samples, reExports, isEnsuringClient, outCwdRelativeDir, templateDef, locatedSegments, isNodeNextResolution, hasMixins, isVovkProject, vovkCliPackage, isBundle, origin, configKey, cliSchemaPath, projectConfig, }: {
|
|
10
|
+
cwd: string;
|
|
11
|
+
projectInfo: ProjectInfo;
|
|
12
|
+
clientTemplateFile: ClientTemplateFile;
|
|
13
|
+
fullSchema: VovkSchema;
|
|
14
|
+
prettifyClient: boolean;
|
|
15
|
+
segmentName: string | null;
|
|
16
|
+
templateContent: string;
|
|
17
|
+
matterResult: {
|
|
18
|
+
data: {
|
|
19
|
+
imports?: string[];
|
|
20
|
+
};
|
|
21
|
+
content: string;
|
|
22
|
+
};
|
|
23
|
+
openAPIObject: OpenAPIObject;
|
|
24
|
+
package: PackageJson;
|
|
25
|
+
readme: VovkReadmeConfig;
|
|
26
|
+
samples: VovkSamplesConfig;
|
|
27
|
+
reExports: VovkStrictConfig['outputConfig']['reExports'];
|
|
28
|
+
isEnsuringClient: boolean;
|
|
29
|
+
outCwdRelativeDir: string;
|
|
30
|
+
templateDef: VovkStrictConfig['clientTemplateDefs'][string];
|
|
31
|
+
locatedSegments: Segment[];
|
|
32
|
+
isNodeNextResolution: boolean;
|
|
33
|
+
hasMixins: boolean;
|
|
34
|
+
isVovkProject: boolean;
|
|
35
|
+
vovkCliPackage: PackageJson;
|
|
36
|
+
isBundle: boolean;
|
|
37
|
+
origin: string | null;
|
|
38
|
+
configKey: 'composedClient' | 'segmentedClient';
|
|
39
|
+
cliSchemaPath: string | null;
|
|
40
|
+
projectConfig: VovkStrictConfig;
|
|
41
|
+
}): Promise<{
|
|
42
|
+
written: boolean;
|
|
43
|
+
}>;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import ejs from 'ejs';
|
|
4
|
+
import _ from 'lodash';
|
|
5
|
+
import * as YAML from 'yaml';
|
|
6
|
+
import TOML from '@iarna/toml';
|
|
7
|
+
import { createCodeSamples, VovkSchemaIdEnum, } from 'vovk/internal';
|
|
8
|
+
import { compileJSONSchemaToTypeScriptType } from '../utils/compileJSONSchemaToTypeScriptType.mjs';
|
|
9
|
+
import { getTemplateClientImports } from './getTemplateClientImports.mjs';
|
|
10
|
+
import { chalkHighlightThing } from '../utils/chalkHighlightThing.mjs';
|
|
11
|
+
import { ROOT_SEGMENT_FILE_NAME } from '../dev/writeOneSegmentSchemaFile.mjs';
|
|
12
|
+
import { prettify } from '../utils/prettify.mjs';
|
|
13
|
+
export function normalizeOutTemplatePath(out, packageJson) {
|
|
14
|
+
return out.replace('[package_name]', packageJson.name?.replace(/-/g, '_') ?? 'my_package_name');
|
|
15
|
+
}
|
|
16
|
+
export async function writeOneClientFile({ cwd, projectInfo, clientTemplateFile, fullSchema, prettifyClient, segmentName,
|
|
17
|
+
// imports,
|
|
18
|
+
templateContent, matterResult: { data, content }, openAPIObject, package: packageJson, readme, samples, reExports, isEnsuringClient, outCwdRelativeDir, templateDef, locatedSegments, isNodeNextResolution, hasMixins, isVovkProject, vovkCliPackage, isBundle, origin, configKey, cliSchemaPath, projectConfig, }) {
|
|
19
|
+
const { config, log } = projectInfo;
|
|
20
|
+
const { templateFilePath, relativeDir } = clientTemplateFile;
|
|
21
|
+
const locatedSegmentsByName = _.keyBy(locatedSegments, 'segmentName');
|
|
22
|
+
const outPath = normalizeOutTemplatePath(path.resolve(cwd, outCwdRelativeDir, typeof segmentName === 'string' ? segmentName || ROOT_SEGMENT_FILE_NAME : '', relativeDir, path.basename(templateFilePath).replace('.ejs', '')), packageJson);
|
|
23
|
+
let placeholder = templateFilePath.endsWith('.json.ejs')
|
|
24
|
+
? ''
|
|
25
|
+
: `// This is a temporary placeholder to avoid compilation errors if client is imported before it's generated.
|
|
26
|
+
// If you still see this text, the client is not generated yet because of an unknown problem.
|
|
27
|
+
// Feel free to report an issue at https://github.com/finom/vovk/issues`;
|
|
28
|
+
placeholder = outPath.endsWith('.py') ? placeholder.replace(/\/\//g, '#') : placeholder;
|
|
29
|
+
const getFirstLineBanner = (type = 'c') => {
|
|
30
|
+
const text = `Generated by vovk-cli v${vovkCliPackage.version} at ${new Date().toISOString()}`;
|
|
31
|
+
switch (type) {
|
|
32
|
+
case 'html':
|
|
33
|
+
return `<!-- ${text} -->`;
|
|
34
|
+
case 'sh':
|
|
35
|
+
return `# ${text}`;
|
|
36
|
+
case 'c':
|
|
37
|
+
return `// ${text}`;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
reExports = _.mapValues(reExports ?? {}, (p) => p.startsWith('.')
|
|
41
|
+
? path.relative(path.join(outCwdRelativeDir, typeof segmentName === 'string' ? segmentName || ROOT_SEGMENT_FILE_NAME : '.'), path.resolve(cwd, p))
|
|
42
|
+
: p);
|
|
43
|
+
// Data for the EJS templates:
|
|
44
|
+
const t = {
|
|
45
|
+
_, // lodash
|
|
46
|
+
hasMixins,
|
|
47
|
+
isVovkProject,
|
|
48
|
+
package: packageJson,
|
|
49
|
+
readme,
|
|
50
|
+
samples,
|
|
51
|
+
reExports,
|
|
52
|
+
openapi: openAPIObject,
|
|
53
|
+
ROOT_SEGMENT_FILE_NAME,
|
|
54
|
+
apiRoot: origin ? `${origin}/${config.rootEntry}` : undefined,
|
|
55
|
+
imports: {},
|
|
56
|
+
schema: fullSchema,
|
|
57
|
+
config: projectConfig,
|
|
58
|
+
VovkSchemaIdEnum,
|
|
59
|
+
createCodeSamples,
|
|
60
|
+
compileJSONSchemaToTypeScriptType,
|
|
61
|
+
YAML,
|
|
62
|
+
TOML,
|
|
63
|
+
getFirstLineBanner,
|
|
64
|
+
nodeNextResolutionExt: {
|
|
65
|
+
ts: isNodeNextResolution ? '.ts' : '',
|
|
66
|
+
js: isNodeNextResolution ? '.js' : '',
|
|
67
|
+
mjs: isNodeNextResolution ? '.mjs' : '',
|
|
68
|
+
},
|
|
69
|
+
schemaOutDir: typeof segmentName === 'string'
|
|
70
|
+
? path.relative(path.join(outCwdRelativeDir, segmentName || ROOT_SEGMENT_FILE_NAME), cliSchemaPath ?? config.schemaOutDir)
|
|
71
|
+
: path.relative(outCwdRelativeDir, cliSchemaPath ?? config.schemaOutDir),
|
|
72
|
+
commonImports: getTemplateClientImports({
|
|
73
|
+
config: projectConfig,
|
|
74
|
+
fullSchema,
|
|
75
|
+
isBundle,
|
|
76
|
+
outCwdRelativeDir,
|
|
77
|
+
segmentName,
|
|
78
|
+
outputConfigs: [templateDef.outputConfig ?? {}],
|
|
79
|
+
}).composedClient,
|
|
80
|
+
segmentImports: Object.fromEntries(Object.values(fullSchema.segments).map(({ segmentName: sName }) => {
|
|
81
|
+
const clientImports = getTemplateClientImports({
|
|
82
|
+
config: projectConfig,
|
|
83
|
+
fullSchema,
|
|
84
|
+
segmentName: sName,
|
|
85
|
+
isBundle,
|
|
86
|
+
outCwdRelativeDir,
|
|
87
|
+
outputConfigs: [projectConfig[configKey].outputConfig ?? {}, templateDef.outputConfig ?? {}],
|
|
88
|
+
});
|
|
89
|
+
const imports = configKey === 'composedClient' ? clientImports.composedClient : clientImports.segmentedClient[sName];
|
|
90
|
+
return [sName, imports];
|
|
91
|
+
})),
|
|
92
|
+
segmentMeta: Object.fromEntries(Object.values(fullSchema.segments).map(({ segmentName: sName, forceApiRoot }) => {
|
|
93
|
+
const { routeFilePath = null } = locatedSegmentsByName[sName] ?? {};
|
|
94
|
+
const segmentImportPath = routeFilePath
|
|
95
|
+
? path.relative(path.resolve(cwd, outCwdRelativeDir, typeof segmentName === 'string' ? segmentName || ROOT_SEGMENT_FILE_NAME : '.'), path.resolve(cwd, routeFilePath))
|
|
96
|
+
: null;
|
|
97
|
+
const segmentConfig = {
|
|
98
|
+
...config.outputConfig.segments?.[sName],
|
|
99
|
+
// ...templateDef.outputConfig?.segments?.[sName],
|
|
100
|
+
};
|
|
101
|
+
const { origin: segmentConfigOrigin, rootEntry: segmentConfigRootEntry, segmentNameOverride } = segmentConfig;
|
|
102
|
+
return [
|
|
103
|
+
sName,
|
|
104
|
+
{
|
|
105
|
+
forceApiRoot: forceApiRoot ??
|
|
106
|
+
(segmentConfigOrigin || segmentConfigRootEntry
|
|
107
|
+
? `${segmentConfigOrigin ?? origin}/${segmentConfigRootEntry ?? config.rootEntry}`
|
|
108
|
+
: null),
|
|
109
|
+
routeFilePath,
|
|
110
|
+
segmentImportPath,
|
|
111
|
+
segmentNameOverride,
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
})),
|
|
115
|
+
};
|
|
116
|
+
if (Array.isArray(data.imports)) {
|
|
117
|
+
for (const imp of data.imports) {
|
|
118
|
+
t.imports = {
|
|
119
|
+
[imp]: await import(imp),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Render the template
|
|
124
|
+
let rendered = templateFilePath.endsWith('.ejs')
|
|
125
|
+
? await ejs.render(content, { t }, {
|
|
126
|
+
filename: templateFilePath,
|
|
127
|
+
async: true,
|
|
128
|
+
})
|
|
129
|
+
: templateContent;
|
|
130
|
+
// Optionally prettify
|
|
131
|
+
if (prettifyClient) {
|
|
132
|
+
rendered = await prettify(rendered, outPath);
|
|
133
|
+
}
|
|
134
|
+
if (isEnsuringClient) {
|
|
135
|
+
rendered = `${rendered}\n\n${placeholder}`;
|
|
136
|
+
}
|
|
137
|
+
// Read existing file content to compare
|
|
138
|
+
const existingContent = await fs.readFile(outPath, 'utf-8').catch(() => null);
|
|
139
|
+
// Determine if we need to rewrite the file, ignore 1st line
|
|
140
|
+
const needsWriting = isEnsuringClient
|
|
141
|
+
? !existingContent
|
|
142
|
+
: !existingContent ||
|
|
143
|
+
existingContent.trim().split('\n').slice(1).join('\n') !== rendered.trim().split('\n').slice(1).join('\n');
|
|
144
|
+
if (needsWriting) {
|
|
145
|
+
log.debug(`Writing file: ${chalkHighlightThing(outPath)} ${existingContent ? '(updated)' : '(new)'}`);
|
|
146
|
+
await fs.mkdir(path.dirname(outPath), { recursive: true });
|
|
147
|
+
await fs.writeFile(outPath, rendered, 'utf-8');
|
|
148
|
+
}
|
|
149
|
+
return { written: needsWriting };
|
|
150
|
+
}
|
package/dist/getProjectInfo/{getConfigAbsolutePaths.mjs → getConfig/getConfigAbsolutePaths.mjs}
RENAMED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
export
|
|
3
|
+
export async function getConfigAbsolutePaths({ cwd, configPath, relativePath, }) {
|
|
4
|
+
if (configPath) {
|
|
5
|
+
return [path.resolve(cwd, configPath)];
|
|
6
|
+
}
|
|
4
7
|
const rootDir = path.resolve(cwd, relativePath || '');
|
|
5
8
|
const baseName = 'vovk.config';
|
|
6
9
|
const extensions = ['cjs', 'mjs', 'js'];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import getFileSystemEntryType,
|
|
3
|
-
export
|
|
2
|
+
import { getFileSystemEntryType, FileSystemEntryType } from '../../utils/getFileSystemEntryType.mjs';
|
|
3
|
+
export async function getRelativeSrcRoot({ cwd }) {
|
|
4
4
|
// Next.js Docs: src/app or src/pages will be ignored if app or pages are present in the root directory.
|
|
5
5
|
if ((await getFileSystemEntryType(path.join(cwd, 'app'))) === FileSystemEntryType.DIRECTORY) {
|
|
6
6
|
return '.';
|
|
@@ -8,5 +8,5 @@ export default async function getRelativeSrcRoot({ cwd }) {
|
|
|
8
8
|
else if ((await getFileSystemEntryType(path.join(cwd, 'src/app'))) === FileSystemEntryType.DIRECTORY) {
|
|
9
9
|
return './src';
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
return null;
|
|
12
12
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { VovkStrictConfig } from 'vovk/internal';
|
|
2
|
+
export declare enum BuiltInTemplateName {
|
|
3
|
+
tsBase = "tsBase",
|
|
4
|
+
jsBase = "jsBase",
|
|
5
|
+
ts = "ts",
|
|
6
|
+
js = "js",
|
|
7
|
+
schemaTs = "schemaTs",
|
|
8
|
+
schemaJs = "schemaJs",
|
|
9
|
+
schemaJson = "schemaJson",
|
|
10
|
+
openapiTs = "openapiTs",
|
|
11
|
+
openapiJs = "openapiJs",
|
|
12
|
+
openapiJson = "openapiJson",
|
|
13
|
+
readme = "readme",
|
|
14
|
+
packageJson = "packageJson",
|
|
15
|
+
mixins = "mixins",
|
|
16
|
+
rsSrc = "rsSrc",
|
|
17
|
+
rsPkg = "rsPkg",
|
|
18
|
+
rsReadme = "rsReadme",
|
|
19
|
+
rs = "rs",
|
|
20
|
+
pySrc = "pySrc",
|
|
21
|
+
pyPkg = "pyPkg",
|
|
22
|
+
pyReadme = "pyReadme",
|
|
23
|
+
py = "py"
|
|
24
|
+
}
|
|
25
|
+
export declare function getTemplateDefs(userTemplateDefs?: VovkStrictConfig['clientTemplateDefs']): VovkStrictConfig['clientTemplateDefs'];
|