vovk-cli 0.0.1-beta.10 → 0.0.1-beta.12
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/dist/getProjectInfo/getConfig.mjs +1 -1
- package/dist/getProjectInfo/index.d.mts +1 -2
- package/dist/getProjectInfo/index.mjs +2 -5
- package/dist/index.mjs +4 -4
- package/dist/server/{diffMetadata.d.mts → diffSchema.d.mts} +5 -5
- package/dist/server/{diffMetadata.mjs → diffSchema.mjs} +1 -1
- package/dist/server/ensureSchemaFiles.d.mts +3 -0
- package/dist/server/{ensureMetadataFiles.mjs → ensureSchemaFiles.mjs} +19 -19
- package/dist/server/generateClient.d.mts +2 -2
- package/dist/server/generateClient.mjs +17 -17
- package/dist/server/index.mjs +35 -39
- package/dist/server/isMetadataEmpty.d.mts +2 -2
- package/dist/server/isMetadataEmpty.mjs +2 -2
- package/dist/server/logDiffResult.d.mts +1 -1
- package/dist/server/writeOneSchemaFile.d.mts +11 -0
- package/dist/server/{writeOneMetadataFile.mjs → writeOneSchemaFile.mjs} +7 -7
- package/dist/types.d.mts +2 -3
- package/package.json +2 -2
- package/dist/server/ensureMetadataFiles.d.mts +0 -3
- package/dist/server/writeOneMetadataFile.d.mts +0 -11
|
@@ -9,7 +9,7 @@ export default async function getConfig({ clientOutDir }) {
|
|
|
9
9
|
validateOnClient: env.VOVK_VALIDATE_ON_CLIENT ?? userConfig.validateOnClient ?? null,
|
|
10
10
|
validationLibrary: env.VOVK_VALIDATION_LIBRARY ?? userConfig.validationLibrary ?? null,
|
|
11
11
|
fetcher: env.VOVK_FETCHER ?? userConfig.fetcher ?? 'vovk/client/defaultFetcher',
|
|
12
|
-
|
|
12
|
+
schemaOutDir: env.VOVK_SCHEMA_OUT_DIR ?? userConfig.schemaOutDir ?? './.vovk-schema',
|
|
13
13
|
clientOutDir: clientOutDir ?? env.VOVK_CLIENT_OUT_DIR ?? userConfig.clientOutDir ?? './node_modules/.vovk',
|
|
14
14
|
origin: (env.VOVK_ORIGIN ?? userConfig.origin ?? '').replace(/\/$/, ''), // Remove trailing slash
|
|
15
15
|
rootEntry: env.VOVK_ROOT_ENTRY ?? userConfig.rootEntry ?? 'api',
|
|
@@ -6,11 +6,10 @@ export default function getProjectInfo({ port: givenPort, clientOutDir, }?: {
|
|
|
6
6
|
}): Promise<{
|
|
7
7
|
cwd: string;
|
|
8
8
|
port: string;
|
|
9
|
-
vovkPort: string;
|
|
10
9
|
apiEntryPoint: string;
|
|
11
10
|
apiDir: string;
|
|
12
11
|
srcRoot: string;
|
|
13
|
-
|
|
12
|
+
schemaOutImportPath: string;
|
|
14
13
|
fetcherClientImportPath: string;
|
|
15
14
|
config: Required<import("../types.mjs").VovkConfig>;
|
|
16
15
|
log: {
|
|
@@ -3,16 +3,14 @@ import loglevel from 'loglevel';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import getConfig from './getConfig.mjs';
|
|
5
5
|
export default async function getProjectInfo({ port: givenPort, clientOutDir, } = {}) {
|
|
6
|
-
const env = process.env;
|
|
7
6
|
const port = givenPort?.toString() ?? process.env.PORT ?? '3000';
|
|
8
7
|
// Make PORT available to the config file at getConfig
|
|
9
8
|
process.env.PORT = port;
|
|
10
9
|
const cwd = process.cwd();
|
|
11
10
|
const { config, srcRoot } = await getConfig({ clientOutDir });
|
|
12
|
-
const vovkPort = env.VOVK_PORT || (parseInt(port) + 6969).toString();
|
|
13
11
|
const apiEntryPoint = `${config.origin ?? ''}/${config.rootEntry}`;
|
|
14
12
|
const apiDir = path.join(srcRoot, 'app', config.rootEntry);
|
|
15
|
-
const
|
|
13
|
+
const schemaOutImportPath = path.relative(config.clientOutDir, config.schemaOutDir);
|
|
16
14
|
const fetcherClientImportPath = config.fetcher.startsWith('.')
|
|
17
15
|
? path.relative(config.clientOutDir, config.fetcher)
|
|
18
16
|
: config.fetcher;
|
|
@@ -27,11 +25,10 @@ export default async function getProjectInfo({ port: givenPort, clientOutDir, }
|
|
|
27
25
|
return {
|
|
28
26
|
cwd,
|
|
29
27
|
port,
|
|
30
|
-
vovkPort,
|
|
31
28
|
apiEntryPoint,
|
|
32
29
|
apiDir,
|
|
33
30
|
srcRoot,
|
|
34
|
-
|
|
31
|
+
schemaOutImportPath,
|
|
35
32
|
fetcherClientImportPath,
|
|
36
33
|
config,
|
|
37
34
|
log,
|
package/dist/index.mjs
CHANGED
|
@@ -35,7 +35,7 @@ program
|
|
|
35
35
|
const { result } = concurrently([
|
|
36
36
|
{
|
|
37
37
|
command: `node ${import.meta.dirname}/server/index.mjs`,
|
|
38
|
-
name: 'Vovk.ts
|
|
38
|
+
name: 'Vovk.ts Schema Server',
|
|
39
39
|
env: Object.assign({ PORT, __VOVK_START_SERVER_IN_STANDALONE_MODE__: 'true' }, options.clientOut ? { VOVK_CLIENT_OUT_DIR: options.clientOut } : {}),
|
|
40
40
|
},
|
|
41
41
|
{
|
|
@@ -67,9 +67,9 @@ program
|
|
|
67
67
|
const projectInfo = await getProjectInfo({ clientOutDir: options.clientOut });
|
|
68
68
|
const { cwd, config, apiDir } = projectInfo;
|
|
69
69
|
const segments = await locateSegments(apiDir);
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
await generateClient(projectInfo, segments,
|
|
70
|
+
const schemaOutFullPath = path.join(cwd, config.schemaOutDir);
|
|
71
|
+
const schema = (await import(path.join(schemaOutFullPath, 'index.js')));
|
|
72
|
+
await generateClient(projectInfo, segments, schema.default);
|
|
73
73
|
});
|
|
74
74
|
program
|
|
75
75
|
.command('help')
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { VovkSchema } from 'vovk';
|
|
2
|
+
import { _VovkControllerSchema, _VovkWorkerSchema } from 'vovk/types';
|
|
3
3
|
interface HandlersDiff {
|
|
4
4
|
nameOfClass: string;
|
|
5
5
|
added: string[];
|
|
@@ -15,8 +15,8 @@ export interface DiffResult {
|
|
|
15
15
|
workers: WorkersOrControllersDiff;
|
|
16
16
|
controllers: WorkersOrControllersDiff;
|
|
17
17
|
}
|
|
18
|
-
export declare function diffHandlers<T extends
|
|
19
|
-
export declare function diffWorkersOrControllers<T extends
|
|
18
|
+
export declare function diffHandlers<T extends _VovkWorkerSchema['_handlers'] | _VovkControllerSchema['_handlers']>(oldHandlers: T, newHandlers: T, nameOfClass: string): HandlersDiff;
|
|
19
|
+
export declare function diffWorkersOrControllers<T extends VovkSchema['controllers'] | VovkSchema['workers']>(oldItems: T, newItems: T): WorkersOrControllersDiff;
|
|
20
20
|
/**
|
|
21
21
|
example output:
|
|
22
22
|
{
|
|
@@ -39,5 +39,5 @@ example output:
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
*/
|
|
42
|
-
export default function
|
|
42
|
+
export default function diffSchema(oldJson: VovkSchema, newJson: VovkSchema): DiffResult;
|
|
43
43
|
export {};
|
|
@@ -62,7 +62,7 @@ example output:
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
*/
|
|
65
|
-
export default function
|
|
65
|
+
export default function diffSchema(oldJson, newJson) {
|
|
66
66
|
const workersDiff = diffWorkersOrControllers(oldJson.workers ?? {}, newJson.workers ?? {});
|
|
67
67
|
const controllersDiff = diffWorkersOrControllers(oldJson.controllers ?? {}, newJson.controllers ?? {});
|
|
68
68
|
return {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
2
|
+
export default function ensureSchemaFiles(schemaOutFullPath: string, segmentNames: string[], projectInfo: ProjectInfo | null): Promise<void>;
|
|
3
|
+
export declare const debouncedEnsureSchemaFiles: import("lodash").DebouncedFunc<typeof ensureSchemaFiles>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import debounce from 'lodash/debounce.js';
|
|
4
|
-
import
|
|
5
|
-
export default async function
|
|
4
|
+
import writeOneSchemaFile, { ROOT_SEGMENT_SCHEMA_NAME } from './writeOneSchemaFile.mjs';
|
|
5
|
+
export default async function ensureSchemaFiles(schemaOutFullPath, segmentNames, projectInfo) {
|
|
6
6
|
const now = Date.now();
|
|
7
7
|
let hasChanged = false;
|
|
8
8
|
// Create index.js file
|
|
@@ -11,18 +11,18 @@ export default async function ensureMetadataFiles(metadataOutFullPath, segmentNa
|
|
|
11
11
|
return `module.exports['${segmentName}'] = require('./${segmentName || ROOT_SEGMENT_SCHEMA_NAME}.json');`;
|
|
12
12
|
})
|
|
13
13
|
.join('\n');
|
|
14
|
-
const dTsContent = `import type {
|
|
15
|
-
declare const
|
|
16
|
-
export default
|
|
17
|
-
await fs.mkdir(
|
|
18
|
-
await fs.writeFile(path.join(
|
|
19
|
-
await fs.writeFile(path.join(
|
|
14
|
+
const dTsContent = `import type { VovkSchema } from 'vovk';
|
|
15
|
+
declare const segmentSchema: Record<string, VovkSchema>;
|
|
16
|
+
export default segmentSchema;`;
|
|
17
|
+
await fs.mkdir(schemaOutFullPath, { recursive: true });
|
|
18
|
+
await fs.writeFile(path.join(schemaOutFullPath, 'index.js'), indexContent);
|
|
19
|
+
await fs.writeFile(path.join(schemaOutFullPath, 'index.d.ts'), dTsContent);
|
|
20
20
|
// Create JSON files (if not exist) with name [segmentName].json (where segmentName can include /, which means the folder structure can be nested) : {} (empty object)
|
|
21
21
|
await Promise.all(segmentNames.map(async (segmentName) => {
|
|
22
|
-
const { isCreated } = await
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const { isCreated } = await writeOneSchemaFile({
|
|
23
|
+
schemaOutFullPath,
|
|
24
|
+
schema: {
|
|
25
|
+
emitSchema: false,
|
|
26
26
|
segmentName,
|
|
27
27
|
controllers: {},
|
|
28
28
|
workers: {},
|
|
@@ -30,7 +30,7 @@ export default segmentMetadata;`;
|
|
|
30
30
|
skipIfExists: true,
|
|
31
31
|
});
|
|
32
32
|
if (isCreated) {
|
|
33
|
-
projectInfo?.log.debug(`Created empty
|
|
33
|
+
projectInfo?.log.debug(`Created empty schema file for segment "${segmentName}"`);
|
|
34
34
|
hasChanged = true;
|
|
35
35
|
}
|
|
36
36
|
}));
|
|
@@ -46,25 +46,25 @@ export default segmentMetadata;`;
|
|
|
46
46
|
const remainingEntries = await fs.readdir(fullPath);
|
|
47
47
|
if (remainingEntries.length === 0) {
|
|
48
48
|
await fs.rmdir(fullPath);
|
|
49
|
-
projectInfo?.log.debug(`Deleted unnecessary
|
|
49
|
+
projectInfo?.log.debug(`Deleted unnecessary schema directory "${fullPath}"`);
|
|
50
50
|
hasChanged = true;
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
else if (entry.isFile() && entry.name.endsWith('.json')) {
|
|
54
|
-
const relativePath = path.relative(
|
|
54
|
+
const relativePath = path.relative(schemaOutFullPath, fullPath);
|
|
55
55
|
const segmentName = relativePath.replace(/\\/g, '/').slice(0, -5); // Remove '.json' extension
|
|
56
56
|
if (!segmentNames.includes(segmentName) &&
|
|
57
57
|
!segmentNames.includes(segmentName.replace(ROOT_SEGMENT_SCHEMA_NAME, ''))) {
|
|
58
58
|
await fs.unlink(fullPath);
|
|
59
|
-
projectInfo?.log.debug(`Deleted unnecessary
|
|
59
|
+
projectInfo?.log.debug(`Deleted unnecessary schema file for segment "${segmentName}"`);
|
|
60
60
|
hasChanged = true;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
}));
|
|
64
64
|
}
|
|
65
65
|
// Start the recursive deletion from the root directory
|
|
66
|
-
await deleteUnnecessaryJsonFiles(
|
|
66
|
+
await deleteUnnecessaryJsonFiles(schemaOutFullPath);
|
|
67
67
|
if (hasChanged)
|
|
68
|
-
projectInfo?.log.info(`
|
|
68
|
+
projectInfo?.log.info(`Schema files updated in ${Date.now() - now}ms`);
|
|
69
69
|
}
|
|
70
|
-
export const
|
|
70
|
+
export const debouncedEnsureSchemaFiles = debounce(ensureSchemaFiles, 1000);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
2
2
|
import type { Segment } from '../locateSegments.mjs';
|
|
3
|
-
import {
|
|
4
|
-
export default function generateClient(projectInfo: ProjectInfo, segments: Segment[],
|
|
3
|
+
import type { VovkSchema } from 'vovk';
|
|
4
|
+
export default function generateClient(projectInfo: ProjectInfo, segments: Segment[], segmentsSchema: Record<string, VovkSchema>): Promise<{
|
|
5
5
|
written: boolean;
|
|
6
6
|
path: string;
|
|
7
7
|
}>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
|
-
export default async function generateClient(projectInfo, segments,
|
|
3
|
+
export default async function generateClient(projectInfo, segments, segmentsSchema) {
|
|
4
4
|
const now = Date.now();
|
|
5
5
|
const clientoOutDirFullPath = path.join(projectInfo.cwd, projectInfo.config.clientOutDir);
|
|
6
6
|
const validateFullPath = projectInfo.config.validateOnClient?.startsWith('.')
|
|
@@ -19,7 +19,7 @@ import type fetcher from '${projectInfo.fetcherClientImportPath}';
|
|
|
19
19
|
const { clientizeController } = require('vovk/client');
|
|
20
20
|
const { promisifyWorker } = require('vovk/worker');
|
|
21
21
|
const { default: fetcher } = require('${projectInfo.fetcherClientImportPath}');
|
|
22
|
-
const
|
|
22
|
+
const schema = require('${projectInfo.schemaOutImportPath}');
|
|
23
23
|
`;
|
|
24
24
|
let ts = `// auto-generated
|
|
25
25
|
/* eslint-disable */
|
|
@@ -27,16 +27,16 @@ import { clientizeController } from 'vovk/client';
|
|
|
27
27
|
import { promisifyWorker } from 'vovk/worker';
|
|
28
28
|
import type { VovkClientFetcher } from 'vovk/client';
|
|
29
29
|
import fetcher from '${projectInfo.fetcherClientImportPath}';
|
|
30
|
-
import
|
|
30
|
+
import schema from '${projectInfo.schemaOutImportPath}';
|
|
31
31
|
|
|
32
32
|
`;
|
|
33
33
|
for (let i = 0; i < segments.length; i++) {
|
|
34
34
|
const { routeFilePath, segmentName } = segments[i];
|
|
35
|
-
const
|
|
36
|
-
if (!
|
|
37
|
-
throw new Error(`Unable to generate client. No
|
|
35
|
+
const schema = segmentsSchema[segmentName];
|
|
36
|
+
if (!schema) {
|
|
37
|
+
throw new Error(`Unable to generate client. No schema found for segment ${segmentName}`);
|
|
38
38
|
}
|
|
39
|
-
if (!
|
|
39
|
+
if (!schema.emitSchema)
|
|
40
40
|
continue;
|
|
41
41
|
const importRouteFilePath = path.relative(projectInfo.config.clientOutDir, routeFilePath);
|
|
42
42
|
dts += `import type { Controllers as Controllers${i}, Workers as Workers${i} } from "${importRouteFilePath}";\n`;
|
|
@@ -56,21 +56,21 @@ const prefix = '${projectInfo.apiEntryPoint}';
|
|
|
56
56
|
`;
|
|
57
57
|
for (let i = 0; i < segments.length; i++) {
|
|
58
58
|
const { segmentName } = segments[i];
|
|
59
|
-
const
|
|
60
|
-
if (!
|
|
61
|
-
throw new Error(`Unable to generate client. No
|
|
59
|
+
const schema = segmentsSchema[segmentName];
|
|
60
|
+
if (!schema) {
|
|
61
|
+
throw new Error(`Unable to generate client. No schema found for segment ${segmentName}`);
|
|
62
62
|
}
|
|
63
|
-
if (!
|
|
63
|
+
if (!schema.emitSchema)
|
|
64
64
|
continue;
|
|
65
|
-
for (const key of Object.keys(
|
|
65
|
+
for (const key of Object.keys(schema.controllers)) {
|
|
66
66
|
dts += `export const ${key}: ReturnType<typeof clientizeController<Controllers${i}["${key}"], Options>>;\n`;
|
|
67
|
-
js += `exports.${key} = clientizeController(
|
|
68
|
-
ts += `export const ${key} = clientizeController<Controllers${i}["${key}"], Options>(
|
|
67
|
+
js += `exports.${key} = clientizeController(schema['${segmentName}'].controllers.${key}, '${segmentName}', { fetcher, validateOnClient, defaultOptions: { prefix } });\n`;
|
|
68
|
+
ts += `export const ${key} = clientizeController<Controllers${i}["${key}"], Options>(schema['${segmentName}'].controllers.${key}, '${segmentName}', { fetcher, validateOnClient, defaultOptions: { prefix } });\n`;
|
|
69
69
|
}
|
|
70
|
-
for (const key of Object.keys(
|
|
70
|
+
for (const key of Object.keys(schema.workers)) {
|
|
71
71
|
dts += `export const ${key}: ReturnType<typeof promisifyWorker<Workers${i}["${key}"]>>;\n`;
|
|
72
|
-
js += `exports.${key} = promisifyWorker(null,
|
|
73
|
-
ts += `export const ${key} = promisifyWorker<Workers${i}["${key}"]>(null,
|
|
72
|
+
js += `exports.${key} = promisifyWorker(null, schema['${segmentName}'].workers.${key});\n`;
|
|
73
|
+
ts += `export const ${key} = promisifyWorker<Workers${i}["${key}"]>(null, schema['${segmentName}'].workers.${key});\n`;
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
const localJsPath = path.join(clientoOutDirFullPath, 'client.js');
|
package/dist/server/index.mjs
CHANGED
|
@@ -2,8 +2,8 @@ import * as chokidar from 'chokidar';
|
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
3
|
import getProjectInfo from '../getProjectInfo/index.mjs';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { debouncedEnsureSchemaFiles } from './ensureSchemaFiles.mjs';
|
|
6
|
+
import writeOneSchemaFile from './writeOneSchemaFile.mjs';
|
|
7
7
|
import logDiffResult from './logDiffResult.mjs';
|
|
8
8
|
import generateClient from './generateClient.mjs';
|
|
9
9
|
import locateSegments from '../locateSegments.mjs';
|
|
@@ -13,14 +13,14 @@ import isEmpty from 'lodash/isEmpty.js';
|
|
|
13
13
|
export class VovkCLIServer {
|
|
14
14
|
#projectInfo;
|
|
15
15
|
#segments = [];
|
|
16
|
-
#
|
|
16
|
+
#schemas = {};
|
|
17
17
|
#isWatching = false;
|
|
18
18
|
#modulesWatcher = null;
|
|
19
19
|
#segmentWatcher = null;
|
|
20
20
|
#watchSegments = () => {
|
|
21
21
|
const segmentReg = /\/?\[\[\.\.\.[a-zA-Z-_]+\]\]\/route.ts$/;
|
|
22
22
|
const { cwd, log, config, apiDir } = this.#projectInfo;
|
|
23
|
-
const
|
|
23
|
+
const schemaOutFullPath = path.join(cwd, config.schemaOutDir);
|
|
24
24
|
const apiDirFullPath = path.join(cwd, apiDir);
|
|
25
25
|
const getSegmentName = (filePath) => path.relative(apiDirFullPath, filePath).replace(segmentReg, '');
|
|
26
26
|
log.debug(`Watching segments in ${apiDirFullPath}`);
|
|
@@ -38,7 +38,7 @@ export class VovkCLIServer {
|
|
|
38
38
|
: [...this.#segments, { routeFilePath: filePath, segmentName }];
|
|
39
39
|
log.info(`Segment "${segmentName}" has been added`);
|
|
40
40
|
log.debug(`Full list of segments: ${this.#segments.map((s) => s.segmentName).join(', ')}`);
|
|
41
|
-
void
|
|
41
|
+
void debouncedEnsureSchemaFiles(schemaOutFullPath, this.#segments.map((s) => s.segmentName), this.#projectInfo // TODO refactor
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
44
|
})
|
|
@@ -71,7 +71,7 @@ export class VovkCLIServer {
|
|
|
71
71
|
this.#segments = this.#segments.filter((s) => s.segmentName !== segmentName);
|
|
72
72
|
log.info(`Segment "${segmentName}" has been removed`);
|
|
73
73
|
log.debug(`Full list of segments: ${this.#segments.map((s) => s.segmentName).join(', ')}`);
|
|
74
|
-
void
|
|
74
|
+
void debouncedEnsureSchemaFiles(schemaOutFullPath, this.#segments.map((s) => s.segmentName), this.#projectInfo // TODO refactor
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
77
|
})
|
|
@@ -169,10 +169,10 @@ export class VovkCLIServer {
|
|
|
169
169
|
const importRegex = /import\s*{[^}]*\b(initVovk|get|post|put|del|head|options|worker)\b[^}]*}\s*from\s*['"]vovk['"]/;
|
|
170
170
|
if (importRegex.test(code) && namesOfClasses.length) {
|
|
171
171
|
const affectedSegments = this.#segments.filter((s) => {
|
|
172
|
-
const
|
|
173
|
-
if (!
|
|
172
|
+
const schema = this.#schemas[s.segmentName];
|
|
173
|
+
if (!schema)
|
|
174
174
|
return false;
|
|
175
|
-
return namesOfClasses.some((name) =>
|
|
175
|
+
return namesOfClasses.some((name) => schema.controllers[name] || schema.workers[name]);
|
|
176
176
|
});
|
|
177
177
|
if (affectedSegments.length) {
|
|
178
178
|
log.debug(`A file with controller or worker ${namesOfClasses.join(', ')} have been modified at path "${filePath}". Segment(s) affected: ${affectedSegments.map((s) => s.segmentName).join(', ')}`);
|
|
@@ -191,53 +191,53 @@ export class VovkCLIServer {
|
|
|
191
191
|
log.warn(`Ping to segment "${segmentName}" failed with status code ${resp.status}. Expected 200.`);
|
|
192
192
|
return;
|
|
193
193
|
}
|
|
194
|
-
let
|
|
194
|
+
let schema = null;
|
|
195
195
|
try {
|
|
196
|
-
({
|
|
196
|
+
({ schema } = (await resp.json()));
|
|
197
197
|
}
|
|
198
198
|
catch (error) {
|
|
199
|
-
log.error(`Error parsing
|
|
199
|
+
log.error(`Error parsing schema for segment "${segmentName}": ${error.message}`);
|
|
200
200
|
}
|
|
201
|
-
await this.#
|
|
201
|
+
await this.#handleSchema(schema);
|
|
202
202
|
}, 500);
|
|
203
|
-
async #
|
|
203
|
+
async #handleSchema(schema) {
|
|
204
204
|
const { log, config, cwd } = this.#projectInfo;
|
|
205
|
-
log.debug(`Handling received
|
|
206
|
-
if (!
|
|
207
|
-
log.warn('Segment
|
|
205
|
+
log.debug(`Handling received schema`);
|
|
206
|
+
if (!schema) {
|
|
207
|
+
log.warn('Segment schema is null');
|
|
208
208
|
return;
|
|
209
209
|
}
|
|
210
|
-
const
|
|
211
|
-
const segment = this.#segments.find((s) => s.segmentName ===
|
|
210
|
+
const schemaOutFullPath = path.join(cwd, config.schemaOutDir);
|
|
211
|
+
const segment = this.#segments.find((s) => s.segmentName === schema.segmentName);
|
|
212
212
|
if (!segment) {
|
|
213
|
-
log.warn(`Segment "${
|
|
213
|
+
log.warn(`Segment "${schema.segmentName}" not found`);
|
|
214
214
|
return;
|
|
215
215
|
}
|
|
216
|
-
this.#
|
|
217
|
-
if (
|
|
216
|
+
this.#schemas[schema.segmentName] = schema;
|
|
217
|
+
if (schema.emitSchema) {
|
|
218
218
|
const now = Date.now();
|
|
219
|
-
const { diffResult } = await
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
const { diffResult } = await writeOneSchemaFile({
|
|
220
|
+
schemaOutFullPath,
|
|
221
|
+
schema,
|
|
222
222
|
skipIfExists: false,
|
|
223
223
|
});
|
|
224
224
|
const timeTook = Date.now() - now;
|
|
225
225
|
if (diffResult) {
|
|
226
226
|
logDiffResult(segment.segmentName, diffResult, this.#projectInfo);
|
|
227
|
-
log.info(`
|
|
227
|
+
log.info(`Schema for segment "${schema.segmentName}" has been updated in ${timeTook}ms`);
|
|
228
228
|
}
|
|
229
229
|
}
|
|
230
|
-
else if (
|
|
231
|
-
log.error(`Non-empty
|
|
230
|
+
else if (schema && (!isEmpty(schema.controllers) || !isEmpty(schema.workers))) {
|
|
231
|
+
log.error(`Non-empty schema provided for segment "${schema.segmentName}" but emitSchema is false`);
|
|
232
232
|
}
|
|
233
|
-
if (this.#segments.every((s) => this.#
|
|
234
|
-
log.debug(`All segments with
|
|
235
|
-
await generateClient(this.#projectInfo, this.#segments, this.#
|
|
233
|
+
if (this.#segments.every((s) => this.#schemas[s.segmentName])) {
|
|
234
|
+
log.debug(`All segments with emitSchema=true have schema.`);
|
|
235
|
+
await generateClient(this.#projectInfo, this.#segments, this.#schemas);
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
async startServer({ clientOutDir } = {}) {
|
|
239
239
|
this.#projectInfo = await getProjectInfo({ clientOutDir });
|
|
240
|
-
const {
|
|
240
|
+
const { log, config, cwd, apiDir } = this.#projectInfo;
|
|
241
241
|
process.on('uncaughtException', (err) => {
|
|
242
242
|
log.error(`Uncaught Exception: ${err.message}`);
|
|
243
243
|
});
|
|
@@ -245,14 +245,10 @@ export class VovkCLIServer {
|
|
|
245
245
|
log.error(`Unhandled Rejection: ${String(reason)}`);
|
|
246
246
|
});
|
|
247
247
|
const apiDirFullPath = path.join(cwd, apiDir);
|
|
248
|
-
const
|
|
248
|
+
const schemaOutFullPath = path.join(cwd, config.schemaOutDir);
|
|
249
249
|
this.#segments = await locateSegments(apiDirFullPath);
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
await debouncedEnsureMetadataFiles(metadataOutFullPath, this.#segments.map((s) => s.segmentName), this.#projectInfo);
|
|
255
|
-
// Ping every segment in 3 seconds in order to update metadata and start watching
|
|
250
|
+
await debouncedEnsureSchemaFiles(schemaOutFullPath, this.#segments.map((s) => s.segmentName), this.#projectInfo);
|
|
251
|
+
// Ping every segment in 3 seconds in order to update schema and start watching
|
|
256
252
|
setTimeout(() => {
|
|
257
253
|
for (const { segmentName } of this.#segments) {
|
|
258
254
|
void this.#ping(segmentName);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export default function
|
|
1
|
+
import type { VovkSchema } from 'vovk';
|
|
2
|
+
export default function isSchemaEmpty(schema: VovkSchema): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import isEmpty from 'lodash/isEmpty.js';
|
|
2
|
-
export default function
|
|
3
|
-
return isEmpty(
|
|
2
|
+
export default function isSchemaEmpty(schema) {
|
|
3
|
+
return isEmpty(schema.controllers) && isEmpty(schema.workers);
|
|
4
4
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
2
|
-
import { DiffResult } from './
|
|
2
|
+
import { DiffResult } from './diffSchema.mjs';
|
|
3
3
|
export default function logDiffResult(segmentName: string, diffResult: DiffResult, projectInfo: ProjectInfo): void;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { VovkSchema } from 'vovk';
|
|
2
|
+
import { DiffResult } from './diffSchema.mjs';
|
|
3
|
+
export declare const ROOT_SEGMENT_SCHEMA_NAME = "_root";
|
|
4
|
+
export default function writeOneSchemaFile({ schemaOutFullPath, schema, skipIfExists, }: {
|
|
5
|
+
schemaOutFullPath: string;
|
|
6
|
+
schema: VovkSchema;
|
|
7
|
+
skipIfExists?: boolean;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
isCreated: boolean;
|
|
10
|
+
diffResult: DiffResult | null;
|
|
11
|
+
}>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
|
-
import
|
|
3
|
+
import diffSchema from './diffSchema.mjs';
|
|
4
4
|
export const ROOT_SEGMENT_SCHEMA_NAME = '_root';
|
|
5
|
-
export default async function
|
|
6
|
-
const segmentPath = path.join(
|
|
5
|
+
export default async function writeOneSchemaFile({ schemaOutFullPath, schema, skipIfExists = false, }) {
|
|
6
|
+
const segmentPath = path.join(schemaOutFullPath, `${schema.segmentName || ROOT_SEGMENT_SCHEMA_NAME}.json`);
|
|
7
7
|
if (skipIfExists) {
|
|
8
8
|
try {
|
|
9
9
|
await fs.stat(segmentPath);
|
|
@@ -14,14 +14,14 @@ export default async function writeOneMetadataFile({ metadataOutFullPath, metada
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
await fs.mkdir(path.dirname(segmentPath), { recursive: true });
|
|
17
|
-
const
|
|
17
|
+
const schemaStr = JSON.stringify(schema, null, 2);
|
|
18
18
|
const existing = await fs.readFile(segmentPath, 'utf-8').catch(() => null);
|
|
19
|
-
if (existing ===
|
|
19
|
+
if (existing === schemaStr) {
|
|
20
20
|
return { isCreated: false, diffResult: null };
|
|
21
21
|
}
|
|
22
|
-
await fs.writeFile(segmentPath,
|
|
22
|
+
await fs.writeFile(segmentPath, schemaStr);
|
|
23
23
|
if (existing) {
|
|
24
|
-
return { isCreated: false, diffResult:
|
|
24
|
+
return { isCreated: false, diffResult: diffSchema(JSON.parse(existing), schema) };
|
|
25
25
|
}
|
|
26
26
|
return { isCreated: true, diffResult: null };
|
|
27
27
|
}
|
package/dist/types.d.mts
CHANGED
|
@@ -3,10 +3,9 @@ export type KnownAny = any;
|
|
|
3
3
|
export type VovkEnv = {
|
|
4
4
|
PORT?: string;
|
|
5
5
|
VOVK_CLIENT_OUT_DIR?: string;
|
|
6
|
-
|
|
6
|
+
VOVK_SCHEMA_OUT_DIR?: string;
|
|
7
7
|
VOVK_FETCHER?: string;
|
|
8
8
|
VOVK_VALIDATE_ON_CLIENT?: string;
|
|
9
|
-
VOVK_PORT?: string;
|
|
10
9
|
VOVK_MODULES_DIR?: string;
|
|
11
10
|
VOVK_VALIDATION_LIBRARY?: string;
|
|
12
11
|
VOVK_ORIGIN?: string;
|
|
@@ -18,7 +17,7 @@ export type VovkEnv = {
|
|
|
18
17
|
};
|
|
19
18
|
export type VovkConfig = {
|
|
20
19
|
clientOutDir?: string;
|
|
21
|
-
|
|
20
|
+
schemaOutDir?: string;
|
|
22
21
|
fetcher?: string;
|
|
23
22
|
validateOnClient?: string | null;
|
|
24
23
|
modulesDir?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vovk-cli",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.12",
|
|
4
4
|
"bin": {
|
|
5
5
|
"vovk": "./dist/index.mjs"
|
|
6
6
|
},
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://vovk.dev",
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"vovk": "^3.0.0-beta.
|
|
33
|
+
"vovk": "^3.0.0-beta.22"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@inquirer/prompts": "^5.3.8",
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
2
|
-
export default function ensureMetadataFiles(metadataOutFullPath: string, segmentNames: string[], projectInfo: ProjectInfo | null): Promise<void>;
|
|
3
|
-
export declare const debouncedEnsureMetadataFiles: import("lodash").DebouncedFunc<typeof ensureMetadataFiles>;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { VovkMetadata } from 'vovk';
|
|
2
|
-
import { DiffResult } from './diffMetadata.mjs';
|
|
3
|
-
export declare const ROOT_SEGMENT_SCHEMA_NAME = "_root";
|
|
4
|
-
export default function writeOneMetadataFile({ metadataOutFullPath, metadata, skipIfExists, }: {
|
|
5
|
-
metadataOutFullPath: string;
|
|
6
|
-
metadata: VovkMetadata;
|
|
7
|
-
skipIfExists?: boolean;
|
|
8
|
-
}): Promise<{
|
|
9
|
-
isCreated: boolean;
|
|
10
|
-
diffResult: DiffResult | null;
|
|
11
|
-
}>;
|