vovk-cli 0.0.1-draft.36 → 0.0.1-draft.38
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/dev/index.mjs +61 -26
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/init/index.mjs +11 -9
- package/dist/init/updateNPMScripts.d.mts +3 -1
- package/dist/init/updateNPMScripts.mjs +9 -6
- package/dist/new/newSegment.mjs +1 -1
- package/dist/types.d.mts +1 -1
- package/dist/utils/debounceWithArgs.d.mts +1 -1
- package/dist/utils/debounceWithArgs.mjs +24 -6
- package/package.json +1 -1
package/dist/dev/index.mjs
CHANGED
|
@@ -22,7 +22,7 @@ export class VovkDev {
|
|
|
22
22
|
#isWatching = false;
|
|
23
23
|
#modulesWatcher = null;
|
|
24
24
|
#segmentWatcher = null;
|
|
25
|
-
#watchSegments = () => {
|
|
25
|
+
#watchSegments = (callback) => {
|
|
26
26
|
const segmentReg = /\/?\[\[\.\.\.[a-zA-Z-_]+\]\]\/route.ts$/;
|
|
27
27
|
const { cwd, log, config, apiDir } = this.#projectInfo;
|
|
28
28
|
const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
|
|
@@ -77,13 +77,14 @@ export class VovkDev {
|
|
|
77
77
|
}
|
|
78
78
|
})
|
|
79
79
|
.on('ready', () => {
|
|
80
|
+
callback();
|
|
80
81
|
log.debug('Segments watcher is ready');
|
|
81
82
|
})
|
|
82
83
|
.on('error', (error) => {
|
|
83
84
|
log.error(`Error watching segments folder: ${error?.message ?? 'Unknown error'}`);
|
|
84
85
|
});
|
|
85
86
|
};
|
|
86
|
-
#watchModules = () => {
|
|
87
|
+
#watchModules = (callback) => {
|
|
87
88
|
const { config, cwd, log } = this.#projectInfo;
|
|
88
89
|
const modulesDirAbsolutePath = path.join(cwd, config.modulesDir);
|
|
89
90
|
log.debug(`Watching modules at ${modulesDirAbsolutePath}`);
|
|
@@ -115,30 +116,37 @@ export class VovkDev {
|
|
|
115
116
|
}
|
|
116
117
|
})
|
|
117
118
|
.on('ready', () => {
|
|
119
|
+
callback();
|
|
118
120
|
log.debug('Modules watcher is ready');
|
|
119
121
|
})
|
|
120
122
|
.on('error', (error) => {
|
|
121
123
|
log.error(`Error watching modules folder: ${error?.message ?? 'Unknown error'}`);
|
|
122
124
|
});
|
|
123
125
|
};
|
|
124
|
-
#watchConfig = () => {
|
|
126
|
+
#watchConfig = (callback) => {
|
|
125
127
|
const { log, cwd } = this.#projectInfo;
|
|
126
128
|
log.debug(`Watching config files`);
|
|
127
129
|
let isInitial = true;
|
|
128
130
|
let isReady = false;
|
|
129
131
|
const handle = debounce(async () => {
|
|
130
132
|
this.#projectInfo = await getProjectInfo();
|
|
131
|
-
|
|
133
|
+
await this.#modulesWatcher?.close();
|
|
134
|
+
await this.#segmentWatcher?.close();
|
|
135
|
+
await Promise.all([
|
|
136
|
+
new Promise((resolve) => this.#watchModules(() => resolve(0))),
|
|
137
|
+
new Promise((resolve) => this.#watchSegments(() => resolve(0)))
|
|
138
|
+
]);
|
|
139
|
+
if (isInitial) {
|
|
140
|
+
callback();
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
132
143
|
log.info('Config file has been updated');
|
|
133
144
|
}
|
|
134
145
|
isInitial = false;
|
|
135
|
-
await this.#modulesWatcher?.close();
|
|
136
|
-
await this.#segmentWatcher?.close();
|
|
137
|
-
this.#watchModules();
|
|
138
|
-
this.#watchSegments();
|
|
139
146
|
}, 1000);
|
|
140
147
|
chokidar
|
|
141
|
-
.watch(['vovk.config.{js,mjs,cjs}', '.config/vovk.config.{js,mjs,cjs}'], {
|
|
148
|
+
// .watch(['vovk.config.{js,mjs,cjs}', '.config/vovk.config.{js,mjs,cjs}'], {
|
|
149
|
+
.watch(['vovk.config.js', 'vovk.config.mjs', 'vovk.config.cjs', '.config/vovk.config.js', '.config/vovk.config.mjs', '.config/vovk.config.cjs'], {
|
|
142
150
|
persistent: true,
|
|
143
151
|
cwd,
|
|
144
152
|
ignoreInitial: false,
|
|
@@ -157,15 +165,14 @@ export class VovkDev {
|
|
|
157
165
|
.on('error', (error) => log.error(`Error watching config files: ${error?.message ?? 'Unknown error'}`));
|
|
158
166
|
void handle();
|
|
159
167
|
};
|
|
160
|
-
async #watch() {
|
|
168
|
+
async #watch(callback) {
|
|
161
169
|
if (this.#isWatching)
|
|
162
170
|
throw new Error('Already watching');
|
|
163
171
|
const { log } = this.#projectInfo;
|
|
164
172
|
log.debug(`Starting segments and modules watcher. Detected initial segments: ${JSON.stringify(this.#segments.map((s) => s.segmentName))}.`);
|
|
165
173
|
await ensureClient(this.#projectInfo);
|
|
166
174
|
// automatically watches segments and modules
|
|
167
|
-
this.#watchConfig();
|
|
168
|
-
log.info('Vovk Dev Watcher is ready');
|
|
175
|
+
this.#watchConfig(callback);
|
|
169
176
|
}
|
|
170
177
|
#processControllerChange = async (filePath) => {
|
|
171
178
|
const { log } = this.#projectInfo;
|
|
@@ -208,22 +215,29 @@ export class VovkDev {
|
|
|
208
215
|
const { devHttps } = config;
|
|
209
216
|
const endpoint = `${apiEntryPoint.startsWith(`http${devHttps ? 's' : ''}://`) ? apiEntryPoint : `http${devHttps ? 's' : ''}://localhost:${port}${apiEntryPoint}`}/${segmentName ? `${segmentName}/` : ''}_schema_`;
|
|
210
217
|
log.debug(`Requesting schema for ${formatLoggedSegmentName(segmentName)} at ${endpoint}`);
|
|
211
|
-
const resp = await fetch(endpoint);
|
|
212
|
-
if (resp.status !== 200) {
|
|
213
|
-
const probableCause = {
|
|
214
|
-
404: 'The segment did not compile or config.origin is wrong.',
|
|
215
|
-
}[resp.status];
|
|
216
|
-
log.warn(`Schema request to ${formatLoggedSegmentName(segmentName)} failed with status code ${resp.status} but expected 200.${probableCause ? ` Probable cause: ${probableCause}` : ''}`);
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
let schema = null;
|
|
220
218
|
try {
|
|
221
|
-
|
|
219
|
+
const resp = await fetch(endpoint);
|
|
220
|
+
if (resp.status !== 200) {
|
|
221
|
+
const probableCause = {
|
|
222
|
+
404: 'The segment did not compile or config.origin is wrong.',
|
|
223
|
+
}[resp.status];
|
|
224
|
+
log.warn(`Schema request to ${formatLoggedSegmentName(segmentName)} failed with status code ${resp.status} but expected 200.${probableCause ? ` Probable cause: ${probableCause}` : ''}`);
|
|
225
|
+
return { isError: true };
|
|
226
|
+
}
|
|
227
|
+
let schema = null;
|
|
228
|
+
try {
|
|
229
|
+
({ schema } = (await resp.json()));
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
log.error(`Error parsing schema for ${formatLoggedSegmentName(segmentName)}: ${error.message}`);
|
|
233
|
+
}
|
|
234
|
+
await this.#handleSchema(schema);
|
|
222
235
|
}
|
|
223
236
|
catch (error) {
|
|
224
|
-
log.error(`Error
|
|
237
|
+
log.error(`Error requesting schema for ${formatLoggedSegmentName(segmentName)}: ${error.message}`);
|
|
238
|
+
return { isError: true };
|
|
225
239
|
}
|
|
226
|
-
|
|
240
|
+
return { isError: false };
|
|
227
241
|
}, 500);
|
|
228
242
|
async #handleSchema(schema) {
|
|
229
243
|
const { log, config, cwd } = this.#projectInfo;
|
|
@@ -261,6 +275,7 @@ export class VovkDev {
|
|
|
261
275
|
}
|
|
262
276
|
}
|
|
263
277
|
async start() {
|
|
278
|
+
const now = Date.now();
|
|
264
279
|
this.#projectInfo = await getProjectInfo();
|
|
265
280
|
const { log, config, cwd, apiDir } = this.#projectInfo;
|
|
266
281
|
if (config.devHttps) {
|
|
@@ -284,9 +299,29 @@ export class VovkDev {
|
|
|
284
299
|
// Request schema every segment in 5 seconds in order to update schema and start watching
|
|
285
300
|
setTimeout(() => {
|
|
286
301
|
for (const { segmentName } of this.#segments) {
|
|
287
|
-
|
|
302
|
+
const MAX_ATTEMPTS = 3;
|
|
303
|
+
let attempts = 0;
|
|
304
|
+
void this.#requestSchema(segmentName).then(({ isError }) => {
|
|
305
|
+
if (isError) {
|
|
306
|
+
const interval = setInterval(() => {
|
|
307
|
+
attempts++;
|
|
308
|
+
if (attempts >= MAX_ATTEMPTS) {
|
|
309
|
+
clearInterval(interval);
|
|
310
|
+
log.error(`Failed to request schema for ${formatLoggedSegmentName(segmentName)} after ${MAX_ATTEMPTS} attempts`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
void this.#requestSchema(segmentName).then(({ isError: isError2 }) => {
|
|
314
|
+
if (!isError2) {
|
|
315
|
+
clearInterval(interval);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}, 5000);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
288
321
|
}
|
|
289
|
-
this.#watch()
|
|
322
|
+
this.#watch(() => {
|
|
323
|
+
log.info(`Ready in ${Date.now() - now}ms`);
|
|
324
|
+
});
|
|
290
325
|
}, 5000);
|
|
291
326
|
}
|
|
292
327
|
}
|
package/dist/index.d.mts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import 'dotenv/config';
|
|
4
6
|
import { Command } from 'commander';
|
|
5
7
|
import concurrently from 'concurrently';
|
|
6
8
|
import getAvailablePort from './utils/getAvailablePort.mjs';
|
|
@@ -9,9 +11,7 @@ import generateClient from './generateClient.mjs';
|
|
|
9
11
|
import locateSegments from './locateSegments.mjs';
|
|
10
12
|
import { VovkDev } from './dev/index.mjs';
|
|
11
13
|
import newComponents from './new/index.mjs';
|
|
12
|
-
import 'dotenv/config';
|
|
13
14
|
import initProgram from './initProgram.mjs';
|
|
14
|
-
import { pathToFileURL } from 'node:url';
|
|
15
15
|
const program = new Command();
|
|
16
16
|
const packageJSON = JSON.parse(readFileSync(path.join(import.meta.dirname, '../package.json'), 'utf-8'));
|
|
17
17
|
program.name('vovk').description('Vovk CLI').version(packageJSON.version);
|
package/dist/init/index.mjs
CHANGED
|
@@ -7,16 +7,17 @@ import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
|
|
|
7
7
|
import installDependencies, { getPackageManager } from './installDependencies.mjs';
|
|
8
8
|
import getLogger from '../utils/getLogger.mjs';
|
|
9
9
|
import createConfig from './createConfig.mjs';
|
|
10
|
-
import updateNPMScripts from './updateNPMScripts.mjs';
|
|
10
|
+
import updateNPMScripts, { getDevScript } from './updateNPMScripts.mjs';
|
|
11
11
|
import checkTSConfigForExperimentalDecorators from './checkTSConfigForExperimentalDecorators.mjs';
|
|
12
12
|
import updateTypeScriptConfig from './updateTypeScriptConfig.mjs';
|
|
13
13
|
import updateDependenciesWithoutInstalling from './updateDependenciesWithoutInstalling.mjs';
|
|
14
14
|
import logUpdateDependenciesError from './logUpdateDependenciesError.mjs';
|
|
15
15
|
import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
|
|
16
|
+
import NPMCliPackageJson from '@npmcli/package-json';
|
|
16
17
|
export class Init {
|
|
17
18
|
root;
|
|
18
19
|
log;
|
|
19
|
-
async #init({ configPaths, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, dryRun, channel, }) {
|
|
20
|
+
async #init({ configPaths, pkgJson, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, dryRun, channel, }) {
|
|
20
21
|
const { log, root } = this;
|
|
21
22
|
const dependencies = ['vovk', 'vovk-client'];
|
|
22
23
|
const devDependencies = ['vovk-cli'];
|
|
@@ -30,13 +31,13 @@ export class Init {
|
|
|
30
31
|
dependencies.push(...({
|
|
31
32
|
'vovk-zod': ['zod'],
|
|
32
33
|
'vovk-yup': ['yup'],
|
|
33
|
-
'vovk-dto': ['class-validator', 'class-transformer'],
|
|
34
|
+
'vovk-dto': ['class-validator', 'class-transformer', 'vovk-mapped-types', 'reflect-metadata'],
|
|
34
35
|
}[validationLibrary] ?? []));
|
|
35
36
|
}
|
|
36
37
|
if (updateScripts) {
|
|
37
38
|
try {
|
|
38
39
|
if (!dryRun)
|
|
39
|
-
await updateNPMScripts(root, updateScripts);
|
|
40
|
+
await updateNPMScripts(pkgJson, root, updateScripts);
|
|
40
41
|
log.info('Updated scripts at package.json');
|
|
41
42
|
}
|
|
42
43
|
catch (error) {
|
|
@@ -113,11 +114,12 @@ export class Init {
|
|
|
113
114
|
const cwd = process.cwd();
|
|
114
115
|
const root = path.resolve(cwd, prefix);
|
|
115
116
|
const log = getLogger(logLevel);
|
|
117
|
+
const pkgJson = await NPMCliPackageJson.load(root);
|
|
116
118
|
this.root = root;
|
|
117
119
|
this.log = log;
|
|
118
120
|
const configPaths = await getConfigPaths({ cwd, relativePath: prefix });
|
|
119
121
|
if (yes) {
|
|
120
|
-
return this.#init({ configPaths }, {
|
|
122
|
+
return this.#init({ configPaths, pkgJson }, {
|
|
121
123
|
useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
|
|
122
124
|
useYarn: useYarn ?? false,
|
|
123
125
|
usePnpm: usePnpm ?? false,
|
|
@@ -164,7 +166,7 @@ export class Init {
|
|
|
164
166
|
{
|
|
165
167
|
name: 'vovk-dto',
|
|
166
168
|
value: 'vovk-dto',
|
|
167
|
-
description: 'Use class-validator
|
|
169
|
+
description: 'Use class-validator for data validation. Also installs class-transformer, vovk-mapped-types and reflect-metadata',
|
|
168
170
|
},
|
|
169
171
|
{ name: 'None', value: null, description: 'Install validation library later' },
|
|
170
172
|
],
|
|
@@ -185,12 +187,12 @@ export class Init {
|
|
|
185
187
|
{
|
|
186
188
|
name: 'Yes, use "concurrently" implicitly',
|
|
187
189
|
value: 'implicit',
|
|
188
|
-
description: `The "dev" script will use "concurrently" API to run "next dev" and "vovk dev" commands together and automatically find an available port ${chalk.whiteBright.bold(`"
|
|
190
|
+
description: `The "dev" script will use "concurrently" API to run "next dev" and "vovk dev" commands together and automatically find an available port ${chalk.whiteBright.bold(`"${getDevScript(pkgJson, 'implicit')}"`)}`,
|
|
189
191
|
},
|
|
190
192
|
{
|
|
191
193
|
name: 'Yes, use "concurrently" explicitly',
|
|
192
194
|
value: 'explicit',
|
|
193
|
-
description: `The "dev" script will use pre-defined PORT variable and run "next dev" and "vovk dev" as "concurrently" CLI arguments ${chalk.whiteBright.bold(`"
|
|
195
|
+
description: `The "dev" script will use pre-defined PORT variable and run "next dev" and "vovk dev" as "concurrently" CLI arguments ${chalk.whiteBright.bold(`"${getDevScript(pkgJson, 'explicit')}"`)}`,
|
|
194
196
|
},
|
|
195
197
|
{
|
|
196
198
|
name: 'No',
|
|
@@ -213,7 +215,7 @@ export class Init {
|
|
|
213
215
|
});
|
|
214
216
|
}
|
|
215
217
|
}
|
|
216
|
-
await this.#init({ configPaths }, {
|
|
218
|
+
await this.#init({ configPaths, pkgJson }, {
|
|
217
219
|
useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
|
|
218
220
|
useYarn: useYarn ?? false,
|
|
219
221
|
usePnpm: usePnpm ?? false,
|
|
@@ -1 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
import NPMCliPackageJson from '@npmcli/package-json';
|
|
2
|
+
export declare function getDevScript(pkgJson: NPMCliPackageJson, updateScriptsMode: 'implicit' | 'explicit'): string;
|
|
3
|
+
export default function updateNPMScripts(pkgJson: NPMCliPackageJson, root: string, updateScriptsMode: 'implicit' | 'explicit'): Promise<void>;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
1
|
+
export function getDevScript(pkgJson, updateScriptsMode) {
|
|
2
|
+
const nextDev = pkgJson.content.scripts?.dev ?? 'next dev';
|
|
3
|
+
const nextDevFlags = nextDev.replace('next dev', '').trim();
|
|
4
|
+
return updateScriptsMode === 'explicit'
|
|
5
|
+
? `PORT=3000 concurrently '${nextDev}' 'vovk dev' --kill-others`
|
|
6
|
+
: `vovk dev --next-dev${nextDevFlags ? ` -- ${nextDevFlags}` : ''}`;
|
|
7
|
+
}
|
|
8
|
+
export default async function updateNPMScripts(pkgJson, root, updateScriptsMode) {
|
|
4
9
|
pkgJson.update({
|
|
5
10
|
scripts: {
|
|
6
11
|
...pkgJson.content.scripts,
|
|
7
12
|
generate: 'vovk generate',
|
|
8
|
-
dev: updateScriptsMode
|
|
9
|
-
? "PORT=3000 concurrently 'next dev' 'vovk dev' --kill-others"
|
|
10
|
-
: 'vovk dev --next-dev',
|
|
13
|
+
dev: getDevScript(pkgJson, updateScriptsMode),
|
|
11
14
|
},
|
|
12
15
|
});
|
|
13
16
|
await pkgJson.save();
|
package/dist/new/newSegment.mjs
CHANGED
|
@@ -29,5 +29,5 @@ ${segmentName ? ` segmentName: '${segmentName}',\n` : ''} emitSchema: true,
|
|
|
29
29
|
await fs.mkdir(path.dirname(absoluteSegmentRoutePath), { recursive: true });
|
|
30
30
|
await fs.writeFile(absoluteSegmentRoutePath, code);
|
|
31
31
|
}
|
|
32
|
-
log.info(`${formatLoggedSegmentName(segmentName, { upperFirst: true })} created at ${absoluteSegmentRoutePath}. Run ${chalkHighlightThing(`vovk new controller ${[segmentName, '
|
|
32
|
+
log.info(`${formatLoggedSegmentName(segmentName, { upperFirst: true })} created at ${absoluteSegmentRoutePath}. Run ${chalkHighlightThing(`vovk new controller ${[segmentName, 'thing'].filter(Boolean).join('/')}`)} to create a new controller`);
|
|
33
33
|
}
|
package/dist/types.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { KnownAny } from '../types.mjs';
|
|
2
|
-
export default function debounceWithArgs<
|
|
2
|
+
export default function debounceWithArgs<Callback extends (...args: KnownAny[]) => KnownAny>(callback: Callback, wait: number): (...args: Parameters<Callback>) => Promise<Awaited<ReturnType<Callback>>>;
|
|
@@ -1,11 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
1
|
+
export default function debounceWithArgs(callback, wait) {
|
|
2
|
+
// Stores timeouts keyed by the stringified arguments
|
|
3
|
+
const timeouts = new Map();
|
|
4
4
|
return (...args) => {
|
|
5
|
+
// Convert arguments to a JSON string (or any other stable key generation)
|
|
5
6
|
const key = JSON.stringify(args);
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
// Clear any existing timer for this specific key
|
|
8
|
+
if (timeouts.has(key)) {
|
|
9
|
+
clearTimeout(timeouts.get(key));
|
|
8
10
|
}
|
|
9
|
-
|
|
11
|
+
// Return a promise that resolves/rejects after the debounce delay
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
const timeoutId = setTimeout(async () => {
|
|
14
|
+
try {
|
|
15
|
+
const result = await callback(...args);
|
|
16
|
+
resolve(result);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
reject(error);
|
|
20
|
+
}
|
|
21
|
+
finally {
|
|
22
|
+
// Remove the entry once the callback is invoked
|
|
23
|
+
timeouts.delete(key);
|
|
24
|
+
}
|
|
25
|
+
}, wait);
|
|
26
|
+
timeouts.set(key, timeoutId);
|
|
27
|
+
});
|
|
10
28
|
};
|
|
11
29
|
}
|