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/dev/index.mjs
CHANGED
|
@@ -1,34 +1,48 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { VovkSchemaIdEnum } from 'vovk/internal';
|
|
3
4
|
import * as chokidar from 'chokidar';
|
|
4
5
|
import { Agent, setGlobalDispatcher } from 'undici';
|
|
5
6
|
import keyBy from 'lodash/keyBy.js';
|
|
6
7
|
import capitalize from 'lodash/capitalize.js';
|
|
7
8
|
import debounce from 'lodash/debounce.js';
|
|
8
9
|
import once from 'lodash/once.js';
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
10
|
+
import isEmpty from 'lodash/isEmpty.js';
|
|
11
|
+
import { ensureSchemaFiles, debouncedEnsureSchemaFiles } from './ensureSchemaFiles.mjs';
|
|
12
|
+
import { writeOneSegmentSchemaFile } from './writeOneSegmentSchemaFile.mjs';
|
|
13
|
+
import { logDiffResult } from './logDiffResult.mjs';
|
|
14
|
+
import { ensureClient } from '../generate/ensureClient.mjs';
|
|
15
|
+
import { getProjectInfo } from '../getProjectInfo/index.mjs';
|
|
16
|
+
import { generate } from '../generate/generate.mjs';
|
|
17
|
+
import { locateSegments } from '../utils/locateSegments.mjs';
|
|
18
|
+
import { debounceWithArgs } from '../utils/debounceWithArgs.mjs';
|
|
19
|
+
import { formatLoggedSegmentName } from '../utils/formatLoggedSegmentName.mjs';
|
|
20
|
+
import { writeMetaJson } from './writeMetaJson.mjs';
|
|
21
|
+
import { chalkHighlightThing } from '../utils/chalkHighlightThing.mjs';
|
|
22
|
+
import { getMetaSchema } from '../getProjectInfo/getMetaSchema.mjs';
|
|
19
23
|
export class VovkDev {
|
|
20
24
|
#projectInfo;
|
|
21
25
|
#segments = [];
|
|
22
|
-
#
|
|
26
|
+
#schemaSegments = {};
|
|
23
27
|
#isWatching = false;
|
|
24
28
|
#modulesWatcher = null;
|
|
25
29
|
#segmentWatcher = null;
|
|
26
30
|
#onFirstTimeGenerate = null;
|
|
31
|
+
#schemaOut = null;
|
|
32
|
+
#devHttps;
|
|
33
|
+
#logLevel;
|
|
34
|
+
constructor({ schemaOut, devHttps, logLevel }) {
|
|
35
|
+
this.#schemaOut = schemaOut || null;
|
|
36
|
+
this.#devHttps = devHttps || false;
|
|
37
|
+
this.#logLevel = logLevel || 'info';
|
|
38
|
+
}
|
|
27
39
|
#watchSegments = (callback) => {
|
|
28
40
|
const segmentReg = /\/?\[\[\.\.\.[a-zA-Z-_]+\]\]\/route.ts$/;
|
|
29
|
-
const { cwd, log, config,
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
const { cwd, log, config, apiDirAbsolutePath } = this.#projectInfo;
|
|
42
|
+
if (!apiDirAbsolutePath) {
|
|
43
|
+
throw new Error('Unable to watch segments. It looks like CWD is not a Next.js app.');
|
|
44
|
+
}
|
|
45
|
+
const schemaOutAbsolutePath = path.resolve(cwd, this.#schemaOut ?? config.schemaOutDir);
|
|
32
46
|
const getSegmentName = (filePath) => path.relative(apiDirAbsolutePath, filePath).replace(segmentReg, '');
|
|
33
47
|
log.debug(`Watching segments at ${apiDirAbsolutePath}`);
|
|
34
48
|
this.#segmentWatcher = chokidar
|
|
@@ -47,7 +61,6 @@ export class VovkDev {
|
|
|
47
61
|
{
|
|
48
62
|
routeFilePath: filePath,
|
|
49
63
|
segmentName,
|
|
50
|
-
segmentImportPath: path.relative(config.clientOutDir, filePath), // TODO DRY locateSegments
|
|
51
64
|
},
|
|
52
65
|
];
|
|
53
66
|
log.info(`${capitalize(formatLoggedSegmentName(segmentName))} has been added`);
|
|
@@ -63,14 +76,22 @@ export class VovkDev {
|
|
|
63
76
|
})
|
|
64
77
|
.on('addDir', async (dirPath) => {
|
|
65
78
|
log.debug(`Directory ${dirPath} has been added to segments folder`);
|
|
66
|
-
this.#segments = await locateSegments({
|
|
79
|
+
this.#segments = await locateSegments({
|
|
80
|
+
dir: apiDirAbsolutePath,
|
|
81
|
+
config,
|
|
82
|
+
log: this.#projectInfo.log,
|
|
83
|
+
});
|
|
67
84
|
for (const { segmentName } of this.#segments) {
|
|
68
85
|
void this.#requestSchema(segmentName);
|
|
69
86
|
}
|
|
70
87
|
})
|
|
71
88
|
.on('unlinkDir', async (dirPath) => {
|
|
72
89
|
log.debug(`Directory ${dirPath} has been removed from segments folder`);
|
|
73
|
-
this.#segments = await locateSegments({
|
|
90
|
+
this.#segments = await locateSegments({
|
|
91
|
+
dir: apiDirAbsolutePath,
|
|
92
|
+
config,
|
|
93
|
+
log: this.#projectInfo.log,
|
|
94
|
+
});
|
|
74
95
|
for (const { segmentName } of this.#segments) {
|
|
75
96
|
void this.#requestSchema(segmentName);
|
|
76
97
|
}
|
|
@@ -138,13 +159,16 @@ export class VovkDev {
|
|
|
138
159
|
let isInitial = true;
|
|
139
160
|
let isReady = false;
|
|
140
161
|
const handle = debounce(async () => {
|
|
141
|
-
this.#projectInfo = await getProjectInfo();
|
|
162
|
+
this.#projectInfo = await getProjectInfo({ logLevel: this.#logLevel });
|
|
163
|
+
const { config, apiDirAbsolutePath } = this.#projectInfo;
|
|
164
|
+
this.#segments = await locateSegments({ dir: apiDirAbsolutePath, config, log });
|
|
142
165
|
await this.#modulesWatcher?.close();
|
|
143
166
|
await this.#segmentWatcher?.close();
|
|
144
167
|
await Promise.all([
|
|
145
168
|
new Promise((resolve) => this.#watchModules(() => resolve(0))),
|
|
146
169
|
new Promise((resolve) => this.#watchSegments(() => resolve(0))),
|
|
147
170
|
]);
|
|
171
|
+
const schemaOutAbsolutePath = path.join(cwd, this.#schemaOut ?? this.#projectInfo.config.schemaOutDir);
|
|
148
172
|
if (isInitial) {
|
|
149
173
|
callback();
|
|
150
174
|
}
|
|
@@ -152,18 +176,12 @@ export class VovkDev {
|
|
|
152
176
|
log.info('Config file has been updated');
|
|
153
177
|
this.#generate();
|
|
154
178
|
}
|
|
179
|
+
await writeMetaJson(schemaOutAbsolutePath, this.#projectInfo);
|
|
155
180
|
isInitial = false;
|
|
156
181
|
}, 1000);
|
|
157
182
|
chokidar
|
|
158
|
-
// .watch(['vovk.config.{js,mjs
|
|
159
|
-
.watch([
|
|
160
|
-
'vovk.config.js',
|
|
161
|
-
'vovk.config.mjs',
|
|
162
|
-
'vovk.config.cjs',
|
|
163
|
-
'.config/vovk.config.js',
|
|
164
|
-
'.config/vovk.config.mjs',
|
|
165
|
-
'.config/vovk.config.cjs',
|
|
166
|
-
], {
|
|
183
|
+
// .watch(['vovk.config.{js,mjs}', '.config/vovk.config.{js,mjs}'], {
|
|
184
|
+
.watch(['vovk.config.js', 'vovk.config.mjs', '.config/vovk.config.js', '.config/vovk.config.mjs'], {
|
|
167
185
|
persistent: true,
|
|
168
186
|
cwd,
|
|
169
187
|
ignoreInitial: false,
|
|
@@ -187,7 +205,6 @@ export class VovkDev {
|
|
|
187
205
|
throw new Error('Already watching');
|
|
188
206
|
const { log } = this.#projectInfo;
|
|
189
207
|
log.debug(`Starting segments and modules watcher. Detected initial segments: ${JSON.stringify(this.#segments.map((s) => s.segmentName))}.`);
|
|
190
|
-
await ensureClient(this.#projectInfo);
|
|
191
208
|
// automatically watches segments and modules
|
|
192
209
|
this.#watchConfig(callback);
|
|
193
210
|
}
|
|
@@ -203,11 +220,11 @@ export class VovkDev {
|
|
|
203
220
|
const importRegex = /import\s*{[^}]*\b(get|post|put|del|head|options)\b[^}]*}\s*from\s*['"]vovk['"]/;
|
|
204
221
|
if (importRegex.test(code) && namesOfClasses.length) {
|
|
205
222
|
const affectedSegments = this.#segments.filter((s) => {
|
|
206
|
-
const
|
|
207
|
-
if (!
|
|
223
|
+
const segmentSchema = this.#schemaSegments[s.segmentName];
|
|
224
|
+
if (!segmentSchema)
|
|
208
225
|
return false;
|
|
209
|
-
const controllersByOriginalName = keyBy(
|
|
210
|
-
return namesOfClasses.some((name) =>
|
|
226
|
+
const controllersByOriginalName = keyBy(segmentSchema.controllers, 'originalControllerName');
|
|
227
|
+
return namesOfClasses.some((name) => segmentSchema.controllers[name] || controllersByOriginalName[name]);
|
|
211
228
|
});
|
|
212
229
|
if (affectedSegments.length) {
|
|
213
230
|
log.debug(`A file with controller ${namesOfClasses.join(', ')} have been modified at path "${filePath}". Segment(s) affected: ${JSON.stringify(affectedSegments.map((s) => s.segmentName))}`);
|
|
@@ -220,58 +237,82 @@ export class VovkDev {
|
|
|
220
237
|
}
|
|
221
238
|
}
|
|
222
239
|
else {
|
|
223
|
-
log.debug(`The file does not contain any controller`);
|
|
240
|
+
log.debug(`The file ${filePath} does not contain any controller`);
|
|
224
241
|
}
|
|
225
242
|
};
|
|
226
243
|
#requestSchema = debounceWithArgs(async (segmentName) => {
|
|
227
244
|
const { apiRoot, log, port, config } = this.#projectInfo;
|
|
228
|
-
const
|
|
245
|
+
const devHttps = this.#devHttps ?? config.devHttps;
|
|
229
246
|
const endpoint = `${apiRoot.startsWith(`http${devHttps ? 's' : ''}://`) ? apiRoot : `http${devHttps ? 's' : ''}://localhost:${port}${apiRoot}`}/${segmentName ? `${segmentName}/` : ''}_schema_`;
|
|
230
247
|
log.debug(`Requesting schema for ${formatLoggedSegmentName(segmentName)} at ${endpoint}`);
|
|
231
248
|
try {
|
|
232
249
|
const resp = await fetch(endpoint);
|
|
250
|
+
const text = await resp.text();
|
|
251
|
+
let json;
|
|
252
|
+
try {
|
|
253
|
+
json = JSON.parse(text);
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
log.error(`Error parsing JSON from ${chalkHighlightThing(endpoint)} for ${formatLoggedSegmentName(segmentName)}: ${error?.message}`);
|
|
257
|
+
log.error(`Response text: ${text.length > 2000 ? `${text.slice(0, 2000)}...` : text}`);
|
|
258
|
+
return { isError: true };
|
|
259
|
+
}
|
|
233
260
|
if (resp.status !== 200) {
|
|
234
261
|
const probableCause = {
|
|
235
262
|
404: 'The segment did not compile or config.origin is wrong.',
|
|
236
263
|
}[resp.status];
|
|
237
|
-
log.warn(`Schema request to ${formatLoggedSegmentName(segmentName)} failed with status code ${resp.status} but expected 200.${probableCause ? ` Probable cause: ${probableCause}
|
|
264
|
+
log.warn(`Schema request to ${chalkHighlightThing(endpoint)} for ${formatLoggedSegmentName(segmentName)} failed with status code ${resp.status} but expected 200.${probableCause ? ` Probable cause: ${probableCause}.` : ''}.`);
|
|
265
|
+
log.warn(`Response from ${formatLoggedSegmentName(segmentName)}: ${text}`);
|
|
238
266
|
return { isError: true };
|
|
239
267
|
}
|
|
240
|
-
let
|
|
268
|
+
let segmentSchema = null;
|
|
241
269
|
try {
|
|
242
|
-
({ schema } =
|
|
270
|
+
({ schema: segmentSchema } = json);
|
|
243
271
|
}
|
|
244
272
|
catch (error) {
|
|
245
|
-
log.error(`Error parsing schema for ${formatLoggedSegmentName(segmentName)}: ${error
|
|
273
|
+
log.error(`Error parsing schema for ${formatLoggedSegmentName(segmentName)}: ${error?.message}`);
|
|
246
274
|
}
|
|
247
|
-
await this.#
|
|
275
|
+
await this.#handleSegmentSchema(segmentName, segmentSchema);
|
|
248
276
|
}
|
|
249
277
|
catch (error) {
|
|
250
|
-
log.error(`Error requesting schema for ${formatLoggedSegmentName(segmentName)} at ${endpoint}: ${error
|
|
278
|
+
log.error(`Error requesting schema for ${formatLoggedSegmentName(segmentName)} at ${endpoint}: ${error?.message}`);
|
|
251
279
|
return { isError: true };
|
|
252
280
|
}
|
|
253
281
|
return { isError: false };
|
|
254
282
|
}, 500);
|
|
255
|
-
#generate = debounce(() =>
|
|
256
|
-
|
|
283
|
+
#generate = debounce(() => {
|
|
284
|
+
const fullSchema = {
|
|
285
|
+
$schema: VovkSchemaIdEnum.SCHEMA,
|
|
286
|
+
segments: this.#schemaSegments,
|
|
287
|
+
meta: getMetaSchema({
|
|
288
|
+
config: this.#projectInfo.config,
|
|
289
|
+
}),
|
|
290
|
+
};
|
|
291
|
+
return generate({
|
|
292
|
+
projectInfo: this.#projectInfo,
|
|
293
|
+
fullSchema,
|
|
294
|
+
locatedSegments: this.#segments,
|
|
295
|
+
}).then(this.#onFirstTimeGenerate);
|
|
296
|
+
}, 1000);
|
|
297
|
+
async #handleSegmentSchema(segmentName, segmentSchema) {
|
|
257
298
|
const { log, config, cwd } = this.#projectInfo;
|
|
258
|
-
if (!
|
|
259
|
-
log.warn(
|
|
299
|
+
if (!segmentSchema) {
|
|
300
|
+
log.warn(`${formatLoggedSegmentName(segmentName, { upperFirst: true })} schema is null`);
|
|
260
301
|
return;
|
|
261
302
|
}
|
|
262
|
-
log.debug(`Handling received schema from ${formatLoggedSegmentName(
|
|
263
|
-
const
|
|
264
|
-
const segment = this.#segments.find((s) => s.segmentName ===
|
|
303
|
+
log.debug(`Handling received schema from ${formatLoggedSegmentName(segmentName)}`);
|
|
304
|
+
const schemaOutAbsolutePath = path.resolve(cwd, this.#schemaOut ?? config.schemaOutDir);
|
|
305
|
+
const segment = this.#segments.find((s) => s.segmentName === segmentName);
|
|
265
306
|
if (!segment) {
|
|
266
|
-
log.warn(
|
|
307
|
+
log.warn(`${formatLoggedSegmentName(segmentName)} not found`);
|
|
267
308
|
return;
|
|
268
309
|
}
|
|
269
|
-
this.#
|
|
270
|
-
if (
|
|
310
|
+
this.#schemaSegments[segmentName] = segmentSchema;
|
|
311
|
+
if (segmentSchema.emitSchema) {
|
|
271
312
|
const now = Date.now();
|
|
272
|
-
const { diffResult } = await
|
|
273
|
-
|
|
274
|
-
|
|
313
|
+
const { diffResult } = await writeOneSegmentSchemaFile({
|
|
314
|
+
schemaOutAbsolutePath,
|
|
315
|
+
segmentSchema,
|
|
275
316
|
skipIfExists: false,
|
|
276
317
|
});
|
|
277
318
|
const timeTook = Date.now() - now;
|
|
@@ -280,25 +321,27 @@ export class VovkDev {
|
|
|
280
321
|
log.info(`Schema for ${formatLoggedSegmentName(segment.segmentName)} has been updated in ${timeTook}ms`);
|
|
281
322
|
}
|
|
282
323
|
}
|
|
283
|
-
else if (
|
|
324
|
+
else if (segmentSchema && !isEmpty(segmentSchema.controllers)) {
|
|
284
325
|
log.error(`Non-empty schema provided for ${formatLoggedSegmentName(segment.segmentName)} but "emitSchema" is false`);
|
|
285
326
|
}
|
|
286
|
-
if (this.#segments.every((s) => this.#
|
|
327
|
+
if (this.#segments.every((s) => this.#schemaSegments[s.segmentName])) {
|
|
287
328
|
log.debug(`All segments with "emitSchema" have schema.`);
|
|
288
329
|
this.#generate();
|
|
289
330
|
}
|
|
290
331
|
}
|
|
291
332
|
async start({ exit }) {
|
|
292
333
|
const now = Date.now();
|
|
293
|
-
this.#projectInfo = await getProjectInfo();
|
|
294
|
-
const { log, config, cwd,
|
|
334
|
+
this.#projectInfo = await getProjectInfo({ logLevel: this.#logLevel });
|
|
335
|
+
const { log, config, cwd, apiDirAbsolutePath } = this.#projectInfo;
|
|
336
|
+
this.#segments = await locateSegments({ dir: apiDirAbsolutePath, config, log });
|
|
295
337
|
log.info('Starting...');
|
|
296
338
|
if (exit) {
|
|
297
339
|
this.#onFirstTimeGenerate = once(() => {
|
|
298
340
|
log.info('The schemas and the RPC client have been generated. Exiting...');
|
|
299
341
|
});
|
|
300
342
|
}
|
|
301
|
-
|
|
343
|
+
const devHttps = this.#devHttps ?? config.devHttps;
|
|
344
|
+
if (devHttps) {
|
|
302
345
|
const agent = new Agent({
|
|
303
346
|
connect: {
|
|
304
347
|
rejectUnauthorized: false,
|
|
@@ -312,10 +355,10 @@ export class VovkDev {
|
|
|
312
355
|
process.on('unhandledRejection', (reason) => {
|
|
313
356
|
log.error(`Unhandled Rejection: ${String(reason)}`);
|
|
314
357
|
});
|
|
315
|
-
const
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
await
|
|
358
|
+
const schemaOutAbsolutePath = path.resolve(cwd, this.#schemaOut ?? config.schemaOutDir);
|
|
359
|
+
const segmentNames = this.#segments.map((s) => s.segmentName);
|
|
360
|
+
await ensureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, segmentNames);
|
|
361
|
+
await ensureClient(this.#projectInfo, this.#segments);
|
|
319
362
|
const MAX_ATTEMPTS = 5;
|
|
320
363
|
const DELAY = 5000;
|
|
321
364
|
// Request schema every segment in 5 seconds in order to update schema on start
|
|
@@ -334,7 +377,7 @@ export class VovkDev {
|
|
|
334
377
|
void this.#requestSchema(segmentName).then(({ isError: isError2 }) => {
|
|
335
378
|
if (!isError2) {
|
|
336
379
|
clearInterval(interval);
|
|
337
|
-
log.info(`Requested schema for ${formatLoggedSegmentName(segmentName)} after ${attempts} attempts`);
|
|
380
|
+
log.info(`Requested schema for ${formatLoggedSegmentName(segmentName)} after ${attempts} attempt${attempts === 1 ? '' : 's'}`);
|
|
338
381
|
}
|
|
339
382
|
});
|
|
340
383
|
}, DELAY);
|
|
@@ -354,5 +397,11 @@ export class VovkDev {
|
|
|
354
397
|
}
|
|
355
398
|
const env = process.env;
|
|
356
399
|
if (env.__VOVK_START_WATCHER_IN_STANDALONE_MODE__ === 'true') {
|
|
357
|
-
void new VovkDev(
|
|
400
|
+
void new VovkDev({
|
|
401
|
+
schemaOut: env.__VOVK_SCHEMA_OUT_FLAG__ || undefined,
|
|
402
|
+
devHttps: env.__VOVK_DEV_HTTPS_FLAG__ === 'true',
|
|
403
|
+
logLevel: env.__VOVK_LOG_LEVEL__,
|
|
404
|
+
}).start({
|
|
405
|
+
exit: env.__VOVK_EXIT__ === 'true',
|
|
406
|
+
});
|
|
358
407
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { DiffResult } from './
|
|
1
|
+
import type { DiffResult } from './diffSegmentSchema.mjs';
|
|
2
2
|
import type { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
3
|
-
export
|
|
3
|
+
export declare function logDiffResult(segmentName: string, diffResult: DiffResult, projectInfo: ProjectInfo): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
|
|
3
|
-
import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
|
|
4
|
-
export
|
|
2
|
+
import { formatLoggedSegmentName } from '../utils/formatLoggedSegmentName.mjs';
|
|
3
|
+
import { chalkHighlightThing } from '../utils/chalkHighlightThing.mjs';
|
|
4
|
+
export function logDiffResult(segmentName, diffResult, projectInfo) {
|
|
5
5
|
const diffNormalized = [];
|
|
6
6
|
diffResult.controllers.added.forEach((name) => {
|
|
7
7
|
diffNormalized.push({ what: 'controller', type: 'added', name });
|
|
@@ -20,7 +20,7 @@ export default function logDiffResult(segmentName, diffResult, projectInfo) {
|
|
|
20
20
|
diffNormalized.push({ what: 'controllerHandler', type: 'changed', name: `${handler.nameOfClass}.${name}` });
|
|
21
21
|
});
|
|
22
22
|
});
|
|
23
|
-
const LIMIT = diffNormalized.length <
|
|
23
|
+
const LIMIT = diffNormalized.length < 17 ? diffNormalized.length : 15;
|
|
24
24
|
const addedText = chalk.green('added');
|
|
25
25
|
const removedText = chalk.red('removed');
|
|
26
26
|
const changedText = chalk.cyan('changed');
|
|
@@ -29,10 +29,10 @@ export default function logDiffResult(segmentName, diffResult, projectInfo) {
|
|
|
29
29
|
case 'controller':
|
|
30
30
|
switch (diffNormalizedItem.type) {
|
|
31
31
|
case 'added':
|
|
32
|
-
projectInfo.log.info(`Schema for RPC ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
32
|
+
projectInfo.log.info(`Schema for RPC module ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
33
33
|
break;
|
|
34
34
|
case 'removed':
|
|
35
|
-
projectInfo.log.info(`Schema for RPC ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
35
|
+
projectInfo.log.info(`Schema for RPC module ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
36
36
|
break;
|
|
37
37
|
}
|
|
38
38
|
break;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { META_FILE_NAME } from './writeOneSegmentSchemaFile.mjs';
|
|
4
|
+
import { chalkHighlightThing } from '../utils/chalkHighlightThing.mjs';
|
|
5
|
+
import { getMetaSchema } from '../getProjectInfo/getMetaSchema.mjs';
|
|
6
|
+
export async function writeMetaJson(schemaOutAbsolutePath, projectInfo) {
|
|
7
|
+
const metaJsonPath = path.join(schemaOutAbsolutePath, `${META_FILE_NAME}.json`);
|
|
8
|
+
const metaStr = JSON.stringify(getMetaSchema({
|
|
9
|
+
config: projectInfo.config,
|
|
10
|
+
}), null, 2);
|
|
11
|
+
const existingStr = await fs.readFile(metaJsonPath, 'utf-8').catch(() => null);
|
|
12
|
+
if (existingStr !== metaStr) {
|
|
13
|
+
await fs.writeFile(metaJsonPath, metaStr);
|
|
14
|
+
projectInfo?.log.info(`Meta JSON is written to ${chalkHighlightThing(metaJsonPath)}`);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
projectInfo?.log.debug(`Meta JSON is up to date at ${chalkHighlightThing(metaJsonPath)}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { VovkSegmentSchema } from 'vovk/internal';
|
|
2
|
+
import { type DiffResult } from './diffSegmentSchema.mjs';
|
|
3
|
+
export declare const ROOT_SEGMENT_FILE_NAME = "root";
|
|
4
|
+
export declare const META_FILE_NAME = "_meta";
|
|
5
|
+
export declare function writeOneSegmentSchemaFile({ schemaOutAbsolutePath, segmentSchema, skipIfExists, }: {
|
|
6
|
+
schemaOutAbsolutePath: string;
|
|
7
|
+
segmentSchema: VovkSegmentSchema;
|
|
8
|
+
skipIfExists?: boolean;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
isCreated: boolean;
|
|
11
|
+
diffResult: DiffResult | null;
|
|
12
|
+
}>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
|
-
import
|
|
4
|
-
import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
|
|
5
|
-
export const
|
|
6
|
-
export const
|
|
7
|
-
export
|
|
8
|
-
const segmentPath = path.join(
|
|
3
|
+
import { diffSegmentSchema } from './diffSegmentSchema.mjs';
|
|
4
|
+
import { getFileSystemEntryType } from '../utils/getFileSystemEntryType.mjs';
|
|
5
|
+
export const ROOT_SEGMENT_FILE_NAME = 'root';
|
|
6
|
+
export const META_FILE_NAME = '_meta';
|
|
7
|
+
export async function writeOneSegmentSchemaFile({ schemaOutAbsolutePath, segmentSchema, skipIfExists = false, }) {
|
|
8
|
+
const segmentPath = path.join(schemaOutAbsolutePath, `${segmentSchema.segmentName || ROOT_SEGMENT_FILE_NAME}.json`);
|
|
9
9
|
if (skipIfExists && (await getFileSystemEntryType(segmentPath))) {
|
|
10
10
|
try {
|
|
11
11
|
await fs.stat(segmentPath);
|
|
@@ -16,14 +16,17 @@ export default async function writeOneSchemaFile({ schemaJsonOutAbsolutePath, sc
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
await fs.mkdir(path.dirname(segmentPath), { recursive: true });
|
|
19
|
-
const schemaStr = JSON.stringify(
|
|
19
|
+
const schemaStr = JSON.stringify(segmentSchema, null, 2);
|
|
20
20
|
const existing = await fs.readFile(segmentPath, 'utf-8').catch(() => null);
|
|
21
21
|
if (existing === schemaStr) {
|
|
22
22
|
return { isCreated: false, diffResult: null };
|
|
23
23
|
}
|
|
24
24
|
await fs.writeFile(segmentPath, schemaStr);
|
|
25
25
|
if (existing) {
|
|
26
|
-
return {
|
|
26
|
+
return {
|
|
27
|
+
isCreated: false,
|
|
28
|
+
diffResult: diffSegmentSchema(JSON.parse(existing), segmentSchema),
|
|
29
|
+
};
|
|
27
30
|
}
|
|
28
31
|
return { isCreated: true, diffResult: null };
|
|
29
32
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import type { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
path: string;
|
|
5
|
-
}>;
|
|
2
|
+
import type { Segment } from '../utils/locateSegments.mjs';
|
|
3
|
+
export declare function ensureClient(projectInfo: ProjectInfo, locatedSegments: Segment[]): Promise<void>;
|
|
@@ -1,30 +1,28 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// This is a temporary placeholder to avoid compilation errors if client is imported before it's generated.
|
|
15
|
-
// If you still see this text, the client is not generated yet because of an unknown problem.
|
|
16
|
-
// Feel free to report an issue at https://github.com/finom/vovk/issues`;
|
|
17
|
-
for (const { outPath, templateName } of templateFiles) {
|
|
18
|
-
const existing = await fs.readFile(outPath, 'utf-8').catch(() => null);
|
|
19
|
-
if (!existing) {
|
|
20
|
-
await fs.mkdir(clientOutDirAbsolutePath, { recursive: true });
|
|
21
|
-
await fs.writeFile(outPath, outPath.endsWith('.py') ? text.replace(/\/\//g, '#') : text);
|
|
22
|
-
usedTemplateNames.push(templateName);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
usedTemplateNames = uniq(usedTemplateNames);
|
|
26
|
-
if (usedTemplateNames.length) {
|
|
27
|
-
log.info(`Placeholder client files from template${usedTemplateNames.length !== 1 ? 's' : ''} ${chalkHighlightThing(usedTemplateNames.map((s) => `"${s}"`).join(', '))} are generated at ${clientOutDirAbsolutePath} in ${Date.now() - now}ms`);
|
|
1
|
+
import { VovkSchemaIdEnum } from 'vovk/internal';
|
|
2
|
+
import { generate } from './generate.mjs';
|
|
3
|
+
import { getMetaSchema } from '../getProjectInfo/getMetaSchema.mjs';
|
|
4
|
+
const getEmptySegmentRecordSchema = (segmentNames) => {
|
|
5
|
+
const result = {};
|
|
6
|
+
for (const segmentName of segmentNames) {
|
|
7
|
+
result[segmentName] = {
|
|
8
|
+
$schema: VovkSchemaIdEnum.SEGMENT,
|
|
9
|
+
segmentName,
|
|
10
|
+
segmentType: 'segment',
|
|
11
|
+
emitSchema: false,
|
|
12
|
+
controllers: {},
|
|
13
|
+
};
|
|
28
14
|
}
|
|
29
|
-
return
|
|
15
|
+
return result;
|
|
16
|
+
};
|
|
17
|
+
export async function ensureClient(projectInfo, locatedSegments) {
|
|
18
|
+
return generate({
|
|
19
|
+
isEnsuringClient: true,
|
|
20
|
+
projectInfo,
|
|
21
|
+
fullSchema: {
|
|
22
|
+
$schema: VovkSchemaIdEnum.SCHEMA,
|
|
23
|
+
segments: getEmptySegmentRecordSchema(locatedSegments.map(({ segmentName }) => segmentName)),
|
|
24
|
+
meta: getMetaSchema({ config: projectInfo.config }),
|
|
25
|
+
},
|
|
26
|
+
locatedSegments,
|
|
27
|
+
});
|
|
30
28
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { VovkSchema } from 'vovk';
|
|
2
|
+
import type { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
3
|
+
import type { GenerateOptions } from '../types.mjs';
|
|
4
|
+
import type { Segment } from '../utils/locateSegments.mjs';
|
|
5
|
+
export declare function generate({ isEnsuringClient, isBundle, projectInfo, forceNothingWrittenLog, fullSchema, locatedSegments, cliGenerateOptions, }: {
|
|
6
|
+
isEnsuringClient?: boolean;
|
|
7
|
+
isBundle?: boolean;
|
|
8
|
+
projectInfo: ProjectInfo;
|
|
9
|
+
forceNothingWrittenLog?: boolean;
|
|
10
|
+
fullSchema: VovkSchema;
|
|
11
|
+
locatedSegments: Segment[];
|
|
12
|
+
cliGenerateOptions?: GenerateOptions;
|
|
13
|
+
}): Promise<void>;
|