vovk-cli 0.0.1-draft.3 → 0.0.1-draft.30
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/README.md +1 -1
- package/dist/{watcher → dev}/diffSchema.d.mts +2 -2
- package/dist/{watcher → dev}/diffSchema.mjs +1 -1
- package/dist/dev/ensureClient.d.mts +5 -0
- package/dist/dev/ensureClient.mjs +31 -0
- package/dist/{watcher → dev}/ensureSchemaFiles.mjs +20 -8
- package/dist/dev/index.d.mts +4 -0
- package/dist/{watcher → dev}/index.mjs +30 -30
- package/dist/{watcher → dev}/logDiffResult.d.mts +2 -2
- package/dist/{watcher → dev}/logDiffResult.mjs +13 -9
- package/dist/{watcher → dev}/writeOneSchemaFile.d.mts +1 -1
- package/dist/{watcher → dev}/writeOneSchemaFile.mjs +4 -3
- package/dist/generateClient.d.mts +1 -1
- package/dist/generateClient.mjs +6 -6
- package/dist/getProjectInfo/getConfigAbsolutePaths.mjs +2 -2
- package/dist/getProjectInfo/getRelativeSrcRoot.mjs +4 -4
- package/dist/getProjectInfo/getUserConfig.mjs +1 -1
- package/dist/getProjectInfo/index.mjs +1 -1
- package/dist/index.d.mts +2 -24
- package/dist/index.mjs +15 -41
- package/dist/init/checkTSConfigForExperimentalDecorators.mjs +2 -2
- package/dist/init/createConfig.d.mts +3 -4
- package/dist/init/createConfig.mjs +5 -5
- package/dist/init/getTemplateFilesFromPackage.d.mts +2 -1
- package/dist/init/getTemplateFilesFromPackage.mjs +3 -4
- package/dist/init/index.d.mts +1 -2
- package/dist/init/index.mjs +36 -85
- package/dist/init/installDependencies.d.mts +4 -1
- package/dist/init/installDependencies.mjs +2 -2
- package/dist/init/logUpdateDependenciesError.d.mts +11 -0
- package/dist/init/logUpdateDependenciesError.mjs +45 -0
- package/dist/init/updateDependenciesWithoutInstalling.d.mts +3 -2
- package/dist/init/updateDependenciesWithoutInstalling.mjs +12 -7
- package/dist/init/updateNPMScripts.mjs +2 -1
- package/dist/init/updateTypeScriptConfig.mjs +2 -2
- package/dist/initProgram.d.mts +2 -0
- package/dist/initProgram.mjs +21 -0
- package/dist/locateSegments.mjs +2 -2
- package/dist/new/addClassToSegmentCode.mjs +6 -2
- package/dist/new/index.d.mts +2 -2
- package/dist/new/index.mjs +13 -3
- package/dist/new/newModule.d.mts +6 -2
- package/dist/new/newModule.mjs +50 -26
- package/dist/new/newSegment.d.mts +3 -2
- package/dist/new/newSegment.mjs +5 -5
- package/dist/new/render.d.mts +3 -7
- package/dist/new/render.mjs +11 -7
- package/dist/postinstall.mjs +5 -3
- package/dist/types.d.mts +37 -2
- package/dist/utils/debounceWithArgs.mjs +1 -4
- package/dist/utils/formatLoggedSegmentName.mjs +1 -1
- package/dist/utils/getAvailablePort.mjs +3 -2
- package/dist/utils/getFileSystemEntryType.mjs +1 -1
- package/package.json +8 -6
- package/templates/controller.ejs +18 -17
- package/templates/service.ejs +24 -4
- package/templates/worker.ejs +24 -1
- package/dist/getProjectInfo/directoryExists.d.mts +0 -1
- package/dist/getProjectInfo/directoryExists.mjs +0 -10
- package/dist/watcher/index.d.mts +0 -6
- package/templates_old/MyThingController.c.only.template.ts +0 -32
- package/templates_old/MyThingController.c.template.ts +0 -34
- package/templates_old/MyThingService.s.template.ts +0 -18
- package/templates_old/controller.ejs +0 -85
- package/templates_old/service.ejs +0 -9
- package/templates_old/worker.ejs +0 -9
- package/templates_old/zod/MyThingController.c.only.template.ts +0 -32
- package/templates_old/zod/MyThingController.c.template.ts +0 -39
- package/templates_old/zod/MyThingService.s.template.ts +0 -18
- /package/dist/{watcher → dev}/ensureSchemaFiles.d.mts +0 -0
- /package/dist/{watcher → dev}/isMetadataEmpty.d.mts +0 -0
- /package/dist/{watcher → dev}/isMetadataEmpty.mjs +0 -0
package/README.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Description is coming soon.
|
|
1
|
+
Description is coming soon.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { VovkSchema } from 'vovk';
|
|
2
|
-
import { _VovkControllerSchema, _VovkWorkerSchema } from 'vovk/types';
|
|
2
|
+
import type { _VovkControllerSchema, _VovkWorkerSchema } from 'vovk/types';
|
|
3
3
|
interface HandlersDiff {
|
|
4
4
|
nameOfClass: string;
|
|
5
5
|
added: string[];
|
|
@@ -15,7 +15,7 @@ export interface DiffResult {
|
|
|
15
15
|
workers: WorkersOrControllersDiff;
|
|
16
16
|
controllers: WorkersOrControllersDiff;
|
|
17
17
|
}
|
|
18
|
-
export declare function diffHandlers<T extends _VovkWorkerSchema['
|
|
18
|
+
export declare function diffHandlers<T extends _VovkWorkerSchema['handlers'] | _VovkControllerSchema['handlers']>(oldHandlers: T, newHandlers: T, nameOfClass: string): HandlersDiff;
|
|
19
19
|
export declare function diffWorkersOrControllers<T extends VovkSchema['controllers'] | VovkSchema['workers']>(oldItems: T, newItems: T): WorkersOrControllersDiff;
|
|
20
20
|
/**
|
|
21
21
|
example output:
|
|
@@ -27,7 +27,7 @@ export function diffWorkersOrControllers(oldItems, newItems) {
|
|
|
27
27
|
added.push(item);
|
|
28
28
|
}
|
|
29
29
|
else {
|
|
30
|
-
const handlers = diffHandlers(oldItems[item].
|
|
30
|
+
const handlers = diffHandlers(oldItems[item].handlers, newItem.handlers, item);
|
|
31
31
|
if (handlers.added.length || handlers.removed.length || handlers.changed.length) {
|
|
32
32
|
handlersDiff.push(handlers);
|
|
33
33
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
export default async function ensureClient(projectInfo) {
|
|
4
|
+
const { config, cwd, log } = projectInfo;
|
|
5
|
+
const now = Date.now();
|
|
6
|
+
const clientoOutDirAbsolutePath = path.join(cwd, config.clientOutDir);
|
|
7
|
+
const dts = `// auto-generated
|
|
8
|
+
// This is a temporary placeholder to avoid errors if client is imported before it's generated.
|
|
9
|
+
// If you still see this text, the client is not generated yet because of an unknown problem.
|
|
10
|
+
// Feel free to report an issue at https://github.com/finom/vovk/issues`;
|
|
11
|
+
const js = dts;
|
|
12
|
+
const ts = dts;
|
|
13
|
+
const localJsAbsolutePath = path.join(clientoOutDirAbsolutePath, 'client.js');
|
|
14
|
+
const localDtsAbsolutePath = path.join(clientoOutDirAbsolutePath, 'client.d.ts');
|
|
15
|
+
const localTsAbsolutePath = path.join(clientoOutDirAbsolutePath, 'index.ts');
|
|
16
|
+
const existingJs = await fs.readFile(localJsAbsolutePath, 'utf-8').catch(() => null);
|
|
17
|
+
const existingDts = await fs.readFile(localDtsAbsolutePath, 'utf-8').catch(() => null);
|
|
18
|
+
const existingTs = await fs.readFile(localTsAbsolutePath, 'utf-8').catch(() => null);
|
|
19
|
+
if (existingJs && existingDts && existingTs) {
|
|
20
|
+
return { written: false, path: clientoOutDirAbsolutePath };
|
|
21
|
+
}
|
|
22
|
+
await fs.mkdir(clientoOutDirAbsolutePath, { recursive: true });
|
|
23
|
+
if (!existingJs)
|
|
24
|
+
await fs.writeFile(localJsAbsolutePath, js);
|
|
25
|
+
if (!existingDts)
|
|
26
|
+
await fs.writeFile(localDtsAbsolutePath, dts);
|
|
27
|
+
if (!existingTs)
|
|
28
|
+
await fs.writeFile(localTsAbsolutePath, ts);
|
|
29
|
+
log.info(`Empty client files are generated in ${Date.now() - now}ms`);
|
|
30
|
+
return { written: true, path: clientoOutDirAbsolutePath };
|
|
31
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
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';
|
|
@@ -7,17 +7,29 @@ export default async function ensureSchemaFiles(projectInfo, schemaOutAbsolutePa
|
|
|
7
7
|
const now = Date.now();
|
|
8
8
|
let hasChanged = false;
|
|
9
9
|
// Create index.js file
|
|
10
|
-
const indexContent =
|
|
10
|
+
const indexContent = `// auto-generated
|
|
11
|
+
${segmentNames
|
|
11
12
|
.map((segmentName) => {
|
|
12
13
|
return `module.exports['${segmentName}'] = require('./${segmentName || ROOT_SEGMENT_SCHEMA_NAME}.json');`;
|
|
13
14
|
})
|
|
14
|
-
.join('\n')
|
|
15
|
-
const dTsContent =
|
|
16
|
-
|
|
15
|
+
.join('\n')}`;
|
|
16
|
+
const dTsContent = `// auto-generated
|
|
17
|
+
import type { VovkSchema } from 'vovk';
|
|
18
|
+
declare const segmentSchema: {
|
|
19
|
+
${segmentNames.map((segmentName) => ` '${segmentName}': VovkSchema;`).join('\n')}
|
|
20
|
+
};
|
|
17
21
|
export default segmentSchema;`;
|
|
22
|
+
const jsAbsolutePath = path.join(schemaOutAbsolutePath, 'index.js');
|
|
23
|
+
const dTsAbsolutePath = path.join(schemaOutAbsolutePath, 'index.d.ts');
|
|
24
|
+
const existingJs = await fs.readFile(jsAbsolutePath, 'utf-8').catch(() => null);
|
|
25
|
+
const existingDTs = await fs.readFile(dTsAbsolutePath, 'utf-8').catch(() => null);
|
|
18
26
|
await fs.mkdir(schemaOutAbsolutePath, { recursive: true });
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
if (existingJs !== indexContent) {
|
|
28
|
+
await fs.writeFile(jsAbsolutePath, indexContent);
|
|
29
|
+
}
|
|
30
|
+
if (existingDTs !== dTsContent) {
|
|
31
|
+
await fs.writeFile(dTsAbsolutePath, dTsContent);
|
|
32
|
+
}
|
|
21
33
|
// Create JSON files (if not exist) with name [segmentName].json (where segmentName can include /, which means the folder structure can be nested)
|
|
22
34
|
await Promise.all(segmentNames.map(async (segmentName) => {
|
|
23
35
|
const { isCreated } = await writeOneSchemaFile({
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
1
3
|
import * as chokidar from 'chokidar';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
4
|
+
import { Agent, setGlobalDispatcher } from 'undici';
|
|
5
|
+
import keyBy from 'lodash/keyBy.js';
|
|
6
|
+
import capitalize from 'lodash/capitalize.js';
|
|
7
|
+
import debounce from 'lodash/debounce.js';
|
|
8
|
+
import isEmpty from 'lodash/isEmpty.js';
|
|
5
9
|
import { debouncedEnsureSchemaFiles } from './ensureSchemaFiles.mjs';
|
|
6
10
|
import writeOneSchemaFile from './writeOneSchemaFile.mjs';
|
|
7
11
|
import logDiffResult from './logDiffResult.mjs';
|
|
12
|
+
import ensureClient from './ensureClient.mjs';
|
|
13
|
+
import getProjectInfo from '../getProjectInfo/index.mjs';
|
|
8
14
|
import generateClient from '../generateClient.mjs';
|
|
9
15
|
import locateSegments from '../locateSegments.mjs';
|
|
10
16
|
import debounceWithArgs from '../utils/debounceWithArgs.mjs';
|
|
11
|
-
import debounce from 'lodash/debounce.js';
|
|
12
|
-
import isEmpty from 'lodash/isEmpty.js';
|
|
13
17
|
import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
|
|
14
|
-
|
|
15
|
-
import capitalize from 'lodash/capitalize.js';
|
|
16
|
-
import { Agent, setGlobalDispatcher } from 'undici';
|
|
17
|
-
export class VovkCLIWatcher {
|
|
18
|
+
export class VovkDev {
|
|
18
19
|
#projectInfo;
|
|
19
20
|
#segments = [];
|
|
20
21
|
#schemas = {};
|
|
@@ -27,7 +28,7 @@ export class VovkCLIWatcher {
|
|
|
27
28
|
const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
|
|
28
29
|
const apiDirAbsolutePath = path.join(cwd, apiDir);
|
|
29
30
|
const getSegmentName = (filePath) => path.relative(apiDirAbsolutePath, filePath).replace(segmentReg, '');
|
|
30
|
-
log.debug(`Watching segments
|
|
31
|
+
log.debug(`Watching segments at ${apiDirAbsolutePath}`);
|
|
31
32
|
this.#segmentWatcher = chokidar
|
|
32
33
|
.watch(apiDirAbsolutePath, {
|
|
33
34
|
persistent: true,
|
|
@@ -51,7 +52,6 @@ export class VovkCLIWatcher {
|
|
|
51
52
|
void this.#requestSchema(getSegmentName(filePath));
|
|
52
53
|
}
|
|
53
54
|
})
|
|
54
|
-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
55
55
|
.on('addDir', async (dirPath) => {
|
|
56
56
|
log.debug(`Directory ${dirPath} has been added to segments folder`);
|
|
57
57
|
this.#segments = await locateSegments(apiDirAbsolutePath);
|
|
@@ -59,7 +59,6 @@ export class VovkCLIWatcher {
|
|
|
59
59
|
void this.#requestSchema(segmentName);
|
|
60
60
|
}
|
|
61
61
|
})
|
|
62
|
-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
63
62
|
.on('unlinkDir', async (dirPath) => {
|
|
64
63
|
log.debug(`Directory ${dirPath} has been removed from segments folder`);
|
|
65
64
|
this.#segments = await locateSegments(apiDirAbsolutePath);
|
|
@@ -81,13 +80,13 @@ export class VovkCLIWatcher {
|
|
|
81
80
|
log.debug('Segments watcher is ready');
|
|
82
81
|
})
|
|
83
82
|
.on('error', (error) => {
|
|
84
|
-
log.error(`Error watching segments folder: ${error
|
|
83
|
+
log.error(`Error watching segments folder: ${error?.message ?? 'Unknown error'}`);
|
|
85
84
|
});
|
|
86
85
|
};
|
|
87
86
|
#watchModules = () => {
|
|
88
87
|
const { config, cwd, log } = this.#projectInfo;
|
|
89
88
|
const modulesDirAbsolutePath = path.join(cwd, config.modulesDir);
|
|
90
|
-
log.debug(`Watching modules
|
|
89
|
+
log.debug(`Watching modules at ${modulesDirAbsolutePath}`);
|
|
91
90
|
const processControllerChange = debounceWithArgs(this.#processControllerChange, 500);
|
|
92
91
|
this.#modulesWatcher = chokidar
|
|
93
92
|
.watch(modulesDirAbsolutePath, {
|
|
@@ -119,13 +118,14 @@ export class VovkCLIWatcher {
|
|
|
119
118
|
log.debug('Modules watcher is ready');
|
|
120
119
|
})
|
|
121
120
|
.on('error', (error) => {
|
|
122
|
-
log.error(`Error watching modules folder: ${error
|
|
121
|
+
log.error(`Error watching modules folder: ${error?.message ?? 'Unknown error'}`);
|
|
123
122
|
});
|
|
124
123
|
};
|
|
125
124
|
#watchConfig = () => {
|
|
126
125
|
const { log, cwd } = this.#projectInfo;
|
|
127
126
|
log.debug(`Watching config files`);
|
|
128
127
|
let isInitial = true;
|
|
128
|
+
let isReady = false;
|
|
129
129
|
const handle = debounce(async () => {
|
|
130
130
|
this.#projectInfo = await getProjectInfo();
|
|
131
131
|
if (!isInitial) {
|
|
@@ -137,7 +137,6 @@ export class VovkCLIWatcher {
|
|
|
137
137
|
this.#watchModules();
|
|
138
138
|
this.#watchSegments();
|
|
139
139
|
}, 1000);
|
|
140
|
-
let ready = false;
|
|
141
140
|
chokidar
|
|
142
141
|
.watch(['vovk.config.{js,mjs,cjs}', '.config/vovk.config.{js,mjs,cjs}'], {
|
|
143
142
|
persistent: true,
|
|
@@ -149,22 +148,21 @@ export class VovkCLIWatcher {
|
|
|
149
148
|
.on('change', () => void handle())
|
|
150
149
|
.on('unlink', () => void handle())
|
|
151
150
|
.on('ready', () => {
|
|
152
|
-
if (
|
|
151
|
+
if (isReady)
|
|
153
152
|
return;
|
|
154
153
|
// for some reason this watcher triggers ready event twice
|
|
155
154
|
log.debug('Config files watcher is ready');
|
|
156
|
-
|
|
155
|
+
isReady = true;
|
|
157
156
|
})
|
|
158
|
-
.on('error', (error) => {
|
|
159
|
-
log.error(`Error watching config files: ${error.message}`);
|
|
160
|
-
});
|
|
157
|
+
.on('error', (error) => log.error(`Error watching config files: ${error?.message ?? 'Unknown error'}`));
|
|
161
158
|
void handle();
|
|
162
159
|
};
|
|
163
|
-
#watch() {
|
|
160
|
+
async #watch() {
|
|
164
161
|
if (this.#isWatching)
|
|
165
162
|
throw new Error('Already watching');
|
|
166
163
|
const { log } = this.#projectInfo;
|
|
167
164
|
log.debug(`Starting segments and modules watcher. Detected initial segments: ${JSON.stringify(this.#segments.map((s) => s.segmentName))}.`);
|
|
165
|
+
await ensureClient(this.#projectInfo);
|
|
168
166
|
// automatically watches segments and modules
|
|
169
167
|
this.#watchConfig();
|
|
170
168
|
}
|
|
@@ -183,8 +181,8 @@ export class VovkCLIWatcher {
|
|
|
183
181
|
const schema = this.#schemas[s.segmentName];
|
|
184
182
|
if (!schema)
|
|
185
183
|
return false;
|
|
186
|
-
const controllersByOriginalName = keyBy(schema.controllers, '
|
|
187
|
-
const workersByOriginalName = keyBy(schema.workers, '
|
|
184
|
+
const controllersByOriginalName = keyBy(schema.controllers, 'originalControllerName');
|
|
185
|
+
const workersByOriginalName = keyBy(schema.workers, 'originalWorkerName');
|
|
188
186
|
return namesOfClasses.some((name) => schema.controllers[name] ||
|
|
189
187
|
schema.workers[name] ||
|
|
190
188
|
controllersByOriginalName[name] ||
|
|
@@ -196,6 +194,9 @@ export class VovkCLIWatcher {
|
|
|
196
194
|
await this.#requestSchema(segment.segmentName);
|
|
197
195
|
}
|
|
198
196
|
}
|
|
197
|
+
else {
|
|
198
|
+
log.debug(`The class ${namesOfClasses.join(', ')} does not belong to any segment`);
|
|
199
|
+
}
|
|
199
200
|
}
|
|
200
201
|
else {
|
|
201
202
|
log.debug(`The file does not contain any controller or worker`);
|
|
@@ -210,7 +211,6 @@ export class VovkCLIWatcher {
|
|
|
210
211
|
if (resp.status !== 200) {
|
|
211
212
|
const probableCause = {
|
|
212
213
|
404: 'The segment is not compiled.',
|
|
213
|
-
500: 'Syntax error in one of controllers.',
|
|
214
214
|
}[resp.status];
|
|
215
215
|
log.warn(`Schema request to ${formatLoggedSegmentName(segmentName)} failed with status code ${resp.status} but expected 200.${probableCause ? ` Probable cause: ${probableCause}` : ''}`);
|
|
216
216
|
return;
|
|
@@ -259,8 +259,8 @@ export class VovkCLIWatcher {
|
|
|
259
259
|
await generateClient(this.#projectInfo, this.#segments, this.#schemas);
|
|
260
260
|
}
|
|
261
261
|
}
|
|
262
|
-
async start(
|
|
263
|
-
this.#projectInfo = await getProjectInfo(
|
|
262
|
+
async start() {
|
|
263
|
+
this.#projectInfo = await getProjectInfo();
|
|
264
264
|
const { log, config, cwd, apiDir } = this.#projectInfo;
|
|
265
265
|
if (config.devHttps) {
|
|
266
266
|
const agent = new Agent({
|
|
@@ -280,16 +280,16 @@ export class VovkCLIWatcher {
|
|
|
280
280
|
const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
|
|
281
281
|
this.#segments = await locateSegments(apiDirAbsolutePath);
|
|
282
282
|
await debouncedEnsureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, this.#segments.map((s) => s.segmentName));
|
|
283
|
-
// Request schema every segment in
|
|
283
|
+
// Request schema every segment in 5 seconds in order to update schema and start watching
|
|
284
284
|
setTimeout(() => {
|
|
285
285
|
for (const { segmentName } of this.#segments) {
|
|
286
286
|
void this.#requestSchema(segmentName);
|
|
287
287
|
}
|
|
288
288
|
this.#watch();
|
|
289
|
-
},
|
|
289
|
+
}, 5000);
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
292
|
const env = process.env;
|
|
293
293
|
if (env.__VOVK_START_WATCHER_IN_STANDALONE_MODE__ === 'true') {
|
|
294
|
-
void new
|
|
294
|
+
void new VovkDev().start();
|
|
295
295
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { DiffResult } from './diffSchema.mjs';
|
|
2
|
+
import type { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
3
3
|
export default function logDiffResult(segmentName: string, diffResult: DiffResult, projectInfo: ProjectInfo): void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
1
2
|
import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
|
|
2
3
|
import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
|
|
3
4
|
export default function logDiffResult(segmentName, diffResult, projectInfo) {
|
|
@@ -37,48 +38,51 @@ export default function logDiffResult(segmentName, diffResult, projectInfo) {
|
|
|
37
38
|
});
|
|
38
39
|
});
|
|
39
40
|
const LIMIT = diffNormalized.length < 12 ? diffNormalized.length : 10;
|
|
41
|
+
const addedText = chalk.green('added');
|
|
42
|
+
const removedText = chalk.red('removed');
|
|
43
|
+
const changedText = chalk.cyan('changed');
|
|
40
44
|
for (const diffNormalizedItem of diffNormalized.slice(0, LIMIT)) {
|
|
41
45
|
switch (diffNormalizedItem.what) {
|
|
42
46
|
case 'worker':
|
|
43
47
|
switch (diffNormalizedItem.type) {
|
|
44
48
|
case 'added':
|
|
45
|
-
projectInfo.log.info(`Schema for worker ${chalkHighlightThing(diffNormalizedItem.name)} has been
|
|
49
|
+
projectInfo.log.info(`Schema for worker ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
46
50
|
break;
|
|
47
51
|
case 'removed':
|
|
48
|
-
projectInfo.log.info(`Schema for worker ${chalkHighlightThing(diffNormalizedItem.name)} has been
|
|
52
|
+
projectInfo.log.info(`Schema for worker ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
49
53
|
break;
|
|
50
54
|
}
|
|
51
55
|
break;
|
|
52
56
|
case 'controller':
|
|
53
57
|
switch (diffNormalizedItem.type) {
|
|
54
58
|
case 'added':
|
|
55
|
-
projectInfo.log.info(`Schema for controller ${chalkHighlightThing(diffNormalizedItem.name)} has been
|
|
59
|
+
projectInfo.log.info(`Schema for controller ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
56
60
|
break;
|
|
57
61
|
case 'removed':
|
|
58
|
-
projectInfo.log.info(`Schema for controller ${chalkHighlightThing(diffNormalizedItem.name)} has been
|
|
62
|
+
projectInfo.log.info(`Schema for controller ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
59
63
|
break;
|
|
60
64
|
}
|
|
61
65
|
break;
|
|
62
66
|
case 'workerHandler':
|
|
63
67
|
switch (diffNormalizedItem.type) {
|
|
64
68
|
case 'added':
|
|
65
|
-
projectInfo.log.info(`Schema for worker method ${chalkHighlightThing(diffNormalizedItem.name)} has been
|
|
69
|
+
projectInfo.log.info(`Schema for worker method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
66
70
|
break;
|
|
67
71
|
case 'removed':
|
|
68
|
-
projectInfo.log.info(`Schema for worker method ${chalkHighlightThing(diffNormalizedItem.name)} has been
|
|
72
|
+
projectInfo.log.info(`Schema for worker method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
69
73
|
break;
|
|
70
74
|
}
|
|
71
75
|
break;
|
|
72
76
|
case 'controllerHandler':
|
|
73
77
|
switch (diffNormalizedItem.type) {
|
|
74
78
|
case 'added':
|
|
75
|
-
projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been
|
|
79
|
+
projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
76
80
|
break;
|
|
77
81
|
case 'removed':
|
|
78
|
-
projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been
|
|
82
|
+
projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
|
|
79
83
|
break;
|
|
80
84
|
case 'changed':
|
|
81
|
-
projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been
|
|
85
|
+
projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${changedText} at ${formatLoggedSegmentName(segmentName)}`);
|
|
82
86
|
break;
|
|
83
87
|
}
|
|
84
88
|
break;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { VovkSchema } from 'vovk';
|
|
2
|
-
import { DiffResult } from './diffSchema.mjs';
|
|
2
|
+
import { type DiffResult } from './diffSchema.mjs';
|
|
3
3
|
export declare const ROOT_SEGMENT_SCHEMA_NAME = "_root";
|
|
4
4
|
export default function writeOneSchemaFile({ schemaOutAbsolutePath, schema, skipIfExists, }: {
|
|
5
5
|
schemaOutAbsolutePath: string;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs/promises';
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
3
|
import diffSchema from './diffSchema.mjs';
|
|
4
|
+
import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
|
|
4
5
|
export const ROOT_SEGMENT_SCHEMA_NAME = '_root';
|
|
5
6
|
export default async function writeOneSchemaFile({ schemaOutAbsolutePath, schema, skipIfExists = false, }) {
|
|
6
7
|
const segmentPath = path.join(schemaOutAbsolutePath, `${schema.segmentName || ROOT_SEGMENT_SCHEMA_NAME}.json`);
|
|
7
|
-
if (skipIfExists) {
|
|
8
|
+
if (skipIfExists && (await getFileSystemEntryType(segmentPath))) {
|
|
8
9
|
try {
|
|
9
10
|
await fs.stat(segmentPath);
|
|
10
11
|
return { isCreated: false, diffResult: null };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { VovkSchema } from 'vovk';
|
|
1
2
|
import type { ProjectInfo } from './getProjectInfo/index.mjs';
|
|
2
3
|
import type { Segment } from './locateSegments.mjs';
|
|
3
|
-
import type { VovkSchema } from 'vovk';
|
|
4
4
|
export default function generateClient(projectInfo: ProjectInfo, segments: Segment[], segmentsSchema: Record<string, VovkSchema>): Promise<{
|
|
5
5
|
written: boolean;
|
|
6
6
|
path: string;
|
package/dist/generateClient.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs/promises';
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
3
|
import formatLoggedSegmentName from './utils/formatLoggedSegmentName.mjs';
|
|
4
4
|
import prettify from './utils/prettify.mjs';
|
|
5
5
|
export default async function generateClient(projectInfo, segments, segmentsSchema) {
|
|
@@ -48,11 +48,11 @@ type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
|
|
|
48
48
|
ts += `
|
|
49
49
|
${validateOnClientImportPath ? `import validateOnClient from '${validateOnClientImportPath}';\n` : '\nconst validateOnClient = undefined;'}
|
|
50
50
|
type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
|
|
51
|
-
const
|
|
51
|
+
const apiRoot = '${apiEntryPoint}';
|
|
52
52
|
`;
|
|
53
53
|
js += `
|
|
54
54
|
const { default: validateOnClient = null } = ${validateOnClientImportPath ? `require('${validateOnClientImportPath}')` : '{}'};
|
|
55
|
-
const
|
|
55
|
+
const apiRoot = '${apiEntryPoint}';
|
|
56
56
|
`;
|
|
57
57
|
for (let i = 0; i < segments.length; i++) {
|
|
58
58
|
const { segmentName } = segments[i];
|
|
@@ -64,8 +64,8 @@ const prefix = '${apiEntryPoint}';
|
|
|
64
64
|
continue;
|
|
65
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(schema['${segmentName}'].controllers.${key}, '${segmentName}', { fetcher, validateOnClient, defaultOptions: {
|
|
68
|
-
ts += `export const ${key} = clientizeController<Controllers${i}["${key}"], Options>(schema['${segmentName}'].controllers.${key}, '${segmentName}', { fetcher, validateOnClient, defaultOptions: {
|
|
67
|
+
js += `exports.${key} = clientizeController(schema['${segmentName}'].controllers.${key}, '${segmentName}', { fetcher, validateOnClient, defaultOptions: { apiRoot } });\n`;
|
|
68
|
+
ts += `export const ${key} = clientizeController<Controllers${i}["${key}"], Options>(schema['${segmentName}'].controllers.${key}, '${segmentName}', { fetcher, validateOnClient, defaultOptions: { apiRoot } });\n`;
|
|
69
69
|
}
|
|
70
70
|
for (const key of Object.keys(schema.workers)) {
|
|
71
71
|
dts += `export const ${key}: ReturnType<typeof promisifyWorker<Workers${i}["${key}"]>>;\n`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
export default async function getConfigAbsolutePaths({ cwd, relativePath, }) {
|
|
4
4
|
const rootDir = path.resolve(cwd, relativePath || '');
|
|
5
5
|
const baseName = 'vovk.config';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import getFileSystemEntryType, { FileSystemEntryType } from '../utils/getFileSystemEntryType.mjs';
|
|
3
3
|
export default async function getRelativeSrcRoot({ cwd }) {
|
|
4
4
|
// Next.js Docs: src/app or src/pages will be ignored if app or pages are present in the root directory.
|
|
5
|
-
if (await
|
|
5
|
+
if ((await getFileSystemEntryType(path.join(cwd, 'app'))) === FileSystemEntryType.DIRECTORY) {
|
|
6
6
|
return '.';
|
|
7
7
|
}
|
|
8
|
-
else if (await
|
|
8
|
+
else if ((await getFileSystemEntryType(path.join(cwd, 'src/app'))) === FileSystemEntryType.DIRECTORY) {
|
|
9
9
|
return './src';
|
|
10
10
|
}
|
|
11
11
|
throw new Error(`${cwd} Could not find app router directory. Check Next.js docs for more info.`);
|
|
@@ -11,7 +11,7 @@ async function getUserConfig({ cwd, }) {
|
|
|
11
11
|
try {
|
|
12
12
|
userConfig = await importUncachedModule(configPath);
|
|
13
13
|
}
|
|
14
|
-
catch
|
|
14
|
+
catch {
|
|
15
15
|
try {
|
|
16
16
|
const cacheBuster = Date.now();
|
|
17
17
|
({ default: userConfig } = (await import(`${configPath}?cache=${cacheBuster}`)));
|
package/dist/index.d.mts
CHANGED
|
@@ -1,26 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import type { LogLevelNames } from 'loglevel';
|
|
4
|
-
import type { VovkConfig, VovkEnv } from './types.mjs';
|
|
2
|
+
import type { VovkConfig, VovkDevEnv } from './types.mjs';
|
|
5
3
|
import 'dotenv/config';
|
|
6
|
-
export type { VovkConfig,
|
|
7
|
-
export interface InitOptions {
|
|
8
|
-
yes?: boolean;
|
|
9
|
-
logLevel: LogLevelNames;
|
|
10
|
-
useNpm?: boolean;
|
|
11
|
-
useYarn?: boolean;
|
|
12
|
-
usePnpm?: boolean;
|
|
13
|
-
useBun?: boolean;
|
|
14
|
-
skipInstall?: boolean;
|
|
15
|
-
updateTsConfig?: boolean;
|
|
16
|
-
updateScripts?: 'implicit' | 'explicit';
|
|
17
|
-
validationLibrary?: string | null;
|
|
18
|
-
validateOnClient?: boolean;
|
|
19
|
-
dryRun?: boolean;
|
|
20
|
-
channel?: 'latest' | 'beta' | 'dev';
|
|
21
|
-
}
|
|
22
|
-
export interface NewOptions {
|
|
23
|
-
dryRun: boolean;
|
|
24
|
-
}
|
|
25
|
-
declare const program: Command;
|
|
26
|
-
export declare function initProgram(p: typeof program, command: string): Command;
|
|
4
|
+
export type { VovkConfig, VovkDevEnv };
|
package/dist/index.mjs
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import path from 'path';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
3
4
|
import { Command } from 'commander';
|
|
4
|
-
import { readFileSync } from 'fs';
|
|
5
5
|
import concurrently from 'concurrently';
|
|
6
6
|
import getAvailablePort from './utils/getAvailablePort.mjs';
|
|
7
7
|
import getProjectInfo from './getProjectInfo/index.mjs';
|
|
8
8
|
import generateClient from './generateClient.mjs';
|
|
9
9
|
import locateSegments from './locateSegments.mjs';
|
|
10
|
-
import {
|
|
11
|
-
import { Init } from './init/index.mjs';
|
|
10
|
+
import { VovkDev } from './dev/index.mjs';
|
|
12
11
|
import newComponents from './new/index.mjs';
|
|
13
12
|
import 'dotenv/config';
|
|
13
|
+
import initProgram from './initProgram.mjs';
|
|
14
14
|
const program = new Command();
|
|
15
15
|
const packageJSON = JSON.parse(readFileSync(path.join(import.meta.dirname, '../package.json'), 'utf-8'));
|
|
16
16
|
program.name('vovk').description('Vovk CLI').version(packageJSON.version);
|
|
17
|
+
initProgram(program.command('init'));
|
|
17
18
|
program
|
|
18
19
|
.command('dev')
|
|
19
20
|
.description('Start schema watcher (optional flag --next-dev to start it with Next.js)')
|
|
20
|
-
.option('--client-out <path>', 'Path to client output directory')
|
|
21
21
|
.option('--next-dev', 'Start schema watcher and Next.js with automatic port allocation', false)
|
|
22
22
|
.allowUnknownOption(true)
|
|
23
23
|
.action(async (options, command) => {
|
|
@@ -35,16 +35,16 @@ program
|
|
|
35
35
|
}
|
|
36
36
|
if (options.nextDev) {
|
|
37
37
|
const { result } = concurrently([
|
|
38
|
-
{
|
|
39
|
-
command: `node ${import.meta.dirname}/watcher/index.mjs`,
|
|
40
|
-
name: 'Vovk.ts Schema Watcher',
|
|
41
|
-
env: Object.assign({ PORT, __VOVK_START_WATCHER_IN_STANDALONE_MODE__: 'true' }, options.clientOut ? { VOVK_CLIENT_OUT_DIR: options.clientOut } : {}),
|
|
42
|
-
},
|
|
43
38
|
{
|
|
44
39
|
command: `npx next dev ${command.args.join(' ')}`,
|
|
45
40
|
name: 'Next.js Development Server',
|
|
46
41
|
env: { PORT },
|
|
47
42
|
},
|
|
43
|
+
{
|
|
44
|
+
command: `node ${import.meta.dirname}/dev/index.mjs`,
|
|
45
|
+
name: 'Vovk Dev Command',
|
|
46
|
+
env: { PORT, __VOVK_START_WATCHER_IN_STANDALONE_MODE__: 'true' },
|
|
47
|
+
},
|
|
48
48
|
], {
|
|
49
49
|
killOthers: ['failure', 'success'],
|
|
50
50
|
prefix: 'none',
|
|
@@ -57,7 +57,7 @@ program
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
else {
|
|
60
|
-
void new
|
|
60
|
+
void new VovkDev().start();
|
|
61
61
|
}
|
|
62
62
|
});
|
|
63
63
|
program
|
|
@@ -72,46 +72,20 @@ program
|
|
|
72
72
|
const schema = (await import(path.join(schemaOutAbsolutePath, 'index.js')));
|
|
73
73
|
await generateClient(projectInfo, segments, schema.default);
|
|
74
74
|
});
|
|
75
|
-
// reused at vovk-init
|
|
76
|
-
export function initProgram(p, command) {
|
|
77
|
-
return p
|
|
78
|
-
.command(command + '[prefix]')
|
|
79
|
-
.description('Initialize Vovk project')
|
|
80
|
-
.option('-Y, --yes', 'Skip all prompts and use default values')
|
|
81
|
-
.option('--log-level <level>', 'Set log level', 'info')
|
|
82
|
-
.option('--use-npm', 'Use npm as package manager')
|
|
83
|
-
.option('--use-yarn', 'Use yarn as package manager')
|
|
84
|
-
.option('--use-pnpm', 'Use pnpm as package manager')
|
|
85
|
-
.option('--use-bun', 'Use bun as package manager')
|
|
86
|
-
.option('--skip-install', 'Skip installing dependencies')
|
|
87
|
-
.option('--update-ts-config', 'Update tsconfig.json')
|
|
88
|
-
.option('--update-scripts <mode>', 'Update package.json scripts (implicit or explicit)')
|
|
89
|
-
.option('--validation-library <library>', 'Validation library to use ("vovk-zod", "vovk-yup", "vovk-dto" or another). Set to "none" to skip validation')
|
|
90
|
-
.option('--validate-on-client', 'Validate on client')
|
|
91
|
-
.option('--dry-run', 'Do not write files to disk')
|
|
92
|
-
.option('--channel <channel>', 'Channel to use for fetching packages', 'latest')
|
|
93
|
-
.action((prefix = '.', options) => new Init().main(prefix, options));
|
|
94
|
-
}
|
|
95
|
-
initProgram(program, 'init ');
|
|
96
75
|
program
|
|
97
76
|
.command('new [components...]')
|
|
98
77
|
.alias('n')
|
|
99
78
|
.description('Create new components. "vovk new [...components] [segmentName/]moduleName" to create a new module or "vovk new segment [segmentName]" to create a new segment')
|
|
79
|
+
.option('-o, --overwrite', 'Overwrite existing files')
|
|
80
|
+
.option('--template, --templates <templates...>', 'Override config template. Accepts an array of strings that correspond the order of the components')
|
|
81
|
+
.option('--dir <dirname>', 'Override dirName in template file. Relative to the root of the project')
|
|
82
|
+
.option('--no-segment-update', 'Do not update segment files when creating a new module')
|
|
100
83
|
.option('--dry-run', 'Do not write files to disk')
|
|
101
84
|
.action((components, options) => newComponents(components, options));
|
|
102
85
|
program
|
|
103
86
|
.command('help')
|
|
104
87
|
.description('Show help message')
|
|
105
88
|
.action(() => program.help());
|
|
106
|
-
/*
|
|
107
|
-
TODO
|
|
108
|
-
vovk new segment [segmentName]
|
|
109
|
-
vovk new controller service [segmentName/]moduleName
|
|
110
|
-
vovk new c s w [segmentName/]moduleName
|
|
111
|
-
|
|
112
|
-
vovk c s w userApi/user
|
|
113
|
-
vovk new c s w user
|
|
114
|
-
*/
|
|
115
89
|
program.parse(process.argv);
|
|
116
90
|
if (!process.argv.slice(2).length) {
|
|
117
91
|
program.outputHelp();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs/promises';
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
3
|
import * as jsonc from 'jsonc-parser';
|
|
4
4
|
export default async function checkTSConfigForExperimentalDecorators(root) {
|
|
5
5
|
const tsconfigPath = path.resolve(root, 'tsconfig.json');
|