vovk-cli 0.0.1-draft.38 → 0.0.1-draft.381
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 +31 -1
- package/client-templates/cjs/index.cjs.ejs +19 -0
- package/client-templates/cjs/index.d.cts.ejs +22 -0
- package/client-templates/mixins/mixins.d.ts.ejs +64 -0
- package/client-templates/mixins/mixins.json.ejs +1 -0
- package/client-templates/mjs/index.d.mts.ejs +22 -0
- package/client-templates/mjs/index.mjs.ejs +18 -0
- package/client-templates/openapiCjs/openapi.cjs.ejs +4 -0
- package/client-templates/openapiCjs/openapi.d.cts.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/schemaCjs/schema.cjs.ejs +24 -0
- package/client-templates/schemaCjs/schema.d.cts.ejs +10 -0
- package/client-templates/schemaJson/schema.json.ejs +1 -0
- package/client-templates/schemaTs/schema.ts.ejs +28 -0
- package/client-templates/ts/index.ts.ejs +27 -0
- package/dist/bundle/index.d.mts +8 -0
- package/dist/bundle/index.mjs +94 -0
- package/dist/dev/diffSegmentSchema.d.mts +36 -0
- package/dist/dev/{diffSchema.mjs → diffSegmentSchema.mjs} +3 -11
- package/dist/dev/ensureSchemaFiles.d.mts +4 -1
- package/dist/dev/ensureSchemaFiles.mjs +15 -31
- package/dist/dev/index.d.mts +5 -1
- package/dist/dev/index.mjs +141 -65
- package/dist/dev/logDiffResult.d.mts +1 -1
- package/dist/dev/logDiffResult.mjs +6 -43
- package/dist/dev/writeMetaJson.d.mts +2 -0
- package/dist/dev/writeMetaJson.mjs +20 -0
- package/dist/dev/writeOneSegmentSchemaFile.d.mts +12 -0
- package/dist/dev/{writeOneSchemaFile.mjs → writeOneSegmentSchemaFile.mjs} +10 -6
- package/dist/generate/ensureClient.d.mts +3 -0
- package/dist/generate/ensureClient.mjs +28 -0
- package/dist/generate/generate.d.mts +13 -0
- package/dist/generate/generate.mjs +306 -0
- package/dist/generate/getClientTemplateFiles.d.mts +20 -0
- package/dist/generate/getClientTemplateFiles.mjs +85 -0
- package/dist/generate/getProjectFullSchema.d.mts +8 -0
- package/dist/generate/getProjectFullSchema.mjs +66 -0
- package/dist/generate/getTemplateClientImports.d.mts +19 -0
- package/dist/generate/getTemplateClientImports.mjs +49 -0
- package/dist/generate/index.d.mts +33 -0
- package/dist/generate/index.mjs +186 -0
- package/dist/generate/writeOneClientFile.d.mts +42 -0
- package/dist/generate/writeOneClientFile.mjs +139 -0
- package/dist/getProjectInfo/getConfig/getConfigAbsolutePaths.d.mts +5 -0
- package/dist/getProjectInfo/{getConfigAbsolutePaths.mjs → getConfig/getConfigAbsolutePaths.mjs} +4 -1
- package/dist/getProjectInfo/{getRelativeSrcRoot.d.mts → getConfig/getRelativeSrcRoot.d.mts} +1 -1
- package/dist/getProjectInfo/{getRelativeSrcRoot.mjs → getConfig/getRelativeSrcRoot.mjs} +2 -2
- package/dist/getProjectInfo/getConfig/getTemplateDefs.d.mts +24 -0
- package/dist/getProjectInfo/getConfig/getTemplateDefs.mjs +165 -0
- package/dist/getProjectInfo/{getUserConfig.d.mts → getConfig/getUserConfig.d.mts} +3 -2
- package/dist/getProjectInfo/{getUserConfig.mjs → getConfig/getUserConfig.mjs} +3 -3
- package/dist/getProjectInfo/{importUncachedModule.mjs → getConfig/importUncachedModule.mjs} +1 -4
- package/dist/getProjectInfo/getConfig/index.d.mts +91 -0
- package/dist/getProjectInfo/getConfig/index.mjs +92 -0
- package/dist/getProjectInfo/getMetaSchema.d.mts +8 -0
- package/dist/getProjectInfo/getMetaSchema.mjs +27 -0
- package/dist/getProjectInfo/index.d.mts +14 -9
- package/dist/getProjectInfo/index.mjs +24 -22
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +116 -35
- package/dist/init/createConfig.d.mts +2 -2
- package/dist/init/createConfig.mjs +40 -13
- package/dist/init/createStandardSchemaValidatorFile.d.mts +4 -0
- package/dist/init/createStandardSchemaValidatorFile.mjs +52 -0
- package/dist/init/getTemplateFilesFromPackage.mjs +10 -5
- package/dist/init/index.d.mts +2 -2
- package/dist/init/index.mjs +121 -72
- package/dist/init/installDependencies.mjs +4 -2
- package/dist/init/logUpdateDependenciesError.d.mts +3 -1
- package/dist/init/logUpdateDependenciesError.mjs +7 -1
- package/dist/init/updateDependenciesWithoutInstalling.mjs +39 -9
- package/dist/init/updateNPMScripts.mjs +1 -1
- package/dist/init/updateTypeScriptConfig.d.mts +4 -1
- package/dist/init/updateTypeScriptConfig.mjs +11 -7
- package/dist/initProgram.d.mts +1 -1
- package/dist/initProgram.mjs +17 -17
- package/dist/locateSegments.d.mts +8 -1
- package/dist/locateSegments.mjs +13 -3
- package/dist/new/addClassToSegmentCode.d.mts +1 -2
- package/dist/new/addClassToSegmentCode.mjs +3 -3
- package/dist/new/index.d.mts +2 -1
- package/dist/new/index.mjs +5 -3
- package/dist/new/newModule.d.mts +5 -2
- package/dist/new/newModule.mjs +26 -24
- package/dist/new/newSegment.d.mts +4 -1
- package/dist/new/newSegment.mjs +20 -12
- package/dist/new/render.d.mts +7 -3
- package/dist/new/render.mjs +31 -10
- package/dist/types.d.mts +66 -44
- package/dist/utils/compileJSONSchemaToTypeScriptType.d.mts +5 -0
- package/dist/utils/compileJSONSchemaToTypeScriptType.mjs +9 -0
- package/dist/utils/compileTs.d.mts +12 -0
- package/dist/utils/compileTs.mjs +261 -0
- package/dist/utils/debounceWithArgs.d.mts +1 -1
- package/dist/utils/deepExtend.d.mts +54 -0
- package/dist/utils/deepExtend.mjs +129 -0
- package/dist/utils/formatLoggedSegmentName.d.mts +3 -1
- package/dist/utils/formatLoggedSegmentName.mjs +3 -2
- package/dist/utils/generateFnName.d.mts +23 -0
- package/dist/utils/generateFnName.mjs +76 -0
- package/dist/utils/getPackageJson.d.mts +3 -0
- package/dist/utils/getPackageJson.mjs +22 -0
- package/dist/utils/getPublicModuleNameFromPath.d.mts +4 -0
- package/dist/utils/getPublicModuleNameFromPath.mjs +9 -0
- package/dist/utils/normalizeOpenAPIMixin.d.mts +14 -0
- package/dist/utils/normalizeOpenAPIMixin.mjs +114 -0
- package/dist/utils/pickSegmentFullSchema.d.mts +3 -0
- package/dist/utils/pickSegmentFullSchema.mjs +15 -0
- package/dist/utils/removeUnlistedDirectories.d.mts +10 -0
- package/dist/utils/removeUnlistedDirectories.mjs +61 -0
- package/dist/utils/resolveAbsoluteModulePath.d.mts +2 -0
- package/dist/utils/resolveAbsoluteModulePath.mjs +32 -0
- package/module-templates/arktype/controller.ts.ejs +68 -0
- package/module-templates/type/controller.ts.ejs +56 -0
- package/module-templates/type/service.ts.ejs +28 -0
- package/module-templates/valibot/controller.ts.ejs +68 -0
- package/package.json +39 -22
- package/dist/dev/diffSchema.d.mts +0 -43
- package/dist/dev/ensureClient.d.mts +0 -5
- package/dist/dev/ensureClient.mjs +0 -31
- package/dist/dev/isMetadataEmpty.d.mts +0 -2
- package/dist/dev/isMetadataEmpty.mjs +0 -4
- package/dist/dev/writeOneSchemaFile.d.mts +0 -11
- package/dist/generateClient.d.mts +0 -7
- package/dist/generateClient.mjs +0 -97
- package/dist/getProjectInfo/getConfig.d.mts +0 -11
- package/dist/getProjectInfo/getConfig.mjs +0 -29
- package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +0 -4
- package/dist/postinstall.d.mts +0 -1
- package/dist/postinstall.mjs +0 -24
- package/templates/controller.ejs +0 -52
- package/templates/service.ejs +0 -27
- package/templates/worker.ejs +0 -24
- /package/dist/getProjectInfo/{importUncachedModule.d.mts → getConfig/importUncachedModule.d.mts} +0 -0
- /package/dist/getProjectInfo/{importUncachedModuleWorker.d.mts → getConfig/importUncachedModuleWorker.d.mts} +0 -0
- /package/dist/getProjectInfo/{importUncachedModuleWorker.mjs → getConfig/importUncachedModuleWorker.mjs} +0 -0
package/dist/dev/index.mjs
CHANGED
|
@@ -1,32 +1,48 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { VovkSchemaIdEnum } from 'vovk';
|
|
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';
|
|
9
|
+
import once from 'lodash/once.js';
|
|
8
10
|
import isEmpty from 'lodash/isEmpty.js';
|
|
9
|
-
import { debouncedEnsureSchemaFiles } from './ensureSchemaFiles.mjs';
|
|
10
|
-
import
|
|
11
|
+
import ensureSchemaFiles, { debouncedEnsureSchemaFiles } from './ensureSchemaFiles.mjs';
|
|
12
|
+
import writeOneSegmentSchemaFile from './writeOneSegmentSchemaFile.mjs';
|
|
11
13
|
import logDiffResult from './logDiffResult.mjs';
|
|
12
|
-
import ensureClient from '
|
|
14
|
+
import ensureClient from '../generate/ensureClient.mjs';
|
|
13
15
|
import getProjectInfo from '../getProjectInfo/index.mjs';
|
|
14
|
-
import
|
|
15
|
-
import locateSegments from '../locateSegments.mjs';
|
|
16
|
+
import { generate } from '../generate/generate.mjs';
|
|
17
|
+
import { locateSegments } from '../locateSegments.mjs';
|
|
16
18
|
import debounceWithArgs from '../utils/debounceWithArgs.mjs';
|
|
17
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';
|
|
18
23
|
export class VovkDev {
|
|
19
24
|
#projectInfo;
|
|
20
25
|
#segments = [];
|
|
21
|
-
#
|
|
26
|
+
#schemaSegments = {};
|
|
22
27
|
#isWatching = false;
|
|
23
28
|
#modulesWatcher = null;
|
|
24
29
|
#segmentWatcher = null;
|
|
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
|
+
}
|
|
25
39
|
#watchSegments = (callback) => {
|
|
26
40
|
const segmentReg = /\/?\[\[\.\.\.[a-zA-Z-_]+\]\]\/route.ts$/;
|
|
27
|
-
const { cwd, log, config,
|
|
28
|
-
|
|
29
|
-
|
|
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);
|
|
30
46
|
const getSegmentName = (filePath) => path.relative(apiDirAbsolutePath, filePath).replace(segmentReg, '');
|
|
31
47
|
log.debug(`Watching segments at ${apiDirAbsolutePath}`);
|
|
32
48
|
this.#segmentWatcher = chokidar
|
|
@@ -40,7 +56,13 @@ export class VovkDev {
|
|
|
40
56
|
const segmentName = getSegmentName(filePath);
|
|
41
57
|
this.#segments = this.#segments.find((s) => s.segmentName === segmentName)
|
|
42
58
|
? this.#segments
|
|
43
|
-
: [
|
|
59
|
+
: [
|
|
60
|
+
...this.#segments,
|
|
61
|
+
{
|
|
62
|
+
routeFilePath: filePath,
|
|
63
|
+
segmentName,
|
|
64
|
+
},
|
|
65
|
+
];
|
|
44
66
|
log.info(`${capitalize(formatLoggedSegmentName(segmentName))} has been added`);
|
|
45
67
|
log.debug(`Full list of segments: ${this.#segments.map((s) => s.segmentName).join(', ')}`);
|
|
46
68
|
void debouncedEnsureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, this.#segments.map((s) => s.segmentName));
|
|
@@ -54,14 +76,22 @@ export class VovkDev {
|
|
|
54
76
|
})
|
|
55
77
|
.on('addDir', async (dirPath) => {
|
|
56
78
|
log.debug(`Directory ${dirPath} has been added to segments folder`);
|
|
57
|
-
this.#segments = await locateSegments(
|
|
79
|
+
this.#segments = await locateSegments({
|
|
80
|
+
dir: apiDirAbsolutePath,
|
|
81
|
+
config,
|
|
82
|
+
log: this.#projectInfo.log,
|
|
83
|
+
});
|
|
58
84
|
for (const { segmentName } of this.#segments) {
|
|
59
85
|
void this.#requestSchema(segmentName);
|
|
60
86
|
}
|
|
61
87
|
})
|
|
62
88
|
.on('unlinkDir', async (dirPath) => {
|
|
63
89
|
log.debug(`Directory ${dirPath} has been removed from segments folder`);
|
|
64
|
-
this.#segments = await locateSegments(
|
|
90
|
+
this.#segments = await locateSegments({
|
|
91
|
+
dir: apiDirAbsolutePath,
|
|
92
|
+
config,
|
|
93
|
+
log: this.#projectInfo.log,
|
|
94
|
+
});
|
|
65
95
|
for (const { segmentName } of this.#segments) {
|
|
66
96
|
void this.#requestSchema(segmentName);
|
|
67
97
|
}
|
|
@@ -129,24 +159,36 @@ export class VovkDev {
|
|
|
129
159
|
let isInitial = true;
|
|
130
160
|
let isReady = false;
|
|
131
161
|
const handle = debounce(async () => {
|
|
132
|
-
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 });
|
|
133
165
|
await this.#modulesWatcher?.close();
|
|
134
166
|
await this.#segmentWatcher?.close();
|
|
135
167
|
await Promise.all([
|
|
136
168
|
new Promise((resolve) => this.#watchModules(() => resolve(0))),
|
|
137
|
-
new Promise((resolve) => this.#watchSegments(() => resolve(0)))
|
|
169
|
+
new Promise((resolve) => this.#watchSegments(() => resolve(0))),
|
|
138
170
|
]);
|
|
171
|
+
const schemaOutAbsolutePath = path.join(cwd, this.#schemaOut ?? this.#projectInfo.config.schemaOutDir);
|
|
139
172
|
if (isInitial) {
|
|
140
173
|
callback();
|
|
141
174
|
}
|
|
142
175
|
else {
|
|
143
176
|
log.info('Config file has been updated');
|
|
177
|
+
this.#generate();
|
|
144
178
|
}
|
|
179
|
+
await writeMetaJson(schemaOutAbsolutePath, this.#projectInfo);
|
|
145
180
|
isInitial = false;
|
|
146
181
|
}, 1000);
|
|
147
182
|
chokidar
|
|
148
183
|
// .watch(['vovk.config.{js,mjs,cjs}', '.config/vovk.config.{js,mjs,cjs}'], {
|
|
149
|
-
.watch([
|
|
184
|
+
.watch([
|
|
185
|
+
'vovk.config.js',
|
|
186
|
+
'vovk.config.mjs',
|
|
187
|
+
'vovk.config.cjs',
|
|
188
|
+
'.config/vovk.config.js',
|
|
189
|
+
'.config/vovk.config.mjs',
|
|
190
|
+
'.config/vovk.config.cjs',
|
|
191
|
+
], {
|
|
150
192
|
persistent: true,
|
|
151
193
|
cwd,
|
|
152
194
|
ignoreInitial: false,
|
|
@@ -170,7 +212,6 @@ export class VovkDev {
|
|
|
170
212
|
throw new Error('Already watching');
|
|
171
213
|
const { log } = this.#projectInfo;
|
|
172
214
|
log.debug(`Starting segments and modules watcher. Detected initial segments: ${JSON.stringify(this.#segments.map((s) => s.segmentName))}.`);
|
|
173
|
-
await ensureClient(this.#projectInfo);
|
|
174
215
|
// automatically watches segments and modules
|
|
175
216
|
this.#watchConfig(callback);
|
|
176
217
|
}
|
|
@@ -183,21 +224,17 @@ export class VovkDev {
|
|
|
183
224
|
}
|
|
184
225
|
const nameOfClasReg = /\bclass\s+([A-Za-z_]\w*)(?:\s*<[^>]*>)?\s*\{/g;
|
|
185
226
|
const namesOfClasses = [...code.matchAll(nameOfClasReg)].map((match) => match[1]);
|
|
186
|
-
const importRegex = /import\s*{[^}]*\b(get|post|put|del|head|options
|
|
227
|
+
const importRegex = /import\s*{[^}]*\b(get|post|put|del|head|options)\b[^}]*}\s*from\s*['"]vovk['"]/;
|
|
187
228
|
if (importRegex.test(code) && namesOfClasses.length) {
|
|
188
229
|
const affectedSegments = this.#segments.filter((s) => {
|
|
189
|
-
const
|
|
190
|
-
if (!
|
|
230
|
+
const segmentSchema = this.#schemaSegments[s.segmentName];
|
|
231
|
+
if (!segmentSchema)
|
|
191
232
|
return false;
|
|
192
|
-
const controllersByOriginalName = keyBy(
|
|
193
|
-
|
|
194
|
-
return namesOfClasses.some((name) => schema.controllers[name] ||
|
|
195
|
-
schema.workers[name] ||
|
|
196
|
-
controllersByOriginalName[name] ||
|
|
197
|
-
workersByOriginalName[name]);
|
|
233
|
+
const controllersByOriginalName = keyBy(segmentSchema.controllers, 'originalControllerName');
|
|
234
|
+
return namesOfClasses.some((name) => segmentSchema.controllers[name] || controllersByOriginalName[name]);
|
|
198
235
|
});
|
|
199
236
|
if (affectedSegments.length) {
|
|
200
|
-
log.debug(`A file with controller
|
|
237
|
+
log.debug(`A file with controller ${namesOfClasses.join(', ')} have been modified at path "${filePath}". Segment(s) affected: ${JSON.stringify(affectedSegments.map((s) => s.segmentName))}`);
|
|
201
238
|
for (const segment of affectedSegments) {
|
|
202
239
|
await this.#requestSchema(segment.segmentName);
|
|
203
240
|
}
|
|
@@ -207,57 +244,75 @@ export class VovkDev {
|
|
|
207
244
|
}
|
|
208
245
|
}
|
|
209
246
|
else {
|
|
210
|
-
log.debug(`The file does not contain any controller
|
|
247
|
+
log.debug(`The file ${filePath} does not contain any controller`);
|
|
211
248
|
}
|
|
212
249
|
};
|
|
213
250
|
#requestSchema = debounceWithArgs(async (segmentName) => {
|
|
214
|
-
const {
|
|
215
|
-
const
|
|
216
|
-
const endpoint = `${
|
|
251
|
+
const { apiRoot, log, port, config } = this.#projectInfo;
|
|
252
|
+
const devHttps = this.#devHttps ?? config.devHttps;
|
|
253
|
+
const endpoint = `${apiRoot.startsWith(`http${devHttps ? 's' : ''}://`) ? apiRoot : `http${devHttps ? 's' : ''}://localhost:${port}${apiRoot}`}/${segmentName ? `${segmentName}/` : ''}_schema_`;
|
|
217
254
|
log.debug(`Requesting schema for ${formatLoggedSegmentName(segmentName)} at ${endpoint}`);
|
|
218
255
|
try {
|
|
219
256
|
const resp = await fetch(endpoint);
|
|
257
|
+
const text = await resp.text();
|
|
258
|
+
const json = text ? JSON.parse(text) : null;
|
|
220
259
|
if (resp.status !== 200) {
|
|
221
260
|
const probableCause = {
|
|
222
261
|
404: 'The segment did not compile or config.origin is wrong.',
|
|
223
262
|
}[resp.status];
|
|
224
|
-
log.warn(`Schema request to ${formatLoggedSegmentName(segmentName)} failed with status code ${resp.status} but expected 200.${probableCause ? ` Probable cause: ${probableCause}` : ''}
|
|
263
|
+
log.warn(`Schema request to ${chalkHighlightThing(endpoint)} for ${formatLoggedSegmentName(segmentName)} failed with status code ${resp.status} but expected 200.${probableCause ? ` Probable cause: ${probableCause}` : ''}. Response text will be logged below on "debug" level.`);
|
|
264
|
+
log.debug(`Response from ${formatLoggedSegmentName(segmentName)}: ${text}`);
|
|
225
265
|
return { isError: true };
|
|
226
266
|
}
|
|
227
|
-
let
|
|
267
|
+
let segmentSchema = null;
|
|
228
268
|
try {
|
|
229
|
-
({ schema } =
|
|
269
|
+
({ schema: segmentSchema } = json);
|
|
230
270
|
}
|
|
231
271
|
catch (error) {
|
|
232
|
-
log.error(`Error parsing schema for ${formatLoggedSegmentName(segmentName)}: ${error
|
|
272
|
+
log.error(`Error parsing schema for ${formatLoggedSegmentName(segmentName)}: ${error?.message}`);
|
|
233
273
|
}
|
|
234
|
-
await this.#
|
|
274
|
+
await this.#handleSegmentSchema(segmentName, segmentSchema);
|
|
235
275
|
}
|
|
236
276
|
catch (error) {
|
|
237
|
-
log.error(`Error requesting schema for ${formatLoggedSegmentName(segmentName)}: ${error
|
|
277
|
+
log.error(`Error requesting schema for ${formatLoggedSegmentName(segmentName)} at ${endpoint}: ${error?.message}`);
|
|
238
278
|
return { isError: true };
|
|
239
279
|
}
|
|
240
280
|
return { isError: false };
|
|
241
281
|
}, 500);
|
|
242
|
-
|
|
282
|
+
#generate = debounce(() => {
|
|
283
|
+
const fullSchema = {
|
|
284
|
+
$schema: VovkSchemaIdEnum.SCHEMA,
|
|
285
|
+
segments: this.#schemaSegments,
|
|
286
|
+
meta: getMetaSchema({
|
|
287
|
+
config: this.#projectInfo.config,
|
|
288
|
+
useEmitConfig: false,
|
|
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) {
|
|
243
298
|
const { log, config, cwd } = this.#projectInfo;
|
|
244
|
-
if (!
|
|
245
|
-
log.warn(
|
|
299
|
+
if (!segmentSchema) {
|
|
300
|
+
log.warn(`${formatLoggedSegmentName(segmentName, { upperFirst: true })} schema is null`);
|
|
246
301
|
return;
|
|
247
302
|
}
|
|
248
|
-
log.debug(`Handling received schema from ${formatLoggedSegmentName(
|
|
249
|
-
const schemaOutAbsolutePath = path.
|
|
250
|
-
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);
|
|
251
306
|
if (!segment) {
|
|
252
|
-
log.warn(
|
|
307
|
+
log.warn(`${formatLoggedSegmentName(segmentName)} not found`);
|
|
253
308
|
return;
|
|
254
309
|
}
|
|
255
|
-
this.#
|
|
256
|
-
if (
|
|
310
|
+
this.#schemaSegments[segmentName] = segmentSchema;
|
|
311
|
+
if (segmentSchema.emitSchema) {
|
|
257
312
|
const now = Date.now();
|
|
258
|
-
const { diffResult } = await
|
|
313
|
+
const { diffResult } = await writeOneSegmentSchemaFile({
|
|
259
314
|
schemaOutAbsolutePath,
|
|
260
|
-
|
|
315
|
+
segmentSchema,
|
|
261
316
|
skipIfExists: false,
|
|
262
317
|
});
|
|
263
318
|
const timeTook = Date.now() - now;
|
|
@@ -266,19 +321,27 @@ export class VovkDev {
|
|
|
266
321
|
log.info(`Schema for ${formatLoggedSegmentName(segment.segmentName)} has been updated in ${timeTook}ms`);
|
|
267
322
|
}
|
|
268
323
|
}
|
|
269
|
-
else if (
|
|
270
|
-
log.error(`Non-empty schema provided for ${formatLoggedSegmentName(segment.segmentName)} but emitSchema is false`);
|
|
324
|
+
else if (segmentSchema && !isEmpty(segmentSchema.controllers)) {
|
|
325
|
+
log.error(`Non-empty schema provided for ${formatLoggedSegmentName(segment.segmentName)} but "emitSchema" is false`);
|
|
271
326
|
}
|
|
272
|
-
if (this.#segments.every((s) => this.#
|
|
327
|
+
if (this.#segments.every((s) => this.#schemaSegments[s.segmentName])) {
|
|
273
328
|
log.debug(`All segments with "emitSchema" have schema.`);
|
|
274
|
-
|
|
329
|
+
this.#generate();
|
|
275
330
|
}
|
|
276
331
|
}
|
|
277
|
-
async start() {
|
|
332
|
+
async start({ exit }) {
|
|
278
333
|
const now = Date.now();
|
|
279
|
-
this.#projectInfo = await getProjectInfo();
|
|
280
|
-
const { log, config, cwd,
|
|
281
|
-
|
|
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 });
|
|
337
|
+
log.info('Starting...');
|
|
338
|
+
if (exit) {
|
|
339
|
+
this.#onFirstTimeGenerate = once(() => {
|
|
340
|
+
log.info('The schemas and the RPC client have been generated. Exiting...');
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
const devHttps = this.#devHttps ?? config.devHttps;
|
|
344
|
+
if (devHttps) {
|
|
282
345
|
const agent = new Agent({
|
|
283
346
|
connect: {
|
|
284
347
|
rejectUnauthorized: false,
|
|
@@ -292,14 +355,15 @@ export class VovkDev {
|
|
|
292
355
|
process.on('unhandledRejection', (reason) => {
|
|
293
356
|
log.error(`Unhandled Rejection: ${String(reason)}`);
|
|
294
357
|
});
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
this.#
|
|
298
|
-
await
|
|
299
|
-
|
|
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);
|
|
362
|
+
const MAX_ATTEMPTS = 5;
|
|
363
|
+
const DELAY = 5000;
|
|
364
|
+
// Request schema every segment in 5 seconds in order to update schema on start
|
|
300
365
|
setTimeout(() => {
|
|
301
366
|
for (const { segmentName } of this.#segments) {
|
|
302
|
-
const MAX_ATTEMPTS = 3;
|
|
303
367
|
let attempts = 0;
|
|
304
368
|
void this.#requestSchema(segmentName).then(({ isError }) => {
|
|
305
369
|
if (isError) {
|
|
@@ -313,19 +377,31 @@ export class VovkDev {
|
|
|
313
377
|
void this.#requestSchema(segmentName).then(({ isError: isError2 }) => {
|
|
314
378
|
if (!isError2) {
|
|
315
379
|
clearInterval(interval);
|
|
380
|
+
log.info(`Requested schema for ${formatLoggedSegmentName(segmentName)} after ${attempts} attempt${attempts === 1 ? '' : 's'}`);
|
|
316
381
|
}
|
|
317
382
|
});
|
|
318
|
-
},
|
|
383
|
+
}, DELAY);
|
|
319
384
|
}
|
|
320
385
|
});
|
|
321
386
|
}
|
|
387
|
+
}, DELAY);
|
|
388
|
+
if (!exit) {
|
|
322
389
|
this.#watch(() => {
|
|
323
|
-
log.info(`Ready in ${Date.now() - now}ms
|
|
390
|
+
log.info(`Ready in ${Date.now() - now}ms. Making initial requests for schemas in a moment...`);
|
|
324
391
|
});
|
|
325
|
-
}
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
log.info(`Ready in ${Date.now() - now}ms. Making requests for schemas in a moment...`);
|
|
395
|
+
}
|
|
326
396
|
}
|
|
327
397
|
}
|
|
328
398
|
const env = process.env;
|
|
329
399
|
if (env.__VOVK_START_WATCHER_IN_STANDALONE_MODE__ === 'true') {
|
|
330
|
-
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
|
+
});
|
|
331
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
3
|
export default function logDiffResult(segmentName: string, diffResult: DiffResult, projectInfo: ProjectInfo): void;
|
|
@@ -3,23 +3,6 @@ import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
|
|
|
3
3
|
import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
|
|
4
4
|
export default function logDiffResult(segmentName, diffResult, projectInfo) {
|
|
5
5
|
const diffNormalized = [];
|
|
6
|
-
diffResult.workers.added.forEach((name) => {
|
|
7
|
-
diffNormalized.push({ what: 'worker', type: 'added', name });
|
|
8
|
-
});
|
|
9
|
-
diffResult.workers.removed.forEach((name) => {
|
|
10
|
-
diffNormalized.push({ what: 'worker', type: 'removed', name });
|
|
11
|
-
});
|
|
12
|
-
diffResult.workers.handlers.forEach((handler) => {
|
|
13
|
-
handler.added.forEach((name) => {
|
|
14
|
-
diffNormalized.push({ what: 'workerHandler', type: 'added', name: `${handler.nameOfClass}.${name}` });
|
|
15
|
-
});
|
|
16
|
-
handler.removed.forEach((name) => {
|
|
17
|
-
diffNormalized.push({ what: 'workerHandler', type: 'removed', name: `${handler.nameOfClass}.${name}` });
|
|
18
|
-
});
|
|
19
|
-
handler.changed.forEach((name) => {
|
|
20
|
-
diffNormalized.push({ what: 'workerHandler', type: 'changed', name: `${handler.nameOfClass}.${name}` });
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
6
|
diffResult.controllers.added.forEach((name) => {
|
|
24
7
|
diffNormalized.push({ what: 'controller', type: 'added', name });
|
|
25
8
|
});
|
|
@@ -37,52 +20,32 @@ export default function logDiffResult(segmentName, diffResult, projectInfo) {
|
|
|
37
20
|
diffNormalized.push({ what: 'controllerHandler', type: 'changed', name: `${handler.nameOfClass}.${name}` });
|
|
38
21
|
});
|
|
39
22
|
});
|
|
40
|
-
const LIMIT = diffNormalized.length <
|
|
23
|
+
const LIMIT = diffNormalized.length < 17 ? diffNormalized.length : 15;
|
|
41
24
|
const addedText = chalk.green('added');
|
|
42
25
|
const removedText = chalk.red('removed');
|
|
43
26
|
const changedText = chalk.cyan('changed');
|
|
44
27
|
for (const diffNormalizedItem of diffNormalized.slice(0, LIMIT)) {
|
|
45
28
|
switch (diffNormalizedItem.what) {
|
|
46
|
-
case 'worker':
|
|
47
|
-
switch (diffNormalizedItem.type) {
|
|
48
|
-
case 'added':
|
|
49
|
-
projectInfo.log.info(`Schema for worker ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
50
|
-
break;
|
|
51
|
-
case 'removed':
|
|
52
|
-
projectInfo.log.info(`Schema for worker ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
break;
|
|
56
29
|
case 'controller':
|
|
57
30
|
switch (diffNormalizedItem.type) {
|
|
58
31
|
case 'added':
|
|
59
|
-
projectInfo.log.info(`Schema for
|
|
60
|
-
break;
|
|
61
|
-
case 'removed':
|
|
62
|
-
projectInfo.log.info(`Schema for controller ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
break;
|
|
66
|
-
case 'workerHandler':
|
|
67
|
-
switch (diffNormalizedItem.type) {
|
|
68
|
-
case 'added':
|
|
69
|
-
projectInfo.log.info(`Schema for worker method ${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)}`);
|
|
70
33
|
break;
|
|
71
34
|
case 'removed':
|
|
72
|
-
projectInfo.log.info(`Schema for
|
|
35
|
+
projectInfo.log.info(`Schema for RPC module ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
73
36
|
break;
|
|
74
37
|
}
|
|
75
38
|
break;
|
|
76
39
|
case 'controllerHandler':
|
|
77
40
|
switch (diffNormalizedItem.type) {
|
|
78
41
|
case 'added':
|
|
79
|
-
projectInfo.log.info(`Schema for
|
|
42
|
+
projectInfo.log.info(`Schema for RPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
80
43
|
break;
|
|
81
44
|
case 'removed':
|
|
82
|
-
projectInfo.log.info(`Schema for
|
|
45
|
+
projectInfo.log.info(`Schema for RPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
83
46
|
break;
|
|
84
47
|
case 'changed':
|
|
85
|
-
projectInfo.log.info(`Schema for
|
|
48
|
+
projectInfo.log.info(`Schema for RPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${changedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
86
49
|
break;
|
|
87
50
|
}
|
|
88
51
|
break;
|
|
@@ -0,0 +1,20 @@
|
|
|
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 default 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
|
+
useEmitConfig: true,
|
|
11
|
+
}), null, 2);
|
|
12
|
+
const existingStr = await fs.readFile(metaJsonPath, 'utf-8').catch(() => null);
|
|
13
|
+
if (existingStr !== metaStr) {
|
|
14
|
+
await fs.writeFile(metaJsonPath, metaStr);
|
|
15
|
+
projectInfo?.log.info(`Meta JSON is written to ${chalkHighlightThing(metaJsonPath)}`);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
projectInfo?.log.debug(`Meta JSON is up to date at ${chalkHighlightThing(metaJsonPath)}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { VovkSegmentSchema } from 'vovk';
|
|
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 default 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,10 +1,11 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
|
-
import
|
|
3
|
+
import diffSegmentSchema from './diffSegmentSchema.mjs';
|
|
4
4
|
import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
|
|
5
|
-
export const
|
|
6
|
-
export
|
|
7
|
-
|
|
5
|
+
export const ROOT_SEGMENT_FILE_NAME = 'root';
|
|
6
|
+
export const META_FILE_NAME = '_meta';
|
|
7
|
+
export default async function writeOneSegmentSchemaFile({ schemaOutAbsolutePath, segmentSchema, skipIfExists = false, }) {
|
|
8
|
+
const segmentPath = path.join(schemaOutAbsolutePath, `${segmentSchema.segmentName || ROOT_SEGMENT_FILE_NAME}.json`);
|
|
8
9
|
if (skipIfExists && (await getFileSystemEntryType(segmentPath))) {
|
|
9
10
|
try {
|
|
10
11
|
await fs.stat(segmentPath);
|
|
@@ -15,14 +16,17 @@ export default async function writeOneSchemaFile({ schemaOutAbsolutePath, schema
|
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
await fs.mkdir(path.dirname(segmentPath), { recursive: true });
|
|
18
|
-
const schemaStr = JSON.stringify(
|
|
19
|
+
const schemaStr = JSON.stringify(segmentSchema, null, 2);
|
|
19
20
|
const existing = await fs.readFile(segmentPath, 'utf-8').catch(() => null);
|
|
20
21
|
if (existing === schemaStr) {
|
|
21
22
|
return { isCreated: false, diffResult: null };
|
|
22
23
|
}
|
|
23
24
|
await fs.writeFile(segmentPath, schemaStr);
|
|
24
25
|
if (existing) {
|
|
25
|
-
return {
|
|
26
|
+
return {
|
|
27
|
+
isCreated: false,
|
|
28
|
+
diffResult: diffSegmentSchema(JSON.parse(existing), segmentSchema),
|
|
29
|
+
};
|
|
26
30
|
}
|
|
27
31
|
return { isCreated: true, diffResult: null };
|
|
28
32
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { VovkSchemaIdEnum } from 'vovk';
|
|
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
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
};
|
|
17
|
+
export default 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, useEmitConfig: false }),
|
|
25
|
+
},
|
|
26
|
+
locatedSegments,
|
|
27
|
+
});
|
|
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 '../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>;
|