vovk-cli 0.0.1-draft.43 → 0.0.1-draft.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client-templates/compiled/compiled.d.ts.ejs +3 -4
- package/client-templates/compiled/compiled.js.ejs +3 -4
- package/client-templates/ts/index.ts.ejs +3 -4
- package/dist/dev/diffSchema.d.mts +1 -2
- package/dist/dev/ensureSchemaFiles.d.mts +3 -0
- package/dist/dev/ensureSchemaFiles.mjs +4 -1
- package/dist/dev/index.d.mts +3 -1
- package/dist/dev/index.mjs +40 -15
- package/dist/dev/logDiffResult.mjs +9 -9
- package/dist/generate/getClientTemplates.d.mts +1 -1
- package/dist/generate/getClientTemplates.mjs +7 -4
- package/dist/generate/index.mjs +30 -27
- package/dist/getProjectInfo/getConfig.mjs +2 -2
- package/dist/index.mjs +14 -7
- package/dist/init/updateDependenciesWithoutInstalling.mjs +1 -1
- package/dist/locateSegments.d.mts +1 -1
- package/dist/locateSegments.mjs +1 -1
- package/dist/postinstall.mjs +11 -14
- package/dist/types.d.mts +3 -1
- package/package.json +15 -15
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
<%- '// auto-generated\n/* eslint-disable */' %>
|
|
3
|
-
import type {
|
|
4
|
-
import type { promisifyWorker } from 'vovk/worker';
|
|
3
|
+
import type { createRPC, createWPC, VovkClientFetcher } from 'vovk';
|
|
5
4
|
import type fetcher from '<%= fetcherClientImportPath %>';
|
|
6
5
|
<% segments.forEach((segment, i) => {
|
|
7
6
|
const hasWorkers = !!Object.keys(segmentsSchema[segment.segmentName].workers).length;
|
|
@@ -17,9 +16,9 @@ type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
|
|
|
17
16
|
const workers = Object.keys(segSchema.workers);
|
|
18
17
|
%>
|
|
19
18
|
<% controllers.forEach((key) => { %>
|
|
20
|
-
export const <%= key %>: ReturnType<typeof
|
|
19
|
+
export const <%= key %>: ReturnType<typeof createRPC<Controllers<%= i %>["<%= key %>"], Options>>;
|
|
21
20
|
<% }) %>
|
|
22
21
|
<% workers.forEach((key) => { %>
|
|
23
|
-
export const <%= key %>: ReturnType<typeof
|
|
22
|
+
export const <%= key %>: ReturnType<typeof createWPC<Workers<%= i %>["<%= key %>"]>>;
|
|
24
23
|
<% }) %>
|
|
25
24
|
<% }) %>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<%- '// auto-generated\n/* eslint-disable */' %>
|
|
2
|
-
const {
|
|
3
|
-
const { promisifyWorker } = require('vovk/worker');
|
|
2
|
+
const { createRPC, createWPC } = require('vovk');
|
|
4
3
|
const { default: fetcher } = require('<%= fetcherClientImportPath %>');
|
|
5
4
|
const schema = require('<%= schemaOutImportPath %>');
|
|
6
5
|
|
|
@@ -14,13 +13,13 @@ const apiRoot = '<%= apiRoot %>';
|
|
|
14
13
|
const workers = Object.keys(segSchema.workers);
|
|
15
14
|
%>
|
|
16
15
|
<% controllers.forEach((key) => { %>
|
|
17
|
-
exports.<%= key %> =
|
|
16
|
+
exports.<%= key %> = createRPC(
|
|
18
17
|
schema['<%= segment.segmentName %>'].controllers.<%= key %>,
|
|
19
18
|
'<%= segment.segmentName %>',
|
|
20
19
|
{ fetcher, validateOnClient, defaultOptions: { apiRoot } }
|
|
21
20
|
);
|
|
22
21
|
<% }) %>
|
|
23
22
|
<% workers.forEach((key) => { %>
|
|
24
|
-
exports.<%= key %> =
|
|
23
|
+
exports.<%= key %> = createWPC(null, schema['<%= segment.segmentName %>'].workers.<%= key %>);
|
|
25
24
|
<% }) %>
|
|
26
25
|
<% }) %>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<%- '// auto-generated\n/* eslint-disable */' %>
|
|
2
|
-
import {
|
|
3
|
-
<% if(hasWorkers) { %>import { promisifyWorker } from 'vovk/worker';<% } %>
|
|
2
|
+
import { createRPC, createWPC, type VovkClientFetcher } from 'vovk';
|
|
4
3
|
import fetcher from '<%= fetcherClientImportPath %>';
|
|
5
4
|
import schema from '<%= schemaOutImportPath %>';
|
|
6
5
|
<% if (validateOnClientImportPath) { %>
|
|
@@ -19,7 +18,7 @@ const apiRoot = '<%= apiRoot %>';
|
|
|
19
18
|
import type { Controllers as Controllers<%= i %><% if(hasWorkers) { %>, Workers as Workers<%= i %> <% } %>} from "<%= segment.segmentImportPath %>";
|
|
20
19
|
|
|
21
20
|
<% Object.keys(segSchema.controllers).forEach((key) => { %>
|
|
22
|
-
export const <%= key %> =
|
|
21
|
+
export const <%= key %> = createRPC<Controllers<%= i %>["<%= key %>"], Options>(
|
|
23
22
|
schema['<%= segment.segmentName %>'].controllers.<%= key %>,
|
|
24
23
|
'<%= segment.segmentName %>',
|
|
25
24
|
{ fetcher, validateOnClient, defaultOptions: { apiRoot } }
|
|
@@ -27,7 +26,7 @@ export const <%= key %> = clientizeController<Controllers<%= i %>["<%= key %>"],
|
|
|
27
26
|
<% }) %>
|
|
28
27
|
|
|
29
28
|
<% Object.keys(segSchema.workers).forEach((key) => { %>
|
|
30
|
-
export const <%= key %> =
|
|
29
|
+
export const <%= key %> = createWPC<Workers<%= i %>["<%= key %>"]>(
|
|
31
30
|
null,
|
|
32
31
|
schema['<%= segment.segmentName %>'].workers.<%= key %>
|
|
33
32
|
);
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
2
|
+
/**
|
|
3
|
+
* Ensure that the schema files are created to avoid any import errors.
|
|
4
|
+
*/
|
|
2
5
|
export default function ensureSchemaFiles(projectInfo: ProjectInfo | null, schemaOutAbsolutePath: string, segmentNames: string[]): Promise<void>;
|
|
3
6
|
export declare const debouncedEnsureSchemaFiles: import("lodash").DebouncedFunc<typeof ensureSchemaFiles>;
|
|
@@ -3,6 +3,9 @@ import path from 'node:path';
|
|
|
3
3
|
import debounce from 'lodash/debounce.js';
|
|
4
4
|
import writeOneSchemaFile, { ROOT_SEGMENT_SCHEMA_NAME } from './writeOneSchemaFile.mjs';
|
|
5
5
|
import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
|
|
6
|
+
/**
|
|
7
|
+
* Ensure that the schema files are created to avoid any import errors.
|
|
8
|
+
*/
|
|
6
9
|
export default async function ensureSchemaFiles(projectInfo, schemaOutAbsolutePath, segmentNames) {
|
|
7
10
|
const now = Date.now();
|
|
8
11
|
let hasChanged = false;
|
|
@@ -78,6 +81,6 @@ export default segmentSchema;`;
|
|
|
78
81
|
// Start the recursive deletion from the root directory
|
|
79
82
|
await deleteUnnecessaryJsonFiles(schemaOutAbsolutePath);
|
|
80
83
|
if (hasChanged)
|
|
81
|
-
projectInfo?.log.info(`
|
|
84
|
+
projectInfo?.log.info(`Created empty schema files in ${Date.now() - now}ms`);
|
|
82
85
|
}
|
|
83
86
|
export const debouncedEnsureSchemaFiles = debounce(ensureSchemaFiles, 1000);
|
package/dist/dev/index.d.mts
CHANGED
package/dist/dev/index.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import keyBy from 'lodash/keyBy.js';
|
|
|
6
6
|
import capitalize from 'lodash/capitalize.js';
|
|
7
7
|
import debounce from 'lodash/debounce.js';
|
|
8
8
|
import isEmpty from 'lodash/isEmpty.js';
|
|
9
|
+
import once from 'lodash/once.js';
|
|
9
10
|
import { debouncedEnsureSchemaFiles } from './ensureSchemaFiles.mjs';
|
|
10
11
|
import writeOneSchemaFile from './writeOneSchemaFile.mjs';
|
|
11
12
|
import logDiffResult from './logDiffResult.mjs';
|
|
@@ -22,6 +23,7 @@ export class VovkDev {
|
|
|
22
23
|
#isWatching = false;
|
|
23
24
|
#modulesWatcher = null;
|
|
24
25
|
#segmentWatcher = null;
|
|
26
|
+
#onFirstTimeGenerate = null;
|
|
25
27
|
#watchSegments = (callback) => {
|
|
26
28
|
const segmentReg = /\/?\[\[\.\.\.[a-zA-Z-_]+\]\]\/route.ts$/;
|
|
27
29
|
const { cwd, log, config, apiDir } = this.#projectInfo;
|
|
@@ -40,11 +42,14 @@ export class VovkDev {
|
|
|
40
42
|
const segmentName = getSegmentName(filePath);
|
|
41
43
|
this.#segments = this.#segments.find((s) => s.segmentName === segmentName)
|
|
42
44
|
? this.#segments
|
|
43
|
-
: [
|
|
45
|
+
: [
|
|
46
|
+
...this.#segments,
|
|
47
|
+
{
|
|
44
48
|
routeFilePath: filePath,
|
|
45
49
|
segmentName,
|
|
46
|
-
segmentImportPath: path.relative(config.clientOutDir, filePath) // TODO DRY locateSegments
|
|
47
|
-
}
|
|
50
|
+
segmentImportPath: path.relative(config.clientOutDir, filePath), // TODO DRY locateSegments
|
|
51
|
+
},
|
|
52
|
+
];
|
|
48
53
|
log.info(`${capitalize(formatLoggedSegmentName(segmentName))} has been added`);
|
|
49
54
|
log.debug(`Full list of segments: ${this.#segments.map((s) => s.segmentName).join(', ')}`);
|
|
50
55
|
void debouncedEnsureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, this.#segments.map((s) => s.segmentName));
|
|
@@ -138,7 +143,7 @@ export class VovkDev {
|
|
|
138
143
|
await this.#segmentWatcher?.close();
|
|
139
144
|
await Promise.all([
|
|
140
145
|
new Promise((resolve) => this.#watchModules(() => resolve(0))),
|
|
141
|
-
new Promise((resolve) => this.#watchSegments(() => resolve(0)))
|
|
146
|
+
new Promise((resolve) => this.#watchSegments(() => resolve(0))),
|
|
142
147
|
]);
|
|
143
148
|
if (isInitial) {
|
|
144
149
|
callback();
|
|
@@ -150,7 +155,14 @@ export class VovkDev {
|
|
|
150
155
|
}, 1000);
|
|
151
156
|
chokidar
|
|
152
157
|
// .watch(['vovk.config.{js,mjs,cjs}', '.config/vovk.config.{js,mjs,cjs}'], {
|
|
153
|
-
.watch([
|
|
158
|
+
.watch([
|
|
159
|
+
'vovk.config.js',
|
|
160
|
+
'vovk.config.mjs',
|
|
161
|
+
'vovk.config.cjs',
|
|
162
|
+
'.config/vovk.config.js',
|
|
163
|
+
'.config/vovk.config.mjs',
|
|
164
|
+
'.config/vovk.config.cjs',
|
|
165
|
+
], {
|
|
154
166
|
persistent: true,
|
|
155
167
|
cwd,
|
|
156
168
|
ignoreInitial: false,
|
|
@@ -238,11 +250,12 @@ export class VovkDev {
|
|
|
238
250
|
await this.#handleSchema(schema);
|
|
239
251
|
}
|
|
240
252
|
catch (error) {
|
|
241
|
-
log.error(`Error requesting schema for ${formatLoggedSegmentName(segmentName)}: ${error.message}`);
|
|
253
|
+
log.error(`Error requesting schema for ${formatLoggedSegmentName(segmentName)} at ${endpoint}: ${error.message}`);
|
|
242
254
|
return { isError: true };
|
|
243
255
|
}
|
|
244
256
|
return { isError: false };
|
|
245
257
|
}, 500);
|
|
258
|
+
#generate = debounce(() => generate({ projectInfo: this.#projectInfo, segments: this.#segments, segmentsSchema: this.#schemas }).then(this.#onFirstTimeGenerate), 1000);
|
|
246
259
|
async #handleSchema(schema) {
|
|
247
260
|
const { log, config, cwd } = this.#projectInfo;
|
|
248
261
|
if (!schema) {
|
|
@@ -275,14 +288,19 @@ export class VovkDev {
|
|
|
275
288
|
}
|
|
276
289
|
if (this.#segments.every((s) => this.#schemas[s.segmentName])) {
|
|
277
290
|
log.debug(`All segments with "emitSchema" have schema.`);
|
|
278
|
-
|
|
291
|
+
this.#generate();
|
|
279
292
|
}
|
|
280
293
|
}
|
|
281
|
-
async start() {
|
|
294
|
+
async start({ exit }) {
|
|
282
295
|
const now = Date.now();
|
|
283
296
|
this.#projectInfo = await getProjectInfo();
|
|
284
297
|
const { log, config, cwd, apiDir } = this.#projectInfo;
|
|
285
298
|
log.info('Starting...');
|
|
299
|
+
if (exit) {
|
|
300
|
+
this.#onFirstTimeGenerate = once(() => {
|
|
301
|
+
log.info('The schemas and the RPC client have been generated. Exiting...');
|
|
302
|
+
});
|
|
303
|
+
}
|
|
286
304
|
if (config.devHttps) {
|
|
287
305
|
const agent = new Agent({
|
|
288
306
|
connect: {
|
|
@@ -301,10 +319,11 @@ export class VovkDev {
|
|
|
301
319
|
const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
|
|
302
320
|
this.#segments = await locateSegments({ dir: apiDirAbsolutePath, config });
|
|
303
321
|
await debouncedEnsureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, this.#segments.map((s) => s.segmentName));
|
|
322
|
+
const MAX_ATTEMPTS = 5;
|
|
323
|
+
const DELAY = 5000;
|
|
304
324
|
// Request schema every segment in 5 seconds in order to update schema on start
|
|
305
325
|
setTimeout(() => {
|
|
306
326
|
for (const { segmentName } of this.#segments) {
|
|
307
|
-
const MAX_ATTEMPTS = 3;
|
|
308
327
|
let attempts = 0;
|
|
309
328
|
void this.#requestSchema(segmentName).then(({ isError }) => {
|
|
310
329
|
if (isError) {
|
|
@@ -318,19 +337,25 @@ export class VovkDev {
|
|
|
318
337
|
void this.#requestSchema(segmentName).then(({ isError: isError2 }) => {
|
|
319
338
|
if (!isError2) {
|
|
320
339
|
clearInterval(interval);
|
|
340
|
+
log.info(`Requested schema for ${formatLoggedSegmentName(segmentName)} after ${attempts} attempts`);
|
|
321
341
|
}
|
|
322
342
|
});
|
|
323
|
-
},
|
|
343
|
+
}, DELAY);
|
|
324
344
|
}
|
|
325
345
|
});
|
|
326
346
|
}
|
|
327
|
-
},
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
347
|
+
}, DELAY);
|
|
348
|
+
if (!exit) {
|
|
349
|
+
this.#watch(() => {
|
|
350
|
+
log.info(`Ready in ${Date.now() - now}ms. Making initial requests for schemas in a moment...`);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
log.info(`Ready in ${Date.now() - now}ms. Making initial requests for schemas in a moment...`);
|
|
355
|
+
}
|
|
331
356
|
}
|
|
332
357
|
}
|
|
333
358
|
const env = process.env;
|
|
334
359
|
if (env.__VOVK_START_WATCHER_IN_STANDALONE_MODE__ === 'true') {
|
|
335
|
-
void new VovkDev().start();
|
|
360
|
+
void new VovkDev().start({ exit: env.__VOVK_EXIT__ === 'true' });
|
|
336
361
|
}
|
|
@@ -46,43 +46,43 @@ export default function logDiffResult(segmentName, diffResult, projectInfo) {
|
|
|
46
46
|
case 'worker':
|
|
47
47
|
switch (diffNormalizedItem.type) {
|
|
48
48
|
case 'added':
|
|
49
|
-
projectInfo.log.info(`Schema for
|
|
49
|
+
projectInfo.log.info(`Schema for WPC ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
50
50
|
break;
|
|
51
51
|
case 'removed':
|
|
52
|
-
projectInfo.log.info(`Schema for
|
|
52
|
+
projectInfo.log.info(`Schema for WPC ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
53
53
|
break;
|
|
54
54
|
}
|
|
55
55
|
break;
|
|
56
56
|
case 'controller':
|
|
57
57
|
switch (diffNormalizedItem.type) {
|
|
58
58
|
case 'added':
|
|
59
|
-
projectInfo.log.info(`Schema
|
|
59
|
+
projectInfo.log.info(`Schema forn RPC ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
60
60
|
break;
|
|
61
61
|
case 'removed':
|
|
62
|
-
projectInfo.log.info(`Schema
|
|
62
|
+
projectInfo.log.info(`Schema forn RPC ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
63
63
|
break;
|
|
64
64
|
}
|
|
65
65
|
break;
|
|
66
66
|
case 'workerHandler':
|
|
67
67
|
switch (diffNormalizedItem.type) {
|
|
68
68
|
case 'added':
|
|
69
|
-
projectInfo.log.info(`Schema for
|
|
69
|
+
projectInfo.log.info(`Schema for WPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
70
70
|
break;
|
|
71
71
|
case 'removed':
|
|
72
|
-
projectInfo.log.info(`Schema for
|
|
72
|
+
projectInfo.log.info(`Schema for WPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
73
73
|
break;
|
|
74
74
|
}
|
|
75
75
|
break;
|
|
76
76
|
case 'controllerHandler':
|
|
77
77
|
switch (diffNormalizedItem.type) {
|
|
78
78
|
case 'added':
|
|
79
|
-
projectInfo.log.info(`Schema
|
|
79
|
+
projectInfo.log.info(`Schema forn RPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
80
80
|
break;
|
|
81
81
|
case 'removed':
|
|
82
|
-
projectInfo.log.info(`Schema
|
|
82
|
+
projectInfo.log.info(`Schema forn RPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
83
83
|
break;
|
|
84
84
|
case 'changed':
|
|
85
|
-
projectInfo.log.info(`Schema
|
|
85
|
+
projectInfo.log.info(`Schema forn RPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${changedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
86
86
|
break;
|
|
87
87
|
}
|
|
88
88
|
break;
|
|
@@ -12,14 +12,17 @@ export default function getClientTemplates({ config, cwd, templateNames = [], })
|
|
|
12
12
|
compiled: ['compiled.js.ejs', 'compiled.d.ts.ejs'].map(mapper('compiled')),
|
|
13
13
|
python: ['__init__.py'].map(mapper('python')),
|
|
14
14
|
};
|
|
15
|
-
const templateFiles = (templateNames ?? config.
|
|
15
|
+
const templateFiles = (templateNames ?? config.experimental_clientGenerateTemplateNames).reduce((acc, template) => {
|
|
16
16
|
if (template in builtInTemplatesMap) {
|
|
17
17
|
return [...acc, ...builtInTemplatesMap[template]];
|
|
18
18
|
}
|
|
19
|
-
return [
|
|
19
|
+
return [
|
|
20
|
+
...acc,
|
|
21
|
+
{
|
|
20
22
|
templatePath: path.resolve(cwd, template),
|
|
21
|
-
outPath: path.join(clientOutDirAbsolutePath, path.basename(template).replace('.ejs', ''))
|
|
22
|
-
}
|
|
23
|
+
outPath: path.join(clientOutDirAbsolutePath, path.basename(template).replace('.ejs', '')),
|
|
24
|
+
},
|
|
25
|
+
];
|
|
23
26
|
}, []);
|
|
24
27
|
return templateFiles;
|
|
25
28
|
}
|
package/dist/generate/index.mjs
CHANGED
|
@@ -5,9 +5,9 @@ import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
|
|
|
5
5
|
import prettify from '../utils/prettify.mjs';
|
|
6
6
|
import getClientTemplates from './getClientTemplates.mjs';
|
|
7
7
|
export default async function generate({ projectInfo, segments, segmentsSchema, templates, prettify: prettifyClient, fullSchema, }) {
|
|
8
|
-
templates = templates ?? projectInfo.config.
|
|
8
|
+
templates = templates ?? projectInfo.config.experimental_clientGenerateTemplateNames;
|
|
9
9
|
const noClient = templates?.[0] === 'none';
|
|
10
|
-
const { config, cwd, log, validateOnClientImportPath, apiRoot, fetcherClientImportPath, schemaOutImportPath
|
|
10
|
+
const { config, cwd, log, validateOnClientImportPath, apiRoot, fetcherClientImportPath, schemaOutImportPath } = projectInfo;
|
|
11
11
|
const clientOutDirAbsolutePath = path.resolve(cwd, config.clientOutDir);
|
|
12
12
|
const templateFiles = getClientTemplates({ config, cwd, templateNames: templates });
|
|
13
13
|
// Ensure that each segment has a matching schema if it needs to be emitted:
|
|
@@ -30,40 +30,43 @@ export default async function generate({ projectInfo, segments, segmentsSchema,
|
|
|
30
30
|
segments,
|
|
31
31
|
segmentsSchema,
|
|
32
32
|
};
|
|
33
|
-
//
|
|
34
|
-
const processedTemplates = noClient
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
33
|
+
// Process each template in parallel
|
|
34
|
+
const processedTemplates = noClient
|
|
35
|
+
? []
|
|
36
|
+
: await Promise.all(templateFiles.map(async ({ templatePath, outPath }) => {
|
|
37
|
+
// Read the EJS template
|
|
38
|
+
const templateContent = await fs.readFile(templatePath, 'utf-8');
|
|
39
|
+
// Render the template
|
|
40
|
+
let rendered = templatePath.endsWith('.ejs') ? ejs.render(templateContent, ejsData) : templateContent;
|
|
41
|
+
// Optionally prettify
|
|
42
|
+
if (prettifyClient || config.prettifyClient) {
|
|
43
|
+
rendered = await prettify(rendered, outPath);
|
|
44
|
+
}
|
|
45
|
+
// Read existing file content to compare
|
|
46
|
+
const existingContent = await fs.readFile(outPath, 'utf-8').catch(() => '');
|
|
47
|
+
// Determine if we need to rewrite the file
|
|
48
|
+
const needsWriting = existingContent !== rendered;
|
|
49
|
+
return {
|
|
50
|
+
outPath,
|
|
51
|
+
rendered,
|
|
52
|
+
needsWriting,
|
|
53
|
+
};
|
|
54
|
+
}));
|
|
55
|
+
const anyNeedsWriting = processedTemplates.some(({ needsWriting }) => needsWriting);
|
|
56
|
+
if (fullSchema || anyNeedsWriting) {
|
|
57
|
+
// Make sure the output directory exists
|
|
58
|
+
await fs.mkdir(clientOutDirAbsolutePath, { recursive: true });
|
|
59
|
+
}
|
|
53
60
|
if (fullSchema) {
|
|
54
61
|
const fullSchemaOutAbsolutePath = path.resolve(clientOutDirAbsolutePath, typeof fullSchema === 'string' ? fullSchema : 'full-schema.json');
|
|
55
62
|
await fs.writeFile(fullSchemaOutAbsolutePath, JSON.stringify(segmentsSchema, null, 2));
|
|
56
63
|
log.info(`Full schema has ben written to ${fullSchemaOutAbsolutePath}`);
|
|
57
64
|
}
|
|
58
|
-
// 2. Check if any file needs rewriting
|
|
59
|
-
const anyNeedsWriting = processedTemplates.some(({ needsWriting }) => needsWriting);
|
|
60
65
|
if (!anyNeedsWriting) {
|
|
61
66
|
log.debug(`Client is up to date and doesn't need to be regenerated (${Date.now() - now}ms)`);
|
|
62
67
|
return { written: false, path: clientOutDirAbsolutePath };
|
|
63
68
|
}
|
|
64
|
-
//
|
|
65
|
-
await fs.mkdir(clientOutDirAbsolutePath, { recursive: true });
|
|
66
|
-
// 4. Write updated files where needed
|
|
69
|
+
// Write updated files where needed
|
|
67
70
|
await Promise.all(processedTemplates.map(({ outPath, rendered, needsWriting }) => {
|
|
68
71
|
if (needsWriting) {
|
|
69
72
|
return fs.writeFile(outPath, rendered);
|
|
@@ -9,7 +9,7 @@ export default async function getConfig({ clientOutDir, cwd }) {
|
|
|
9
9
|
modulesDir: env.VOVK_MODULES_DIR ?? conf.modulesDir ?? './' + [srcRoot, 'modules'].filter(Boolean).join('/'),
|
|
10
10
|
validateOnClient: env.VOVK_VALIDATE_ON_CLIENT ?? conf.validateOnClient ?? null,
|
|
11
11
|
validationLibrary: env.VOVK_VALIDATION_LIBRARY ?? conf.validationLibrary ?? null,
|
|
12
|
-
fetcher: env.VOVK_FETCHER ?? conf.fetcher ?? 'vovk/client/defaultFetcher',
|
|
12
|
+
fetcher: env.VOVK_FETCHER ?? conf.fetcher ?? 'vovk/dist/client/defaultFetcher',
|
|
13
13
|
schemaOutDir: env.VOVK_SCHEMA_OUT_DIR ?? conf.schemaOutDir ?? './.vovk-schema',
|
|
14
14
|
clientOutDir: clientOutDir ?? env.VOVK_CLIENT_OUT_DIR ?? conf.clientOutDir ?? './node_modules/.vovk-client',
|
|
15
15
|
origin: (env.VOVK_ORIGIN ?? conf.origin ?? '').replace(/\/$/, ''), // Remove trailing slash
|
|
@@ -18,7 +18,7 @@ export default async function getConfig({ clientOutDir, cwd }) {
|
|
|
18
18
|
logLevel: env.VOVK_LOG_LEVEL ?? conf.logLevel ?? 'info',
|
|
19
19
|
prettifyClient: (env.VOVK_PRETTIFY_CLIENT ? !!env.VOVK_PRETTIFY_CLIENT : null) ?? conf.prettifyClient ?? false,
|
|
20
20
|
devHttps: (env.VOVK_DEV_HTTPS ? !!env.VOVK_DEV_HTTPS : null) ?? conf.devHttps ?? false,
|
|
21
|
-
|
|
21
|
+
experimental_clientGenerateTemplateNames: conf.experimental_clientGenerateTemplateNames ?? ['ts', 'compiled'],
|
|
22
22
|
templates: {
|
|
23
23
|
service: 'vovk-cli/templates/service.ejs',
|
|
24
24
|
controller: 'vovk-cli/templates/controller.ejs',
|
package/dist/index.mjs
CHANGED
|
@@ -19,11 +19,13 @@ initProgram(program.command('init'));
|
|
|
19
19
|
program
|
|
20
20
|
.command('dev')
|
|
21
21
|
.description('Start schema watcher (optional flag --next-dev to start it with Next.js)')
|
|
22
|
-
.option('--next-dev', 'Start schema watcher and Next.js with automatic port allocation'
|
|
22
|
+
.option('--next-dev', 'Start schema watcher and Next.js with automatic port allocation')
|
|
23
|
+
.option('--exit', 'Kill the processe when schema and client is generated')
|
|
23
24
|
.allowUnknownOption(true)
|
|
24
25
|
.action(async (options, command) => {
|
|
26
|
+
const { nextDev, exit = false } = options;
|
|
25
27
|
const portAttempts = 30;
|
|
26
|
-
const PORT = !
|
|
28
|
+
const PORT = !nextDev
|
|
27
29
|
? process.env.PORT
|
|
28
30
|
: process.env.PORT ||
|
|
29
31
|
(await getAvailablePort(3000, portAttempts, 0, (failedPort, tryingPort) =>
|
|
@@ -34,7 +36,7 @@ program
|
|
|
34
36
|
if (!PORT) {
|
|
35
37
|
throw new Error('🐺 ❌ PORT env variable is required');
|
|
36
38
|
}
|
|
37
|
-
if (
|
|
39
|
+
if (nextDev) {
|
|
38
40
|
const { result } = concurrently([
|
|
39
41
|
{
|
|
40
42
|
command: `npx next dev ${command.args.join(' ')}`,
|
|
@@ -43,12 +45,17 @@ program
|
|
|
43
45
|
},
|
|
44
46
|
{
|
|
45
47
|
command: `node ${import.meta.dirname}/dev/index.mjs`,
|
|
46
|
-
name: 'Vovk Dev
|
|
47
|
-
env: {
|
|
48
|
+
name: 'Vovk Dev Watcher',
|
|
49
|
+
env: {
|
|
50
|
+
PORT,
|
|
51
|
+
__VOVK_START_WATCHER_IN_STANDALONE_MODE__: 'true',
|
|
52
|
+
__VOVK_EXIT__: exit ? 'true' : 'false',
|
|
53
|
+
},
|
|
48
54
|
},
|
|
49
55
|
], {
|
|
50
56
|
killOthers: ['failure', 'success'],
|
|
51
57
|
prefix: 'none',
|
|
58
|
+
successCondition: 'first',
|
|
52
59
|
});
|
|
53
60
|
try {
|
|
54
61
|
await result;
|
|
@@ -58,7 +65,7 @@ program
|
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
else {
|
|
61
|
-
void new VovkDev().start();
|
|
68
|
+
void new VovkDev().start({ exit });
|
|
62
69
|
}
|
|
63
70
|
});
|
|
64
71
|
program
|
|
@@ -76,7 +83,7 @@ program
|
|
|
76
83
|
const segments = await locateSegments({ dir: apiDir, config });
|
|
77
84
|
const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
|
|
78
85
|
const schemaImportUrl = pathToFileURL(path.join(schemaOutAbsolutePath, 'index.js')).href;
|
|
79
|
-
const { default: segmentsSchema } = await import(schemaImportUrl);
|
|
86
|
+
const { default: segmentsSchema } = (await import(schemaImportUrl));
|
|
80
87
|
await generate({ projectInfo, segments, segmentsSchema, templates, prettify, fullSchema });
|
|
81
88
|
});
|
|
82
89
|
program
|
|
@@ -5,7 +5,7 @@ import getNPMPackageMetadata from '../utils/getNPMPackageMetadata.mjs';
|
|
|
5
5
|
async function updateDeps({ packageJson, packageNames, channel, key, }) {
|
|
6
6
|
return Promise.all(packageNames.map(async (packageName) => {
|
|
7
7
|
const metadata = await getNPMPackageMetadata(packageName);
|
|
8
|
-
const isVovk = packageName.startsWith('vovk');
|
|
8
|
+
const isVovk = packageName.startsWith('vovk') && packageName !== 'vovk-mapped-types';
|
|
9
9
|
const latestVersion = metadata['dist-tags'][isVovk ? (channel ?? 'latest') : 'latest'];
|
|
10
10
|
if (!packageJson[key]) {
|
|
11
11
|
packageJson[key] = {};
|
|
@@ -4,7 +4,7 @@ export type Segment = {
|
|
|
4
4
|
segmentName: string;
|
|
5
5
|
segmentImportPath: string;
|
|
6
6
|
};
|
|
7
|
-
export default function locateSegments({ dir, rootDir, config }: {
|
|
7
|
+
export default function locateSegments({ dir, rootDir, config, }: {
|
|
8
8
|
dir: string;
|
|
9
9
|
rootDir?: string;
|
|
10
10
|
config: Required<VovkConfig> | null;
|
package/dist/locateSegments.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import getFileSystemEntryType from './utils/getFileSystemEntryType.mjs';
|
|
4
4
|
// config: null is used for testing
|
|
5
|
-
export default async function locateSegments({ dir, rootDir, config }) {
|
|
5
|
+
export default async function locateSegments({ dir, rootDir, config, }) {
|
|
6
6
|
let results = [];
|
|
7
7
|
rootDir = rootDir ?? dir;
|
|
8
8
|
// Read the contents of the directory
|
package/dist/postinstall.mjs
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
* Checks if a file exists at the given path.
|
|
5
|
-
* @param {string} filePath - The path to the file.
|
|
6
|
-
* @returns {Promise<boolean>} - A promise that resolves to true if the file exists, false otherwise.
|
|
7
|
-
*/
|
|
8
|
-
const getFileSystemEntryType = async (filePath) => !!(await fs.stat(filePath).catch(() => false));
|
|
3
|
+
import getFileSystemEntryType from './utils/getFileSystemEntryType.mjs';
|
|
9
4
|
async function postinstall() {
|
|
5
|
+
// TODO: The function doesn't consider client templates, how to do that?
|
|
10
6
|
const vovk = path.join(import.meta.dirname, '../../.vovk-client');
|
|
11
7
|
const js = path.join(vovk, 'compiled.js');
|
|
12
8
|
const ts = path.join(vovk, 'compiled.d.ts');
|
|
13
9
|
const index = path.join(vovk, 'index.ts');
|
|
14
|
-
if ((await getFileSystemEntryType(js)) ||
|
|
15
|
-
(await getFileSystemEntryType(ts)) ||
|
|
16
|
-
(await getFileSystemEntryType(index))) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
10
|
await fs.mkdir(vovk, { recursive: true });
|
|
20
|
-
await
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
if (!(await getFileSystemEntryType(js))) {
|
|
12
|
+
await fs.writeFile(js, '/* postinstall */');
|
|
13
|
+
}
|
|
14
|
+
if (!(await getFileSystemEntryType(ts))) {
|
|
15
|
+
await fs.writeFile(ts, '/* postinstall */');
|
|
16
|
+
}
|
|
17
|
+
if (!(await getFileSystemEntryType(index))) {
|
|
18
|
+
await fs.writeFile(index, '/* postinstall */');
|
|
19
|
+
}
|
|
23
20
|
}
|
|
24
21
|
void postinstall();
|
package/dist/types.d.mts
CHANGED
|
@@ -16,6 +16,7 @@ export type VovkEnv = {
|
|
|
16
16
|
VOVK_PRETTIFY_CLIENT?: string;
|
|
17
17
|
VOVK_DEV_HTTPS?: string;
|
|
18
18
|
__VOVK_START_WATCHER_IN_STANDALONE_MODE__?: 'true';
|
|
19
|
+
__VOVK_EXIT__?: 'true' | 'false';
|
|
19
20
|
};
|
|
20
21
|
export type VovkConfig = {
|
|
21
22
|
clientOutDir?: string;
|
|
@@ -30,7 +31,7 @@ export type VovkConfig = {
|
|
|
30
31
|
logLevel?: LogLevelNames;
|
|
31
32
|
prettifyClient?: boolean;
|
|
32
33
|
devHttps?: boolean;
|
|
33
|
-
|
|
34
|
+
experimental_clientGenerateTemplateNames?: string[];
|
|
34
35
|
templates?: {
|
|
35
36
|
service?: string;
|
|
36
37
|
controller?: string;
|
|
@@ -47,6 +48,7 @@ export type VovkModuleRenderResult = {
|
|
|
47
48
|
};
|
|
48
49
|
export interface DevOptions {
|
|
49
50
|
nextDev?: boolean;
|
|
51
|
+
exit?: boolean;
|
|
50
52
|
}
|
|
51
53
|
export interface GenerateOptions {
|
|
52
54
|
clientOutDir?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vovk-cli",
|
|
3
|
-
"version": "0.0.1-draft.
|
|
3
|
+
"version": "0.0.1-draft.44",
|
|
4
4
|
"bin": {
|
|
5
5
|
"vovk": "./dist/index.mjs"
|
|
6
6
|
},
|
|
@@ -36,27 +36,27 @@
|
|
|
36
36
|
},
|
|
37
37
|
"homepage": "https://vovk.dev",
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"vovk": "^3.0.0-draft.
|
|
39
|
+
"vovk": "^3.0.0-draft.50"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@inquirer/prompts": "^7.
|
|
43
|
-
"@npmcli/package-json": "^6.1.
|
|
44
|
-
"chalk": "^5.
|
|
45
|
-
"chokidar": "^4.0.
|
|
46
|
-
"commander": "^
|
|
47
|
-
"concurrently": "^9.1.
|
|
48
|
-
"dotenv": "^16.4.
|
|
42
|
+
"@inquirer/prompts": "^7.2.3",
|
|
43
|
+
"@npmcli/package-json": "^6.1.1",
|
|
44
|
+
"chalk": "^5.4.1",
|
|
45
|
+
"chokidar": "^4.0.3",
|
|
46
|
+
"commander": "^13.1.0",
|
|
47
|
+
"concurrently": "^9.1.2",
|
|
48
|
+
"dotenv": "^16.4.7",
|
|
49
49
|
"ejs": "^3.1.10",
|
|
50
50
|
"gray-matter": "^4.0.3",
|
|
51
|
-
"inflection": "^3.0.
|
|
51
|
+
"inflection": "^3.0.2",
|
|
52
52
|
"jsonc-parser": "^3.3.1",
|
|
53
53
|
"lodash": "^4.17.21",
|
|
54
54
|
"loglevel": "^1.9.2",
|
|
55
55
|
"pluralize": "^8.0.0",
|
|
56
|
-
"prettier": "^3.4.
|
|
56
|
+
"prettier": "^3.4.2",
|
|
57
57
|
"tar-stream": "^3.1.7",
|
|
58
|
-
"ts-morph": "^
|
|
59
|
-
"undici": "^7.
|
|
58
|
+
"ts-morph": "^25.0.0",
|
|
59
|
+
"undici": "^7.3.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/concat-stream": "^2.0.3",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"@types/pluralize": "^0.0.33",
|
|
66
66
|
"@types/tar-stream": "^3.1.3",
|
|
67
67
|
"concat-stream": "^2.0.0",
|
|
68
|
-
"create-next-app": "^15.
|
|
68
|
+
"create-next-app": "^15.1.6",
|
|
69
69
|
"node-pty": "^1.0.0",
|
|
70
|
-
"type-fest": "^4.
|
|
70
|
+
"type-fest": "^4.33.0"
|
|
71
71
|
}
|
|
72
72
|
}
|