vovk-cli 0.0.1-draft.4 → 0.0.1-draft.40
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 +92 -55
- 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/getConfig.mjs +1 -1
- package/dist/getProjectInfo/getConfigAbsolutePaths.mjs +2 -2
- package/dist/getProjectInfo/getRelativeSrcRoot.mjs +4 -4
- package/dist/getProjectInfo/getUserConfig.mjs +4 -2
- package/dist/getProjectInfo/importUncachedModule.mjs +0 -1
- package/dist/getProjectInfo/importUncachedModuleWorker.mjs +0 -1
- package/dist/getProjectInfo/index.mjs +2 -2
- package/dist/index.d.mts +1 -23
- package/dist/index.mjs +19 -43
- 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 +4 -5
- package/dist/init/index.d.mts +1 -2
- package/dist/init/index.mjs +46 -93
- 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.d.mts +3 -1
- package/dist/init/updateNPMScripts.mjs +10 -6
- package/dist/init/updateTypeScriptConfig.mjs +2 -2
- package/dist/initProgram.d.mts +2 -0
- package/dist/initProgram.mjs +21 -0
- package/dist/locateSegments.mjs +3 -3
- package/dist/new/addClassToSegmentCode.mjs +6 -2
- package/dist/new/addCommonTerms.mjs +1 -0
- 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 +36 -1
- package/dist/utils/debounceWithArgs.d.mts +1 -1
- package/dist/utils/debounceWithArgs.mjs +24 -9
- package/dist/utils/formatLoggedSegmentName.mjs +1 -1
- package/dist/utils/getAvailablePort.mjs +3 -2
- package/dist/utils/getFileSystemEntryType.mjs +1 -1
- package/package.json +14 -11
- package/templates/controller.ejs +20 -18
- 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
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Init } from './init/index.mjs';
|
|
2
|
+
// reused at vovk-init
|
|
3
|
+
export default function initProgram(program) {
|
|
4
|
+
return program
|
|
5
|
+
.argument('[prefix]', 'Directory to initialize project in', '.')
|
|
6
|
+
.description('Initialize Vovk project')
|
|
7
|
+
.option('-y, --yes', 'Skip all prompts and use default values')
|
|
8
|
+
.option('--log-level <level>', 'Set log level', 'info')
|
|
9
|
+
.option('--use-npm', 'Use npm as package manager')
|
|
10
|
+
.option('--use-yarn', 'Use yarn as package manager')
|
|
11
|
+
.option('--use-pnpm', 'Use pnpm as package manager')
|
|
12
|
+
.option('--use-bun', 'Use bun as package manager')
|
|
13
|
+
.option('--skip-install', 'Skip installing dependencies')
|
|
14
|
+
.option('--update-ts-config', 'Update tsconfig.json')
|
|
15
|
+
.option('--update-scripts <mode>', 'Update package.json scripts ("implicit" or "explicit")')
|
|
16
|
+
.option('--validation-library <library>', 'Validation library to use ("vovk-zod", "vovk-yup", "vovk-dto" or another). Set to "none" to skip validation')
|
|
17
|
+
.option('--validate-on-client', 'Path to validateOnClient file')
|
|
18
|
+
.option('--channel <channel>', 'Channel to use for fetching packages', 'latest')
|
|
19
|
+
.option('--dry-run', 'Do not write files to disk')
|
|
20
|
+
.action((prefix = '.', options) => new Init().main(prefix, options));
|
|
21
|
+
}
|
package/dist/locateSegments.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
import getFileSystemEntryType from './utils/getFileSystemEntryType.mjs';
|
|
4
4
|
export default async function locateSegments(dir, rootDir = dir) {
|
|
5
5
|
let results = [];
|
|
@@ -16,7 +16,7 @@ export default async function locateSegments(dir, rootDir = dir) {
|
|
|
16
16
|
const routeFilePath = path.join(filePath, 'route.ts');
|
|
17
17
|
if (await getFileSystemEntryType(routeFilePath)) {
|
|
18
18
|
// Calculate the basePath relative to the root directory
|
|
19
|
-
const segmentName = path.relative(rootDir, dir);
|
|
19
|
+
const segmentName = path.relative(rootDir, dir).replace(/\\/g, '/'); // windows fix
|
|
20
20
|
results.push({ routeFilePath, segmentName });
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { Project, SyntaxKind } from 'ts-morph';
|
|
1
|
+
import { Project, QuoteKind, SyntaxKind } from 'ts-morph';
|
|
2
2
|
export default function addClassToSegmentCode(segmentSourceCode, { sourceName, compiledName, type, importPath, }) {
|
|
3
|
-
const project = new Project(
|
|
3
|
+
const project = new Project({
|
|
4
|
+
manipulationSettings: {
|
|
5
|
+
quoteKind: QuoteKind.Single,
|
|
6
|
+
},
|
|
7
|
+
});
|
|
4
8
|
const sourceFile = project.createSourceFile('route.ts', segmentSourceCode, { overwrite: true });
|
|
5
9
|
// Add the import if it doesn't exist
|
|
6
10
|
let importDeclaration = sourceFile.getImportDeclaration((imp) => {
|
package/dist/new/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { NewOptions } from '../
|
|
2
|
-
export default function newComponents(components: string[],
|
|
1
|
+
import type { NewOptions } from '../types.mjs';
|
|
2
|
+
export default function newComponents(components: string[], { dryRun, dir, templates, overwrite, noSegmentUpdate }: NewOptions): Promise<void>;
|
package/dist/new/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import newModule from './newModule.mjs';
|
|
2
2
|
import newSegment from './newSegment.mjs';
|
|
3
|
-
export default async function newComponents(components,
|
|
3
|
+
export default async function newComponents(components, { dryRun, dir, templates, overwrite, noSegmentUpdate }) {
|
|
4
4
|
if (components[0] === 'segment' || components[0] === 'segments') {
|
|
5
|
+
// vovk new segment [segmentName]
|
|
5
6
|
let segmentNames = components
|
|
6
7
|
.slice(1)
|
|
7
8
|
.map((segmentName) => (segmentName === '""' || segmentName === "''" ? '' : segmentName));
|
|
@@ -9,10 +10,11 @@ export default async function newComponents(components, options) {
|
|
|
9
10
|
segmentNames = [''];
|
|
10
11
|
}
|
|
11
12
|
for (const segmentName of segmentNames) {
|
|
12
|
-
await newSegment({ segmentName,
|
|
13
|
+
await newSegment({ segmentName, overwrite, dryRun });
|
|
13
14
|
}
|
|
14
15
|
}
|
|
15
16
|
else {
|
|
17
|
+
// vovk new [what...] [moduleNameWithOptionalSegment]
|
|
16
18
|
if (components.length < 2) {
|
|
17
19
|
throw new Error('Invalid command invocation. Please provide at least two arguments.');
|
|
18
20
|
}
|
|
@@ -21,6 +23,14 @@ export default async function newComponents(components, options) {
|
|
|
21
23
|
if (!moduleNameWithOptionalSegment) {
|
|
22
24
|
throw new Error('A module name with an optional segment cannot be empty');
|
|
23
25
|
}
|
|
24
|
-
await newModule({
|
|
26
|
+
await newModule({
|
|
27
|
+
what,
|
|
28
|
+
moduleNameWithOptionalSegment,
|
|
29
|
+
dir,
|
|
30
|
+
templates,
|
|
31
|
+
overwrite,
|
|
32
|
+
noSegmentUpdate,
|
|
33
|
+
dryRun,
|
|
34
|
+
});
|
|
25
35
|
}
|
|
26
36
|
}
|
package/dist/new/newModule.d.mts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
export default function newModule({ what, moduleNameWithOptionalSegment, dryRun, }: {
|
|
1
|
+
export default function newModule({ what, moduleNameWithOptionalSegment, dryRun, dir: dirFlag, templates: templatesFlag, noSegmentUpdate, overwrite, }: {
|
|
2
2
|
what: string[];
|
|
3
3
|
moduleNameWithOptionalSegment: string;
|
|
4
|
-
dryRun
|
|
4
|
+
dryRun?: boolean;
|
|
5
|
+
dir?: string;
|
|
6
|
+
templates?: string[];
|
|
7
|
+
noSegmentUpdate?: boolean;
|
|
8
|
+
overwrite?: boolean;
|
|
5
9
|
}): Promise<void>;
|
package/dist/new/newModule.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs/promises';
|
|
3
|
-
import getProjectInfo from '../getProjectInfo/index.mjs';
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
4
3
|
import render from './render.mjs';
|
|
4
|
+
import addClassToSegmentCode from './addClassToSegmentCode.mjs';
|
|
5
|
+
import getProjectInfo from '../getProjectInfo/index.mjs';
|
|
6
|
+
import locateSegments from '../locateSegments.mjs';
|
|
5
7
|
import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
|
|
6
8
|
import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
|
|
7
|
-
import locateSegments from '../locateSegments.mjs';
|
|
8
|
-
import addClassToSegmentCode from './addClassToSegmentCode.mjs';
|
|
9
9
|
import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
|
|
10
10
|
import prettify from '../utils/prettify.mjs';
|
|
11
11
|
function splitByLast(str, delimiter = '/') {
|
|
@@ -18,9 +18,9 @@ function splitByLast(str, delimiter = '/') {
|
|
|
18
18
|
const after = str.substring(index + delimiter.length);
|
|
19
19
|
return [before, after];
|
|
20
20
|
}
|
|
21
|
-
export default async function newModule({ what, moduleNameWithOptionalSegment, dryRun, }) {
|
|
21
|
+
export default async function newModule({ what, moduleNameWithOptionalSegment, dryRun, dir: dirFlag, templates: templatesFlag, noSegmentUpdate, overwrite, }) {
|
|
22
22
|
const { config, log, apiDir, cwd } = await getProjectInfo();
|
|
23
|
-
|
|
23
|
+
let templates = config.templates;
|
|
24
24
|
const [segmentName, moduleName] = splitByLast(moduleNameWithOptionalSegment);
|
|
25
25
|
// replace c by controller, s by service, w by worker, everything else keeps the same
|
|
26
26
|
what = what.map((s) => {
|
|
@@ -35,16 +35,24 @@ export default async function newModule({ what, moduleNameWithOptionalSegment, d
|
|
|
35
35
|
return s;
|
|
36
36
|
}
|
|
37
37
|
});
|
|
38
|
-
|
|
38
|
+
if (templatesFlag) {
|
|
39
|
+
if (templatesFlag.length > what.length) {
|
|
40
|
+
throw new Error('Too many templates provided');
|
|
41
|
+
}
|
|
42
|
+
templates = templatesFlag.reduce((acc, templatePath, index) => ({
|
|
43
|
+
...acc,
|
|
44
|
+
[what[index]]: templatePath,
|
|
45
|
+
}), templates);
|
|
46
|
+
}
|
|
39
47
|
for (const type of what) {
|
|
40
48
|
if (!templates[type]) {
|
|
41
|
-
throw new Error(`Template for ${type} not found
|
|
49
|
+
throw new Error(`Template for "${type}" not found`);
|
|
42
50
|
}
|
|
43
51
|
}
|
|
44
52
|
const segments = await locateSegments(apiDir);
|
|
45
53
|
const segment = segments.find((s) => s.segmentName === segmentName);
|
|
46
54
|
if (!segment) {
|
|
47
|
-
throw new Error(`Segment ${segmentName} not found`);
|
|
55
|
+
throw new Error(`Unable to create module. Segment "${segmentName}" not found. Run "vovk new segment ${segmentName}" to create it`);
|
|
48
56
|
}
|
|
49
57
|
for (const type of what) {
|
|
50
58
|
const templatePath = templates[type];
|
|
@@ -52,39 +60,55 @@ export default async function newModule({ what, moduleNameWithOptionalSegment, d
|
|
|
52
60
|
? path.resolve(cwd, templatePath)
|
|
53
61
|
: path.resolve(cwd, './node_modules', templatePath);
|
|
54
62
|
const templateCode = await fs.readFile(templateAbsolutePath, 'utf-8');
|
|
55
|
-
const {
|
|
63
|
+
const { dir: renderedDir, fileName, sourceName, compiledName, code, } = await render(templateCode, {
|
|
64
|
+
cwd,
|
|
56
65
|
config,
|
|
57
66
|
withService: what.includes('service'),
|
|
58
67
|
segmentName,
|
|
59
68
|
moduleName,
|
|
60
69
|
});
|
|
61
|
-
const
|
|
62
|
-
|
|
70
|
+
const dir = dirFlag || renderedDir;
|
|
71
|
+
if (!dir) {
|
|
72
|
+
throw new Error(`The template for "${type}" does not provide a dir`);
|
|
73
|
+
}
|
|
74
|
+
if (!fileName) {
|
|
75
|
+
throw new Error(`The template for "${type}" does not provide a fileName`);
|
|
76
|
+
}
|
|
77
|
+
const absoluteModuleDir = path.join(cwd, dir);
|
|
78
|
+
const absoluteModulePath = path.join(absoluteModuleDir, fileName);
|
|
63
79
|
const prettiedCode = await prettify(code, absoluteModulePath);
|
|
64
80
|
if (!dryRun) {
|
|
65
|
-
if (await getFileSystemEntryType(absoluteModulePath)) {
|
|
81
|
+
if (!overwrite && (await getFileSystemEntryType(absoluteModulePath))) {
|
|
66
82
|
log.warn(`File ${chalkHighlightThing(absoluteModulePath)} already exists, skipping this "${type}"`);
|
|
67
83
|
}
|
|
68
84
|
else {
|
|
69
|
-
await fs.mkdir(
|
|
85
|
+
await fs.mkdir(absoluteModuleDir, { recursive: true });
|
|
70
86
|
await fs.writeFile(absoluteModulePath, prettiedCode);
|
|
87
|
+
log.info(`Created ${chalkHighlightThing(fileName)} using ${chalkHighlightThing(`"${type}"`)} template for ${formatLoggedSegmentName(segmentName)}`);
|
|
71
88
|
}
|
|
72
89
|
}
|
|
73
90
|
if (type === 'controller' || type === 'worker') {
|
|
91
|
+
if (!sourceName) {
|
|
92
|
+
throw new Error(`The template for "${type}" does not provide a sourceName`);
|
|
93
|
+
}
|
|
94
|
+
if (!compiledName) {
|
|
95
|
+
throw new Error('The template for "${type}" does not provide a compiledName');
|
|
96
|
+
}
|
|
74
97
|
const { routeFilePath } = segment;
|
|
75
98
|
const segmentSourceCode = await fs.readFile(routeFilePath, 'utf-8');
|
|
76
|
-
const importPath = path.relative(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
99
|
+
const importPath = path.relative(path.dirname(routeFilePath), absoluteModulePath).replace(/\.(ts|tsx)$/, '');
|
|
100
|
+
if (!noSegmentUpdate) {
|
|
101
|
+
const newSegmentCode = await prettify(addClassToSegmentCode(segmentSourceCode, {
|
|
102
|
+
sourceName,
|
|
103
|
+
compiledName,
|
|
104
|
+
type,
|
|
105
|
+
importPath,
|
|
106
|
+
}), routeFilePath);
|
|
107
|
+
if (!dryRun) {
|
|
108
|
+
await fs.writeFile(routeFilePath, newSegmentCode);
|
|
109
|
+
}
|
|
85
110
|
}
|
|
86
|
-
log.info(`Added ${chalkHighlightThing(sourceName)} ${type}
|
|
111
|
+
log.info(`Added ${chalkHighlightThing(sourceName)} ${type} as ${chalkHighlightThing(compiledName)} to ${formatLoggedSegmentName(segmentName)}`);
|
|
87
112
|
}
|
|
88
|
-
log.info(`Created ${chalkHighlightThing(sourceName)} with ${chalkHighlightThing(type)} template for ${formatLoggedSegmentName(segmentName)}`);
|
|
89
113
|
}
|
|
90
114
|
}
|
package/dist/new/newSegment.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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 getProjectInfo from '../getProjectInfo/index.mjs';
|
|
4
4
|
import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
|
|
5
5
|
import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
|
|
6
6
|
import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
|
|
7
7
|
import prettify from '../utils/prettify.mjs';
|
|
8
|
-
export default async function newSegment({ segmentName, dryRun }) {
|
|
8
|
+
export default async function newSegment({ segmentName, overwrite, dryRun, }) {
|
|
9
9
|
const { apiDir, cwd, log } = await getProjectInfo();
|
|
10
10
|
const absoluteSegmentRoutePath = path.join(cwd, apiDir, segmentName, '[[...vovk]]/route.ts');
|
|
11
|
-
if (await getFileSystemEntryType(absoluteSegmentRoutePath)) {
|
|
11
|
+
if (!overwrite && (await getFileSystemEntryType(absoluteSegmentRoutePath))) {
|
|
12
12
|
throw new Error(`Unable to create new segment. ${formatLoggedSegmentName(segmentName, { upperFirst: true })} already exists.`);
|
|
13
13
|
}
|
|
14
14
|
const code = await prettify(`import { initVovk } from 'vovk';
|
|
@@ -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/new/render.d.mts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import type { VovkConfig } from '../types.mjs';
|
|
1
|
+
import type { VovkConfig, VovkModuleRenderResult } from '../types.mjs';
|
|
2
2
|
export default function render(codeTemplate: string, { config, withService, segmentName, moduleName, }: {
|
|
3
|
+
cwd: string;
|
|
3
4
|
config: VovkConfig;
|
|
4
5
|
withService: boolean;
|
|
5
6
|
segmentName: string;
|
|
6
7
|
moduleName: string;
|
|
7
|
-
}): Promise<
|
|
8
|
-
filePath: string;
|
|
9
|
-
sourceName: string;
|
|
10
|
-
compiledName: string;
|
|
11
|
-
code: string;
|
|
12
|
-
}>;
|
|
8
|
+
}): Promise<VovkModuleRenderResult>;
|
package/dist/new/render.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import pluralize from 'pluralize';
|
|
|
5
5
|
import addCommonTerms from './addCommonTerms.mjs';
|
|
6
6
|
addCommonTerms();
|
|
7
7
|
export default async function render(codeTemplate, { config, withService, segmentName, moduleName, }) {
|
|
8
|
-
const
|
|
8
|
+
const getModuleDirName = (givenSegmentName, givenModuleName) => [config.modulesDir, givenSegmentName || config.rootSegmentModulesDirName, _.camelCase(givenModuleName)]
|
|
9
9
|
.filter(Boolean)
|
|
10
10
|
.join('/');
|
|
11
11
|
const templateVars = {
|
|
@@ -15,15 +15,19 @@ export default async function render(codeTemplate, { config, withService, segmen
|
|
|
15
15
|
segmentName,
|
|
16
16
|
moduleName,
|
|
17
17
|
// utils
|
|
18
|
-
|
|
18
|
+
getModuleDirName,
|
|
19
19
|
// libraries
|
|
20
20
|
_, // lodash
|
|
21
21
|
pluralize,
|
|
22
22
|
};
|
|
23
|
-
// first, render the front matter because it can use ejs variables
|
|
24
23
|
const parsed = matter((await ejs.render(codeTemplate, templateVars, { async: true })).trim());
|
|
25
|
-
const {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
const { dir, fileName, sourceName, compiledName } = parsed.data;
|
|
25
|
+
const code = parsed.content;
|
|
26
|
+
return {
|
|
27
|
+
dir,
|
|
28
|
+
fileName,
|
|
29
|
+
sourceName,
|
|
30
|
+
compiledName,
|
|
31
|
+
code,
|
|
32
|
+
};
|
|
29
33
|
}
|
package/dist/postinstall.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import path from 'path';
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
/**
|
|
4
4
|
* Checks if a file exists at the given path.
|
|
5
5
|
* @param {string} filePath - The path to the file.
|
|
@@ -11,7 +11,9 @@ async function postinstall() {
|
|
|
11
11
|
const js = path.join(vovk, 'client.js');
|
|
12
12
|
const ts = path.join(vovk, 'client.d.ts');
|
|
13
13
|
const index = path.join(vovk, 'index.ts');
|
|
14
|
-
if ((await getFileSystemEntryType(js)) ||
|
|
14
|
+
if ((await getFileSystemEntryType(js)) ||
|
|
15
|
+
(await getFileSystemEntryType(ts)) ||
|
|
16
|
+
(await getFileSystemEntryType(index))) {
|
|
15
17
|
return;
|
|
16
18
|
}
|
|
17
19
|
await fs.mkdir(vovk, { recursive: true });
|
package/dist/types.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LogLevelNames } from 'loglevel';
|
|
1
|
+
import type { LogLevelNames } from 'loglevel';
|
|
2
2
|
export type KnownAny = any;
|
|
3
3
|
export type VovkEnv = {
|
|
4
4
|
PORT?: string;
|
|
@@ -37,3 +37,38 @@ export type VovkConfig = {
|
|
|
37
37
|
[key: string]: string | undefined;
|
|
38
38
|
};
|
|
39
39
|
};
|
|
40
|
+
export type VovkModuleRenderResult = {
|
|
41
|
+
fileName: string;
|
|
42
|
+
dir: string;
|
|
43
|
+
sourceName?: string;
|
|
44
|
+
compiledName?: string;
|
|
45
|
+
code: string;
|
|
46
|
+
};
|
|
47
|
+
export interface DevOptions {
|
|
48
|
+
nextDev?: boolean;
|
|
49
|
+
}
|
|
50
|
+
export interface GenerateOptions {
|
|
51
|
+
clientOut?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface InitOptions {
|
|
54
|
+
yes?: boolean;
|
|
55
|
+
logLevel: LogLevelNames;
|
|
56
|
+
useNpm?: boolean;
|
|
57
|
+
useYarn?: boolean;
|
|
58
|
+
usePnpm?: boolean;
|
|
59
|
+
useBun?: boolean;
|
|
60
|
+
skipInstall?: boolean;
|
|
61
|
+
updateTsConfig?: boolean;
|
|
62
|
+
updateScripts?: 'implicit' | 'explicit';
|
|
63
|
+
validationLibrary?: string | null;
|
|
64
|
+
validateOnClient?: boolean;
|
|
65
|
+
dryRun?: boolean;
|
|
66
|
+
channel?: 'latest' | 'beta' | 'draft';
|
|
67
|
+
}
|
|
68
|
+
export interface NewOptions {
|
|
69
|
+
dryRun?: boolean;
|
|
70
|
+
templates?: string[];
|
|
71
|
+
dir?: string;
|
|
72
|
+
overwrite?: boolean;
|
|
73
|
+
noSegmentUpdate?: boolean;
|
|
74
|
+
}
|
|
@@ -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,14 +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
|
-
|
|
8
|
-
|
|
9
|
-
const debouncedFn = debouncedFunctions.get(key);
|
|
10
|
-
if (debouncedFn) {
|
|
11
|
-
debouncedFn(...args);
|
|
7
|
+
// Clear any existing timer for this specific key
|
|
8
|
+
if (timeouts.has(key)) {
|
|
9
|
+
clearTimeout(timeouts.get(key));
|
|
12
10
|
}
|
|
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
|
+
});
|
|
13
28
|
};
|
|
14
29
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import chalkHighlightThing from './chalkHighlightThing.mjs';
|
|
2
1
|
import upperFirstLodash from 'lodash/upperFirst.js';
|
|
2
|
+
import chalkHighlightThing from './chalkHighlightThing.mjs';
|
|
3
3
|
export default function formatLoggedSegmentName(segmentName, { withChalk = true, upperFirst = false } = {}) {
|
|
4
4
|
let text = segmentName ? `segment "${segmentName}"` : 'the root segment';
|
|
5
5
|
text = upperFirst ? upperFirstLodash(text) : text;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import net from 'net';
|
|
1
|
+
import net from 'node:net';
|
|
2
|
+
// Created with AI
|
|
2
3
|
/**
|
|
3
4
|
* Checks if a port is available.
|
|
4
5
|
* @param {number} port - The port to check.
|
|
@@ -34,7 +35,7 @@ function getAvailablePort(startPort, maxAttempts, attempt, onWarning) {
|
|
|
34
35
|
getAvailablePort(startPort + 1, maxAttempts, attempt + 1, onWarning).then(resolve, reject);
|
|
35
36
|
}
|
|
36
37
|
else {
|
|
37
|
-
reject('No available ports found');
|
|
38
|
+
reject(new Error('No available ports found'));
|
|
38
39
|
}
|
|
39
40
|
});
|
|
40
41
|
});
|
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.40",
|
|
4
4
|
"bin": {
|
|
5
5
|
"vovk": "./dist/index.mjs"
|
|
6
6
|
},
|
|
@@ -10,9 +10,12 @@
|
|
|
10
10
|
"prefix": ".",
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "rm -rf dist tsconfig.build.tsbuildinfo && tsc -P tsconfig.build.json",
|
|
13
|
+
"postbuild": "chmod +x ./dist/index.mjs",
|
|
13
14
|
"build-test": "rm -rf test_dist && tsc -P tsconfig.test.json",
|
|
14
|
-
"test": "npm run build &&
|
|
15
|
-
"
|
|
15
|
+
"pre-test": "npm run build && npm run build-test",
|
|
16
|
+
"test-only": "npm run pre-test && node --test --test-only test_dist/test/**/*.mjs",
|
|
17
|
+
"test": "npm run pre-test && node --test --test-concurrency=1 test_dist/test/**/*.mjs",
|
|
18
|
+
"tsc": "tsc --noEmit",
|
|
16
19
|
"ncu": "npm-check-updates -u",
|
|
17
20
|
"npm-publish": "npm publish"
|
|
18
21
|
},
|
|
@@ -33,15 +36,15 @@
|
|
|
33
36
|
},
|
|
34
37
|
"homepage": "https://vovk.dev",
|
|
35
38
|
"peerDependencies": {
|
|
36
|
-
"vovk": "^3.0.0-draft.
|
|
39
|
+
"vovk": "^3.0.0-draft.34"
|
|
37
40
|
},
|
|
38
41
|
"dependencies": {
|
|
39
|
-
"@inquirer/prompts": "^7.
|
|
40
|
-
"@npmcli/package-json": "^6.0
|
|
42
|
+
"@inquirer/prompts": "^7.1.0",
|
|
43
|
+
"@npmcli/package-json": "^6.1.0",
|
|
41
44
|
"chalk": "^5.3.0",
|
|
42
45
|
"chokidar": "^4.0.1",
|
|
43
46
|
"commander": "^12.1.0",
|
|
44
|
-
"concurrently": "^9.0
|
|
47
|
+
"concurrently": "^9.1.0",
|
|
45
48
|
"dotenv": "^16.4.5",
|
|
46
49
|
"ejs": "^3.1.10",
|
|
47
50
|
"gray-matter": "^4.0.3",
|
|
@@ -50,10 +53,10 @@
|
|
|
50
53
|
"lodash": "^4.17.21",
|
|
51
54
|
"loglevel": "^1.9.2",
|
|
52
55
|
"pluralize": "^8.0.0",
|
|
53
|
-
"prettier": "^3.
|
|
56
|
+
"prettier": "^3.4.1",
|
|
54
57
|
"tar-stream": "^3.1.7",
|
|
55
58
|
"ts-morph": "^24.0.0",
|
|
56
|
-
"undici": "^
|
|
59
|
+
"undici": "^7.0.0"
|
|
57
60
|
},
|
|
58
61
|
"devDependencies": {
|
|
59
62
|
"@types/concat-stream": "^2.0.3",
|
|
@@ -62,8 +65,8 @@
|
|
|
62
65
|
"@types/pluralize": "^0.0.33",
|
|
63
66
|
"@types/tar-stream": "^3.1.3",
|
|
64
67
|
"concat-stream": "^2.0.0",
|
|
65
|
-
"create-next-app": "^15.0.
|
|
68
|
+
"create-next-app": "^15.0.3",
|
|
66
69
|
"node-pty": "^1.0.0",
|
|
67
|
-
"type-fest": "^4.
|
|
70
|
+
"type-fest": "^4.29.0"
|
|
68
71
|
}
|
|
69
72
|
}
|
package/templates/controller.ejs
CHANGED
|
@@ -1,50 +1,52 @@
|
|
|
1
1
|
<% var modulePascalName = _.upperFirst(_.camelCase(moduleName)); %>
|
|
2
2
|
<% var modulePascalNamePlural = pluralize(modulePascalName); %>
|
|
3
|
-
<% var
|
|
4
|
-
<% var
|
|
5
|
-
<% var
|
|
3
|
+
<% var controllerName = modulePascalName + 'Controller'; %>
|
|
4
|
+
<% var compiledName = modulePascalName + 'RPC'; %>
|
|
5
|
+
<% var serviceName = modulePascalName + 'Service'; %>
|
|
6
|
+
<% var prefix = pluralize(_.kebabCase(moduleName).toLowerCase()); %>
|
|
6
7
|
---
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
dir: <%= getModuleDirName(segmentName, moduleName) %>
|
|
9
|
+
fileName: <%= controllerName + '.ts' %>
|
|
10
|
+
sourceName: <%= controllerName %>
|
|
11
|
+
compiledName: <%= compiledName %>
|
|
10
12
|
---
|
|
11
13
|
|
|
12
|
-
import { prefix, get, put, post, del
|
|
14
|
+
import { prefix, get, put, post, del, type VovkRequest } from 'vovk';
|
|
13
15
|
<% if(withService) { %>
|
|
14
|
-
import <%=
|
|
16
|
+
import <%= serviceName %> from './<%= serviceName %>';
|
|
15
17
|
<% } %>
|
|
16
18
|
|
|
17
|
-
@prefix('<%=
|
|
18
|
-
export default class <%=
|
|
19
|
+
@prefix('<%= prefix %>')
|
|
20
|
+
export default class <%= controllerName %> {
|
|
19
21
|
@get()
|
|
20
|
-
static get<%= modulePascalNamePlural %> = async (req: VovkRequest<null, {
|
|
21
|
-
const
|
|
22
|
+
static get<%= modulePascalNamePlural %> = async (req: VovkRequest<null, { search: string }>) => {
|
|
23
|
+
const search = req.nextUrl.searchParams.get('search');
|
|
22
24
|
<% if(withService) { %>
|
|
23
|
-
return <%=
|
|
25
|
+
return <%= serviceName %>.get<%= modulePascalNamePlural %>(search);
|
|
24
26
|
<% } else { %>
|
|
25
|
-
return {
|
|
27
|
+
return { results: [], search };
|
|
26
28
|
<% } %>
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
@put(':id')
|
|
30
|
-
static update<%=
|
|
32
|
+
static update<%= modulePascalName %> = async (req: VovkRequest<{ foo: 'bar' | 'baz' }, { q: string }>, params: { id: string }) => {
|
|
31
33
|
const { id } = params;
|
|
32
34
|
const body = await req.json();
|
|
33
35
|
const q = req.nextUrl.searchParams.get('q');
|
|
34
36
|
<% if(withService) { %>
|
|
35
|
-
return
|
|
37
|
+
return <%= serviceName %>.update<%= modulePascalName %>(id, q, body);
|
|
36
38
|
<% } else { %>
|
|
37
39
|
return { id, body, q };
|
|
38
40
|
<% } %>
|
|
39
41
|
};
|
|
40
42
|
|
|
41
43
|
@post()
|
|
42
|
-
static create<%=
|
|
44
|
+
static create<%= modulePascalName %> = () => {
|
|
43
45
|
// ...
|
|
44
46
|
};
|
|
45
47
|
|
|
46
48
|
@del(':id')
|
|
47
|
-
static delete<%=
|
|
49
|
+
static delete<%= modulePascalName %> = () => {
|
|
48
50
|
// ...
|
|
49
51
|
};
|
|
50
52
|
}
|