vovk-cli 0.0.1-draft.13 → 0.0.1-draft.131
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/LICENSE +1 -1
- package/README.md +29 -1
- package/client-templates/fullSchema/fullSchema.cjs.ejs +13 -0
- package/client-templates/fullSchema/fullSchema.d.cts.ejs +11 -0
- package/client-templates/main/main.cjs.ejs +15 -0
- package/client-templates/main/main.d.cts.ejs +15 -0
- package/client-templates/module/module.d.mts.ejs +15 -0
- package/client-templates/module/module.mjs.ejs +21 -0
- package/client-templates/ts/index.ts.ejs +24 -0
- package/dist/dev/diffSegmentSchema.d.mts +36 -0
- package/dist/{watcher/diffSchema.mjs → dev/diffSegmentSchema.mjs} +4 -12
- package/dist/{watcher → dev}/ensureSchemaFiles.d.mts +3 -0
- package/dist/{watcher → dev}/ensureSchemaFiles.mjs +12 -30
- package/dist/dev/index.d.mts +6 -0
- package/dist/{watcher → dev}/index.mjs +157 -89
- package/dist/dev/isSegmentSchemaEmpty.d.mts +2 -0
- package/dist/dev/isSegmentSchemaEmpty.mjs +4 -0
- package/dist/{watcher → dev}/logDiffResult.d.mts +1 -1
- package/dist/dev/logDiffResult.mjs +57 -0
- package/dist/dev/writeConfigJson.d.mts +2 -0
- package/dist/dev/writeConfigJson.mjs +15 -0
- package/dist/dev/writeOneSegmentSchemaFile.d.mts +12 -0
- package/dist/{watcher/writeOneSchemaFile.mjs → dev/writeOneSegmentSchemaFile.mjs} +10 -6
- package/dist/generate/ensureClient.d.mts +4 -0
- package/dist/generate/ensureClient.mjs +66 -0
- package/dist/generate/getClientTemplates.d.mts +23 -0
- package/dist/generate/getClientTemplates.mjs +87 -0
- package/dist/generate/getFullSchemaFromJSON.d.mts +3 -0
- package/dist/generate/getFullSchemaFromJSON.mjs +53 -0
- package/dist/generate/index.d.mts +13 -0
- package/dist/generate/index.mjs +182 -0
- package/dist/getProjectInfo/getConfig.d.mts +5 -4
- package/dist/getProjectInfo/getConfig.mjs +28 -7
- package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +2 -1
- package/dist/getProjectInfo/getConfigAbsolutePaths.mjs +4 -1
- package/dist/getProjectInfo/getUserConfig.d.mts +3 -2
- package/dist/getProjectInfo/getUserConfig.mjs +5 -3
- package/dist/getProjectInfo/importUncachedModule.mjs +0 -1
- package/dist/getProjectInfo/importUncachedModuleWorker.mjs +0 -1
- package/dist/getProjectInfo/index.d.mts +14 -6
- package/dist/getProjectInfo/index.mjs +47 -14
- package/dist/index.d.mts +0 -5
- package/dist/index.mjs +55 -65
- package/dist/init/createConfig.d.mts +2 -3
- package/dist/init/createConfig.mjs +11 -7
- package/dist/init/getTemplateFilesFromPackage.d.mts +2 -1
- package/dist/init/getTemplateFilesFromPackage.mjs +2 -3
- package/dist/init/index.d.mts +1 -1
- package/dist/init/index.mjs +29 -28
- package/dist/init/updateDependenciesWithoutInstalling.mjs +3 -5
- package/dist/init/updateNPMScripts.d.mts +3 -1
- package/dist/init/updateNPMScripts.mjs +10 -7
- package/dist/initProgram.d.mts +2 -0
- package/dist/initProgram.mjs +21 -0
- package/dist/locateSegments.d.mts +7 -1
- package/dist/locateSegments.mjs +6 -4
- package/dist/new/addClassToSegmentCode.d.mts +1 -2
- package/dist/new/addClassToSegmentCode.mjs +3 -3
- package/dist/new/addCommonTerms.mjs +1 -0
- package/dist/new/index.d.mts +1 -1
- package/dist/new/index.mjs +2 -1
- package/dist/new/newModule.d.mts +2 -1
- package/dist/new/newModule.mjs +14 -16
- package/dist/new/newSegment.mjs +6 -4
- package/dist/new/render.d.mts +6 -3
- package/dist/new/render.mjs +25 -13
- package/dist/types.d.mts +9 -42
- package/dist/utils/debounceWithArgs.d.mts +2 -2
- package/dist/utils/debounceWithArgs.mjs +24 -9
- package/dist/utils/getAvailablePort.mjs +1 -1
- package/dist/utils/pickSegmentFullSchema.d.mts +2 -0
- package/dist/utils/pickSegmentFullSchema.mjs +8 -0
- package/dist/utils/removeUnlistedDirectories.d.mts +10 -0
- package/dist/utils/removeUnlistedDirectories.mjs +54 -0
- package/dist/utils/resolveAbsoluteModulePath.d.mts +1 -0
- package/dist/utils/resolveAbsoluteModulePath.mjs +6 -0
- package/package.json +25 -21
- package/templates/controller.ejs +20 -21
- package/templates/service.ejs +12 -12
- package/dist/generateClient.d.mts +0 -7
- package/dist/generateClient.mjs +0 -97
- package/dist/postinstall.d.mts +0 -1
- package/dist/postinstall.mjs +0 -24
- package/dist/watcher/diffSchema.d.mts +0 -43
- package/dist/watcher/ensureClient.d.mts +0 -5
- package/dist/watcher/ensureClient.mjs +0 -31
- package/dist/watcher/index.d.mts +0 -6
- package/dist/watcher/isMetadataEmpty.d.mts +0 -2
- package/dist/watcher/isMetadataEmpty.mjs +0 -4
- package/dist/watcher/logDiffResult.mjs +0 -94
- package/dist/watcher/writeOneSchemaFile.d.mts +0 -11
- package/templates/worker.ejs +0 -24
package/dist/index.mjs
CHANGED
|
@@ -1,54 +1,61 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { readFileSync } from 'node:fs';
|
|
4
|
+
import 'dotenv/config';
|
|
4
5
|
import { Command } from 'commander';
|
|
5
6
|
import concurrently from 'concurrently';
|
|
6
7
|
import getAvailablePort from './utils/getAvailablePort.mjs';
|
|
7
8
|
import getProjectInfo from './getProjectInfo/index.mjs';
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import { VovkCLIWatcher } from './watcher/index.mjs';
|
|
11
|
-
import { Init } from './init/index.mjs';
|
|
9
|
+
import generate from './generate/index.mjs';
|
|
10
|
+
import { VovkDev } from './dev/index.mjs';
|
|
12
11
|
import newComponents from './new/index.mjs';
|
|
13
|
-
import '
|
|
12
|
+
import initProgram from './initProgram.mjs';
|
|
13
|
+
import { getFullSchemaFromJSON } from './generate/getFullSchemaFromJSON.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
|
-
.
|
|
21
|
-
.
|
|
22
|
-
.
|
|
23
|
-
.
|
|
20
|
+
.alias('d')
|
|
21
|
+
.description('start schema watcher (optional flag --next-dev to start it with Next.js)')
|
|
22
|
+
.argument('[nextArgs...]', 'extra arguments for the dev command')
|
|
23
|
+
.option('--next-dev', 'start schema watcher and Next.js with automatic port allocation')
|
|
24
|
+
.option('--exit', 'kill the processe when schema and client is generated')
|
|
25
|
+
.action(async (nextArgs, options) => {
|
|
26
|
+
const { nextDev, exit = false } = options;
|
|
24
27
|
const portAttempts = 30;
|
|
25
|
-
const PORT = !
|
|
28
|
+
const PORT = !nextDev
|
|
26
29
|
? process.env.PORT
|
|
27
30
|
: process.env.PORT ||
|
|
28
31
|
(await getAvailablePort(3000, portAttempts, 0, (failedPort, tryingPort) =>
|
|
29
32
|
// eslint-disable-next-line no-console
|
|
30
|
-
console.warn(`🐺
|
|
31
|
-
throw new Error(`🐺 ❌ Failed to find available
|
|
33
|
+
console.warn(`🐺 Port ${failedPort} is in use, trying ${tryingPort} instead.`)).catch(() => {
|
|
34
|
+
throw new Error(`🐺 ❌ Failed to find an available port after ${portAttempts} attempts`);
|
|
32
35
|
}));
|
|
33
36
|
if (!PORT) {
|
|
34
37
|
throw new Error('🐺 ❌ PORT env variable is required');
|
|
35
38
|
}
|
|
36
|
-
|
|
37
|
-
if (options.nextDev) {
|
|
39
|
+
if (nextDev) {
|
|
38
40
|
const { result } = concurrently([
|
|
39
41
|
{
|
|
40
|
-
command: `
|
|
41
|
-
name: 'Vovk.ts Schema Watcher',
|
|
42
|
-
env: Object.assign({ PORT, __VOVK_START_WATCHER_IN_STANDALONE_MODE__: 'true' }, options.clientOut ? { VOVK_CLIENT_OUT_DIR: options.clientOut } : {}),
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
command: `npx next dev ${command.args.join(' ')}`,
|
|
42
|
+
command: `npx next dev ${nextArgs.join(' ')}`,
|
|
46
43
|
name: 'Next.js Development Server',
|
|
47
44
|
env: { PORT },
|
|
48
45
|
},
|
|
46
|
+
{
|
|
47
|
+
command: `node ${import.meta.dirname}/dev/index.mjs`,
|
|
48
|
+
name: 'Vovk Dev Watcher',
|
|
49
|
+
env: {
|
|
50
|
+
PORT,
|
|
51
|
+
__VOVK_START_WATCHER_IN_STANDALONE_MODE__: 'true',
|
|
52
|
+
__VOVK_EXIT__: exit ? 'true' : 'false',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
49
55
|
], {
|
|
50
56
|
killOthers: ['failure', 'success'],
|
|
51
57
|
prefix: 'none',
|
|
58
|
+
successCondition: 'first',
|
|
52
59
|
});
|
|
53
60
|
try {
|
|
54
61
|
await result;
|
|
@@ -58,65 +65,48 @@ program
|
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
else {
|
|
61
|
-
void new
|
|
68
|
+
void new VovkDev().start({ exit });
|
|
62
69
|
}
|
|
63
70
|
});
|
|
64
71
|
program
|
|
65
72
|
.command('generate')
|
|
66
|
-
.
|
|
67
|
-
.
|
|
73
|
+
.alias('g')
|
|
74
|
+
.description('Generate RPC client from schema')
|
|
75
|
+
.option('--out, --client-out-dir <path>', 'path to output directory')
|
|
76
|
+
.option('--template, --templates <templates...>', 'client code templates ("ts", "compiled", "python", "none", a custom path)')
|
|
77
|
+
.option('--emit-full-schema, --full-schema-json [fileName]', 'generate client with full schema')
|
|
78
|
+
.option('--prettify', 'prettify output files')
|
|
79
|
+
.option('--config <config>', 'path to config file')
|
|
68
80
|
.action(async (options) => {
|
|
69
|
-
const
|
|
70
|
-
const {
|
|
71
|
-
const
|
|
81
|
+
const { clientOutDir, templates, prettify, emitFullSchema, config: configPath } = options;
|
|
82
|
+
const projectInfo = await getProjectInfo({ clientOutDir, configPath });
|
|
83
|
+
const { cwd, config } = projectInfo;
|
|
72
84
|
const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
|
|
73
|
-
const
|
|
74
|
-
await
|
|
85
|
+
const fullSchema = await getFullSchemaFromJSON(schemaOutAbsolutePath, projectInfo);
|
|
86
|
+
await generate({
|
|
87
|
+
projectInfo,
|
|
88
|
+
fullSchema,
|
|
89
|
+
templates,
|
|
90
|
+
prettify,
|
|
91
|
+
forceNothingWrittenLog: true,
|
|
92
|
+
emitFullSchema,
|
|
93
|
+
});
|
|
75
94
|
});
|
|
76
|
-
// reused at vovk-init
|
|
77
|
-
export function initProgram(p, command) {
|
|
78
|
-
return p
|
|
79
|
-
.command(command + '[prefix]')
|
|
80
|
-
.description('Initialize Vovk project')
|
|
81
|
-
.option('-y, --yes', 'Skip all prompts and use default values')
|
|
82
|
-
.option('--log-level <level>', 'Set log level', 'info')
|
|
83
|
-
.option('--use-npm', 'Use npm as package manager')
|
|
84
|
-
.option('--use-yarn', 'Use yarn as package manager')
|
|
85
|
-
.option('--use-pnpm', 'Use pnpm as package manager')
|
|
86
|
-
.option('--use-bun', 'Use bun as package manager')
|
|
87
|
-
.option('--skip-install', 'Skip installing dependencies')
|
|
88
|
-
.option('--update-ts-config', 'Update tsconfig.json')
|
|
89
|
-
.option('--update-scripts <mode>', 'Update package.json scripts (implicit or explicit)')
|
|
90
|
-
.option('--validation-library <library>', 'Validation library to use ("vovk-zod", "vovk-yup", "vovk-dto" or another). Set to "none" to skip validation')
|
|
91
|
-
.option('--validate-on-client', 'Validate on client')
|
|
92
|
-
.option('--channel <channel>', 'Channel to use for fetching packages', 'latest')
|
|
93
|
-
.option('--dry-run', 'Do not write files to disk')
|
|
94
|
-
.action((prefix = '.', options) => new Init().main(prefix, options));
|
|
95
|
-
}
|
|
96
|
-
initProgram(program, 'init ');
|
|
97
95
|
program
|
|
98
96
|
.command('new [components...]')
|
|
99
97
|
.alias('n')
|
|
100
|
-
.description('
|
|
101
|
-
.option('-o, --overwrite', '
|
|
102
|
-
.option('--template, --templates <templates...>', '
|
|
103
|
-
.option('--dir <dirname>', '
|
|
104
|
-
.option('--
|
|
105
|
-
.option('--
|
|
98
|
+
.description('create new components. "vovk new [...components] [segmentName/]moduleName" to create a new module or "vovk new segment [segmentName]" to create a new segment')
|
|
99
|
+
.option('-o, --overwrite', 'overwrite existing files')
|
|
100
|
+
.option('--template, --templates <templates...>', 'override config template; accepts an array of strings that correspond the order of the components')
|
|
101
|
+
.option('--dir <dirname>', 'override dirName in template file; relative to the root of the project')
|
|
102
|
+
.option('--empty', 'create an empty module')
|
|
103
|
+
.option('--no-segment-update', 'do not update segment files when creating a new module')
|
|
104
|
+
.option('--dry-run', 'do not write files to disk')
|
|
106
105
|
.action((components, options) => newComponents(components, options));
|
|
107
106
|
program
|
|
108
107
|
.command('help')
|
|
109
108
|
.description('Show help message')
|
|
110
109
|
.action(() => program.help());
|
|
111
|
-
/*
|
|
112
|
-
TODO
|
|
113
|
-
vovk new segment [segmentName]
|
|
114
|
-
vovk new controller service [segmentName/]moduleName
|
|
115
|
-
vovk new c s w [segmentName/]moduleName
|
|
116
|
-
|
|
117
|
-
vovk c s w userApi/user
|
|
118
|
-
vovk new c s w user
|
|
119
|
-
*/
|
|
120
110
|
program.parse(process.argv);
|
|
121
111
|
if (!process.argv.slice(2).length) {
|
|
122
112
|
program.outputHelp();
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type getLogger from '../utils/getLogger.mjs';
|
|
2
2
|
import type { InitOptions } from '../types.mjs';
|
|
3
|
-
export default function createConfig({ root, log,
|
|
3
|
+
export default function createConfig({ root, log, options: { validationLibrary, reactQuery, channel, dryRun }, }: {
|
|
4
4
|
root: string;
|
|
5
5
|
log: ReturnType<typeof getLogger>;
|
|
6
|
-
dryRun
|
|
7
|
-
options: Pick<InitOptions, 'validationLibrary' | 'validateOnClient'>;
|
|
6
|
+
options: Pick<InitOptions, 'validationLibrary' | 'reactQuery' | 'channel' | 'dryRun'>;
|
|
8
7
|
}): Promise<{
|
|
9
8
|
configAbsolutePath: string;
|
|
10
9
|
}>;
|
|
@@ -3,7 +3,7 @@ import fs from 'node:fs/promises';
|
|
|
3
3
|
import getTemplateFilesFromPackage from './getTemplateFilesFromPackage.mjs';
|
|
4
4
|
import prettify from '../utils/prettify.mjs';
|
|
5
5
|
import getFileSystemEntryType, { FileSystemEntryType } from '../utils/getFileSystemEntryType.mjs';
|
|
6
|
-
export default async function createConfig({ root, log,
|
|
6
|
+
export default async function createConfig({ root, log, options: { validationLibrary, reactQuery, channel, dryRun }, }) {
|
|
7
7
|
const config = {};
|
|
8
8
|
const dotConfigPath = path.join(root, '.config');
|
|
9
9
|
const dir = (await getFileSystemEntryType(dotConfigPath)) === FileSystemEntryType.DIRECTORY ? dotConfigPath : root;
|
|
@@ -14,21 +14,25 @@ export default async function createConfig({ root, log, dryRun, options: { valid
|
|
|
14
14
|
const templates = {
|
|
15
15
|
controller: 'vovk-cli/templates/controller.ejs',
|
|
16
16
|
service: 'vovk-cli/templates/service.ejs',
|
|
17
|
-
worker: 'vovk-cli/templates/worker.ejs',
|
|
18
17
|
};
|
|
19
18
|
if (validationLibrary) {
|
|
20
|
-
config.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
config.imports ??= {};
|
|
20
|
+
config.imports.validateOnClient =
|
|
21
|
+
{
|
|
22
|
+
'vovk-dto': `vovk-dto/validateOnClient.js`,
|
|
23
|
+
}[validationLibrary] ?? 'vovk-ajv';
|
|
24
24
|
try {
|
|
25
|
-
const validationTemplates = await getTemplateFilesFromPackage(validationLibrary);
|
|
25
|
+
const validationTemplates = await getTemplateFilesFromPackage(validationLibrary, channel);
|
|
26
26
|
Object.assign(templates, validationTemplates);
|
|
27
27
|
}
|
|
28
28
|
catch (error) {
|
|
29
29
|
log.warn(`Failed to fetch validation library templates: ${error.message}`);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
if (reactQuery) {
|
|
33
|
+
config.imports ??= {};
|
|
34
|
+
config.imports.createRPC = 'vovk-react-query';
|
|
35
|
+
}
|
|
32
36
|
config.templates = templates;
|
|
33
37
|
const configStr = await prettify(`/** @type {import('vovk-cli').VovkConfig} */
|
|
34
38
|
const config = ${JSON.stringify(config, null, 2)};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { InitOptions } from '../types.mjs';
|
|
1
2
|
/**
|
|
2
3
|
* Retrieves a list of files in the 'templates' folder of an NPM package.
|
|
3
4
|
* @param packageName - The name of the NPM package.
|
|
4
5
|
* @returns A promise that resolves to an array of file paths.
|
|
5
6
|
*/
|
|
6
|
-
export default function
|
|
7
|
+
export default function getTemplateFilesFromPackage(packageName: string, channel?: InitOptions['channel']): Promise<Record<string, string>>;
|
|
@@ -2,13 +2,13 @@ import { Readable } from 'node:stream';
|
|
|
2
2
|
import { createGunzip } from 'node:zlib';
|
|
3
3
|
import tar from 'tar-stream';
|
|
4
4
|
import getNPMPackageMetadata from '../utils/getNPMPackageMetadata.mjs';
|
|
5
|
+
// Crereated with AI
|
|
5
6
|
/**
|
|
6
7
|
* Retrieves a list of files in the 'templates' folder of an NPM package.
|
|
7
8
|
* @param packageName - The name of the NPM package.
|
|
8
9
|
* @returns A promise that resolves to an array of file paths.
|
|
9
10
|
*/
|
|
10
|
-
export default async function
|
|
11
|
-
) {
|
|
11
|
+
export default async function getTemplateFilesFromPackage(packageName, channel = 'latest') {
|
|
12
12
|
const metadata = await getNPMPackageMetadata(packageName);
|
|
13
13
|
const latestVersion = metadata['dist-tags'][channel];
|
|
14
14
|
const tarballUrl = metadata.versions[latestVersion].dist.tarball;
|
|
@@ -37,7 +37,6 @@ function extractTemplatesFromTarball(tarballBuffer) {
|
|
|
37
37
|
const files = [];
|
|
38
38
|
extract.on('entry', (header, stream, next) => {
|
|
39
39
|
const filePath = header.name;
|
|
40
|
-
// TODO revisit comments
|
|
41
40
|
// Check if the file is in the 'templates' folder
|
|
42
41
|
if (filePath.startsWith('package/templates/')) {
|
|
43
42
|
files.push(filePath.replace('package/', ''));
|
package/dist/init/index.d.mts
CHANGED
|
@@ -4,5 +4,5 @@ export declare class Init {
|
|
|
4
4
|
#private;
|
|
5
5
|
root: string;
|
|
6
6
|
log: ReturnType<typeof getLogger>;
|
|
7
|
-
main(prefix: string, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary,
|
|
7
|
+
main(prefix: string, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, reactQuery, dryRun, channel, }: InitOptions): Promise<void>;
|
|
8
8
|
}
|
package/dist/init/index.mjs
CHANGED
|
@@ -7,18 +7,19 @@ 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,
|
|
20
|
+
async #init({ configPaths, pkgJson, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, reactQuery, dryRun, channel, }) {
|
|
20
21
|
const { log, root } = this;
|
|
21
|
-
const dependencies = ['vovk'];
|
|
22
|
+
const dependencies = ['vovk', 'vovk-client', 'vovk-openapi'];
|
|
22
23
|
const devDependencies = ['vovk-cli'];
|
|
23
24
|
// delete older config files
|
|
24
25
|
if (configPaths.length) {
|
|
@@ -26,17 +27,19 @@ export class Init {
|
|
|
26
27
|
log.debug(`Deleted existing config file${configPaths.length > 1 ? 's' : ''} at ${configPaths.join(', ')}`);
|
|
27
28
|
}
|
|
28
29
|
if (validationLibrary) {
|
|
29
|
-
dependencies.push(validationLibrary
|
|
30
|
-
dependencies.push(...({
|
|
30
|
+
dependencies.push(validationLibrary, 'vovk-ajv', ...({
|
|
31
31
|
'vovk-zod': ['zod'],
|
|
32
32
|
'vovk-yup': ['yup'],
|
|
33
|
-
'vovk-dto': ['class-validator', 'class-transformer'],
|
|
33
|
+
'vovk-dto': ['class-validator', 'class-transformer', 'vovk-mapped-types', 'reflect-metadata'],
|
|
34
34
|
}[validationLibrary] ?? []));
|
|
35
35
|
}
|
|
36
|
+
if (reactQuery) {
|
|
37
|
+
dependencies.push('vovk-react-query', '@tanstack/react-query');
|
|
38
|
+
}
|
|
36
39
|
if (updateScripts) {
|
|
37
40
|
try {
|
|
38
41
|
if (!dryRun)
|
|
39
|
-
await updateNPMScripts(root, updateScripts);
|
|
42
|
+
await updateNPMScripts(pkgJson, root, updateScripts);
|
|
40
43
|
log.info('Updated scripts at package.json');
|
|
41
44
|
}
|
|
42
45
|
catch (error) {
|
|
@@ -101,8 +104,7 @@ export class Init {
|
|
|
101
104
|
const { configAbsolutePath } = await createConfig({
|
|
102
105
|
root,
|
|
103
106
|
log,
|
|
104
|
-
options: { validationLibrary,
|
|
105
|
-
dryRun,
|
|
107
|
+
options: { validationLibrary, reactQuery, channel, dryRun },
|
|
106
108
|
});
|
|
107
109
|
log.info('Config created successfully at ' + configAbsolutePath);
|
|
108
110
|
}
|
|
@@ -110,15 +112,16 @@ export class Init {
|
|
|
110
112
|
log.error(`Failed to create config: ${error.message}`);
|
|
111
113
|
}
|
|
112
114
|
}
|
|
113
|
-
async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary,
|
|
115
|
+
async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, reactQuery, dryRun, channel, }) {
|
|
114
116
|
const cwd = process.cwd();
|
|
115
117
|
const root = path.resolve(cwd, prefix);
|
|
116
118
|
const log = getLogger(logLevel);
|
|
119
|
+
const pkgJson = await NPMCliPackageJson.load(root);
|
|
117
120
|
this.root = root;
|
|
118
121
|
this.log = log;
|
|
119
122
|
const configPaths = await getConfigPaths({ cwd, relativePath: prefix });
|
|
120
123
|
if (yes) {
|
|
121
|
-
return this.#init({ configPaths }, {
|
|
124
|
+
return this.#init({ configPaths, pkgJson }, {
|
|
122
125
|
useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
|
|
123
126
|
useYarn: useYarn ?? false,
|
|
124
127
|
usePnpm: usePnpm ?? false,
|
|
@@ -127,7 +130,7 @@ export class Init {
|
|
|
127
130
|
updateTsConfig: updateTsConfig ?? true,
|
|
128
131
|
updateScripts: updateScripts ?? 'implicit',
|
|
129
132
|
validationLibrary: validationLibrary?.toLocaleLowerCase() === 'none' ? null : (validationLibrary ?? 'vovk-zod'),
|
|
130
|
-
|
|
133
|
+
reactQuery: reactQuery ?? true,
|
|
131
134
|
dryRun: dryRun ?? false,
|
|
132
135
|
channel: channel ?? 'latest',
|
|
133
136
|
});
|
|
@@ -165,56 +168,54 @@ export class Init {
|
|
|
165
168
|
{
|
|
166
169
|
name: 'vovk-dto',
|
|
167
170
|
value: 'vovk-dto',
|
|
168
|
-
description: 'Use class-validator
|
|
171
|
+
description: 'Use class-validator for data validation. Also installs class-transformer, vovk-mapped-types and reflect-metadata',
|
|
169
172
|
},
|
|
170
173
|
{ name: 'None', value: null, description: 'Install validation library later' },
|
|
171
174
|
],
|
|
172
175
|
})));
|
|
173
|
-
if (validationLibrary) {
|
|
174
|
-
validateOnClient =
|
|
175
|
-
validateOnClient ??
|
|
176
|
-
(await confirm({
|
|
177
|
-
message: 'Do you want to enable client validation?',
|
|
178
|
-
}));
|
|
179
|
-
}
|
|
180
176
|
updateScripts =
|
|
181
177
|
updateScripts ??
|
|
182
178
|
(await select({
|
|
183
|
-
message: 'Do you want to update
|
|
179
|
+
message: 'Do you want to update "dev" NPM script at package.json?',
|
|
184
180
|
default: 'implicit',
|
|
185
181
|
choices: [
|
|
186
182
|
{
|
|
187
183
|
name: 'Yes, use "concurrently" implicitly',
|
|
188
|
-
description: `The "dev" script will use "concurrently" API internally in order to run "next dev" and "vovk dev" together and automatically look for an available port ${chalk.whiteBright.bold(`"vovk dev --next-dev"`)}`,
|
|
189
184
|
value: 'implicit',
|
|
185
|
+
description: `The 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')}"`)}`,
|
|
190
186
|
},
|
|
191
187
|
{
|
|
192
188
|
name: 'Yes, use "concurrently" explicitly',
|
|
193
189
|
value: 'explicit',
|
|
194
|
-
description: `The
|
|
190
|
+
description: `The script will use pre-defined PORT variable and run "next dev" and "vovk dev" as "concurrently" CLI arguments ${chalk.whiteBright.bold(`"${getDevScript(pkgJson, 'explicit')}"`)}`,
|
|
195
191
|
},
|
|
196
192
|
{
|
|
197
193
|
name: 'No',
|
|
198
194
|
value: undefined,
|
|
199
|
-
description: 'Add the scripts manually',
|
|
195
|
+
description: 'Add the NPM scripts manually',
|
|
200
196
|
},
|
|
201
197
|
],
|
|
202
198
|
}));
|
|
199
|
+
reactQuery =
|
|
200
|
+
reactQuery ??
|
|
201
|
+
(await confirm({
|
|
202
|
+
message: 'Do you want to use @tanstack/react-query for data fetching at React components?',
|
|
203
|
+
}));
|
|
203
204
|
if (typeof updateTsConfig === 'undefined') {
|
|
204
205
|
let shouldAsk = false;
|
|
205
206
|
try {
|
|
206
207
|
shouldAsk = !(await checkTSConfigForExperimentalDecorators(root));
|
|
207
208
|
}
|
|
208
209
|
catch (error) {
|
|
209
|
-
log.error(`Failed to check tsconfig.json for experimentalDecorators: ${error.message}`);
|
|
210
|
+
log.error(`Failed to check tsconfig.json for "experimentalDecorators": ${error.message}`);
|
|
210
211
|
}
|
|
211
212
|
if (shouldAsk) {
|
|
212
213
|
updateTsConfig = await confirm({
|
|
213
|
-
message: 'Do you want to add experimentalDecorators to tsconfig.json?',
|
|
214
|
+
message: 'Do you want to add "experimentalDecorators" option to tsconfig.json?',
|
|
214
215
|
});
|
|
215
216
|
}
|
|
216
217
|
}
|
|
217
|
-
await this.#init({ configPaths }, {
|
|
218
|
+
await this.#init({ configPaths, pkgJson }, {
|
|
218
219
|
useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
|
|
219
220
|
useYarn: useYarn ?? false,
|
|
220
221
|
usePnpm: usePnpm ?? false,
|
|
@@ -223,7 +224,7 @@ export class Init {
|
|
|
223
224
|
updateTsConfig,
|
|
224
225
|
updateScripts,
|
|
225
226
|
validationLibrary,
|
|
226
|
-
|
|
227
|
+
reactQuery,
|
|
227
228
|
dryRun,
|
|
228
229
|
channel,
|
|
229
230
|
});
|
|
@@ -4,10 +4,8 @@ import chalk from 'chalk';
|
|
|
4
4
|
import getNPMPackageMetadata from '../utils/getNPMPackageMetadata.mjs';
|
|
5
5
|
async function updateDeps({ packageJson, packageNames, channel, key, }) {
|
|
6
6
|
return Promise.all(packageNames.map(async (packageName) => {
|
|
7
|
-
if (packageJson[key]?.[packageName])
|
|
8
|
-
return; // Skip if already present
|
|
9
7
|
const metadata = await getNPMPackageMetadata(packageName);
|
|
10
|
-
const isVovk = packageName.startsWith('vovk');
|
|
8
|
+
const isVovk = packageName.startsWith('vovk') && packageName !== 'vovk-mapped-types';
|
|
11
9
|
const latestVersion = metadata['dist-tags'][isVovk ? (channel ?? 'latest') : 'latest'];
|
|
12
10
|
if (!packageJson[key]) {
|
|
13
11
|
packageJson[key] = {};
|
|
@@ -23,10 +21,10 @@ export default async function updateDependenciesWithoutInstalling({ log, dir, de
|
|
|
23
21
|
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
24
22
|
log.info('Added dependencies to package.json:');
|
|
25
23
|
for (const dependency of dependencyNames) {
|
|
26
|
-
log.raw.
|
|
24
|
+
log.raw.info(` - ${chalk.cyan(dependency)}`);
|
|
27
25
|
}
|
|
28
26
|
log.info('Added devDependencies to package.json:');
|
|
29
27
|
for (const dependency of devDependencyNames) {
|
|
30
|
-
log.raw.
|
|
28
|
+
log.raw.info(` - ${chalk.cyan(dependency)}`);
|
|
31
29
|
}
|
|
32
30
|
}
|
|
@@ -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,12 +1,15 @@
|
|
|
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
|
-
|
|
7
|
-
dev: updateScriptsMode
|
|
8
|
-
? "PORT=3000 concurrently 'vovk dev' 'next dev' --kill-others"
|
|
9
|
-
: 'vovk dev --next-dev',
|
|
11
|
+
...pkgJson.content.scripts,
|
|
12
|
+
dev: getDevScript(pkgJson, updateScriptsMode),
|
|
10
13
|
},
|
|
11
14
|
});
|
|
12
15
|
await pkgJson.save();
|
|
@@ -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.ts 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')
|
|
17
|
+
.option('--react-query', 'use @tanstack/react-query for data fetching inside components')
|
|
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
|
+
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import type { VovkStrictConfig } from 'vovk';
|
|
1
2
|
export type Segment = {
|
|
2
3
|
routeFilePath: string;
|
|
3
4
|
segmentName: string;
|
|
5
|
+
segmentImportPath: string;
|
|
4
6
|
};
|
|
5
|
-
export default function locateSegments(dir
|
|
7
|
+
export default function locateSegments({ dir, rootDir, config, }: {
|
|
8
|
+
dir: string;
|
|
9
|
+
rootDir?: string;
|
|
10
|
+
config: VovkStrictConfig | null;
|
|
11
|
+
}): Promise<Segment[]>;
|
package/dist/locateSegments.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import getFileSystemEntryType from './utils/getFileSystemEntryType.mjs';
|
|
4
|
-
export default async function locateSegments(dir, rootDir
|
|
4
|
+
export default async function locateSegments({ dir, rootDir, config, }) {
|
|
5
5
|
let results = [];
|
|
6
|
+
rootDir = rootDir ?? dir;
|
|
6
7
|
// Read the contents of the directory
|
|
7
8
|
const list = await fs.readdir(dir);
|
|
8
9
|
// Iterate through each item in the directory
|
|
@@ -16,12 +17,13 @@ export default async function locateSegments(dir, rootDir = dir) {
|
|
|
16
17
|
const routeFilePath = path.join(filePath, 'route.ts');
|
|
17
18
|
if (await getFileSystemEntryType(routeFilePath)) {
|
|
18
19
|
// Calculate the basePath relative to the root directory
|
|
19
|
-
const segmentName = path.relative(rootDir, dir);
|
|
20
|
-
|
|
20
|
+
const segmentName = path.relative(rootDir, dir).replace(/\\/g, '/'); // windows fix
|
|
21
|
+
const segmentImportPath = path.relative(config?.clientOutDir ?? '.__error', routeFilePath);
|
|
22
|
+
results.push({ routeFilePath, segmentName, segmentImportPath });
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
// Recursively search inside subdirectories
|
|
24
|
-
const subDirResults = await locateSegments(filePath, rootDir);
|
|
26
|
+
const subDirResults = await locateSegments({ dir: filePath, rootDir, config });
|
|
25
27
|
results = results.concat(subDirResults);
|
|
26
28
|
}
|
|
27
29
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export default function addClassToSegmentCode(segmentSourceCode: string, { sourceName, compiledName,
|
|
1
|
+
export default function addClassToSegmentCode(segmentSourceCode: string, { sourceName, compiledName, importPath, }: {
|
|
2
2
|
sourceName: string;
|
|
3
3
|
compiledName: string;
|
|
4
|
-
type: 'worker' | 'controller';
|
|
5
4
|
importPath: string;
|
|
6
5
|
}): string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Project, QuoteKind, SyntaxKind } from 'ts-morph';
|
|
2
|
-
export default function addClassToSegmentCode(segmentSourceCode, { sourceName, compiledName,
|
|
2
|
+
export default function addClassToSegmentCode(segmentSourceCode, { sourceName, compiledName, importPath, }) {
|
|
3
3
|
const project = new Project({
|
|
4
4
|
manipulationSettings: {
|
|
5
5
|
quoteKind: QuoteKind.Single,
|
|
@@ -16,8 +16,8 @@ export default function addClassToSegmentCode(segmentSourceCode, { sourceName, c
|
|
|
16
16
|
moduleSpecifier: importPath,
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
|
-
// Get the variable declaration for controllers
|
|
20
|
-
const variableDeclaration = sourceFile.getVariableDeclaration(
|
|
19
|
+
// Get the variable declaration for controllers
|
|
20
|
+
const variableDeclaration = sourceFile.getVariableDeclaration('controllers');
|
|
21
21
|
if (variableDeclaration) {
|
|
22
22
|
const initializer = variableDeclaration.getInitializer();
|
|
23
23
|
if (initializer && initializer.getKind() === SyntaxKind.ObjectLiteralExpression) {
|
package/dist/new/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { NewOptions } from '../types.mjs';
|
|
2
|
-
export default function newComponents(components: string[], { dryRun, dir, templates, overwrite, noSegmentUpdate }: NewOptions): Promise<void>;
|
|
2
|
+
export default function newComponents(components: string[], { dryRun, dir, templates, overwrite, noSegmentUpdate, empty }: NewOptions): Promise<void>;
|
package/dist/new/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import newModule from './newModule.mjs';
|
|
2
2
|
import newSegment from './newSegment.mjs';
|
|
3
|
-
export default async function newComponents(components, { dryRun, dir, templates, overwrite, noSegmentUpdate }) {
|
|
3
|
+
export default async function newComponents(components, { dryRun, dir, templates, overwrite, noSegmentUpdate, empty }) {
|
|
4
4
|
if (components[0] === 'segment' || components[0] === 'segments') {
|
|
5
5
|
// vovk new segment [segmentName]
|
|
6
6
|
let segmentNames = components
|
|
@@ -31,6 +31,7 @@ export default async function newComponents(components, { dryRun, dir, templates
|
|
|
31
31
|
overwrite,
|
|
32
32
|
noSegmentUpdate,
|
|
33
33
|
dryRun,
|
|
34
|
+
empty,
|
|
34
35
|
});
|
|
35
36
|
}
|
|
36
37
|
}
|