vovk-cli 0.0.1-draft.37 → 0.0.1-draft.370

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.
Files changed (142) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +29 -1
  3. package/client-templates/cjs/index.cjs.ejs +19 -0
  4. package/client-templates/cjs/index.d.cts.ejs +22 -0
  5. package/client-templates/mixins/mixins.d.ts.ejs +64 -0
  6. package/client-templates/mixins/mixins.json.ejs +1 -0
  7. package/client-templates/mjs/index.d.mts.ejs +22 -0
  8. package/client-templates/mjs/index.mjs.ejs +18 -0
  9. package/client-templates/openapiCjs/openapi.cjs.ejs +4 -0
  10. package/client-templates/openapiCjs/openapi.d.cts.ejs +4 -0
  11. package/client-templates/openapiJson/openapi.json.ejs +1 -0
  12. package/client-templates/openapiTs/openapi.ts.ejs +4 -0
  13. package/client-templates/packageJson/package.json.ejs +1 -0
  14. package/client-templates/readme/README.md.ejs +39 -0
  15. package/client-templates/schemaCjs/schema.cjs.ejs +24 -0
  16. package/client-templates/schemaCjs/schema.d.cts.ejs +10 -0
  17. package/client-templates/schemaJson/schema.json.ejs +1 -0
  18. package/client-templates/schemaTs/schema.ts.ejs +28 -0
  19. package/client-templates/ts/index.ts.ejs +27 -0
  20. package/dist/bundle/index.d.mts +8 -0
  21. package/dist/bundle/index.mjs +103 -0
  22. package/dist/dev/diffSegmentSchema.d.mts +36 -0
  23. package/dist/dev/{diffSchema.mjs → diffSegmentSchema.mjs} +3 -11
  24. package/dist/dev/ensureSchemaFiles.d.mts +4 -1
  25. package/dist/dev/ensureSchemaFiles.mjs +15 -31
  26. package/dist/dev/index.d.mts +5 -1
  27. package/dist/dev/index.mjs +191 -80
  28. package/dist/dev/logDiffResult.d.mts +1 -1
  29. package/dist/dev/logDiffResult.mjs +6 -43
  30. package/dist/dev/writeMetaJson.d.mts +2 -0
  31. package/dist/dev/writeMetaJson.mjs +20 -0
  32. package/dist/dev/writeOneSegmentSchemaFile.d.mts +12 -0
  33. package/dist/dev/{writeOneSchemaFile.mjs → writeOneSegmentSchemaFile.mjs} +10 -6
  34. package/dist/generate/ensureClient.d.mts +3 -0
  35. package/dist/generate/ensureClient.mjs +28 -0
  36. package/dist/generate/generate.d.mts +13 -0
  37. package/dist/generate/generate.mjs +306 -0
  38. package/dist/generate/getClientTemplateFiles.d.mts +20 -0
  39. package/dist/generate/getClientTemplateFiles.mjs +85 -0
  40. package/dist/generate/getProjectFullSchema.d.mts +8 -0
  41. package/dist/generate/getProjectFullSchema.mjs +66 -0
  42. package/dist/generate/getTemplateClientImports.d.mts +19 -0
  43. package/dist/generate/getTemplateClientImports.mjs +49 -0
  44. package/dist/generate/index.d.mts +33 -0
  45. package/dist/generate/index.mjs +186 -0
  46. package/dist/generate/writeOneClientFile.d.mts +42 -0
  47. package/dist/generate/writeOneClientFile.mjs +139 -0
  48. package/dist/getProjectInfo/getConfig/getConfigAbsolutePaths.d.mts +5 -0
  49. package/dist/getProjectInfo/{getConfigAbsolutePaths.mjs → getConfig/getConfigAbsolutePaths.mjs} +4 -1
  50. package/dist/getProjectInfo/{getRelativeSrcRoot.d.mts → getConfig/getRelativeSrcRoot.d.mts} +1 -1
  51. package/dist/getProjectInfo/{getRelativeSrcRoot.mjs → getConfig/getRelativeSrcRoot.mjs} +2 -2
  52. package/dist/getProjectInfo/getConfig/getTemplateDefs.d.mts +24 -0
  53. package/dist/getProjectInfo/getConfig/getTemplateDefs.mjs +165 -0
  54. package/dist/getProjectInfo/{getUserConfig.d.mts → getConfig/getUserConfig.d.mts} +3 -2
  55. package/dist/getProjectInfo/{getUserConfig.mjs → getConfig/getUserConfig.mjs} +3 -3
  56. package/dist/getProjectInfo/{importUncachedModule.mjs → getConfig/importUncachedModule.mjs} +1 -4
  57. package/dist/getProjectInfo/getConfig/index.d.mts +78 -0
  58. package/dist/getProjectInfo/getConfig/index.mjs +91 -0
  59. package/dist/getProjectInfo/getMetaSchema.d.mts +8 -0
  60. package/dist/getProjectInfo/getMetaSchema.mjs +27 -0
  61. package/dist/getProjectInfo/index.d.mts +14 -9
  62. package/dist/getProjectInfo/index.mjs +24 -22
  63. package/dist/index.d.mts +2 -2
  64. package/dist/index.mjs +117 -35
  65. package/dist/init/createConfig.d.mts +2 -2
  66. package/dist/init/createConfig.mjs +40 -13
  67. package/dist/init/createStandardSchemaValidatorFile.d.mts +4 -0
  68. package/dist/init/createStandardSchemaValidatorFile.mjs +52 -0
  69. package/dist/init/getTemplateFilesFromPackage.mjs +10 -5
  70. package/dist/init/index.d.mts +2 -2
  71. package/dist/init/index.mjs +123 -72
  72. package/dist/init/installDependencies.mjs +4 -2
  73. package/dist/init/logUpdateDependenciesError.d.mts +3 -1
  74. package/dist/init/logUpdateDependenciesError.mjs +7 -1
  75. package/dist/init/updateDependenciesWithoutInstalling.mjs +39 -9
  76. package/dist/init/updateNPMScripts.d.mts +3 -1
  77. package/dist/init/updateNPMScripts.mjs +10 -7
  78. package/dist/init/updateTypeScriptConfig.d.mts +4 -1
  79. package/dist/init/updateTypeScriptConfig.mjs +11 -7
  80. package/dist/initProgram.d.mts +1 -1
  81. package/dist/initProgram.mjs +17 -17
  82. package/dist/locateSegments.d.mts +8 -1
  83. package/dist/locateSegments.mjs +13 -3
  84. package/dist/new/addClassToSegmentCode.d.mts +1 -2
  85. package/dist/new/addClassToSegmentCode.mjs +3 -3
  86. package/dist/new/index.d.mts +2 -1
  87. package/dist/new/index.mjs +5 -3
  88. package/dist/new/newModule.d.mts +5 -2
  89. package/dist/new/newModule.mjs +23 -22
  90. package/dist/new/newSegment.d.mts +4 -1
  91. package/dist/new/newSegment.mjs +19 -11
  92. package/dist/new/render.d.mts +7 -3
  93. package/dist/new/render.mjs +31 -10
  94. package/dist/types.d.mts +66 -44
  95. package/dist/utils/compileJSONSchemaToTypeScriptType.d.mts +5 -0
  96. package/dist/utils/compileJSONSchemaToTypeScriptType.mjs +9 -0
  97. package/dist/utils/compileTs.d.mts +12 -0
  98. package/dist/utils/compileTs.mjs +261 -0
  99. package/dist/utils/debounceWithArgs.d.mts +2 -2
  100. package/dist/utils/debounceWithArgs.mjs +24 -6
  101. package/dist/utils/deepExtend.d.mts +54 -0
  102. package/dist/utils/deepExtend.mjs +129 -0
  103. package/dist/utils/formatLoggedSegmentName.d.mts +3 -1
  104. package/dist/utils/formatLoggedSegmentName.mjs +3 -2
  105. package/dist/utils/generateFnName.d.mts +23 -0
  106. package/dist/utils/generateFnName.mjs +76 -0
  107. package/dist/utils/getPackageJson.d.mts +3 -0
  108. package/dist/utils/getPackageJson.mjs +22 -0
  109. package/dist/utils/getPublicModuleNameFromPath.d.mts +4 -0
  110. package/dist/utils/getPublicModuleNameFromPath.mjs +9 -0
  111. package/dist/utils/normalizeOpenAPIMixin.d.mts +14 -0
  112. package/dist/utils/normalizeOpenAPIMixin.mjs +114 -0
  113. package/dist/utils/pickSegmentFullSchema.d.mts +3 -0
  114. package/dist/utils/pickSegmentFullSchema.mjs +15 -0
  115. package/dist/utils/removeUnlistedDirectories.d.mts +10 -0
  116. package/dist/utils/removeUnlistedDirectories.mjs +61 -0
  117. package/dist/utils/resolveAbsoluteModulePath.d.mts +2 -0
  118. package/dist/utils/resolveAbsoluteModulePath.mjs +32 -0
  119. package/module-templates/arktype/controller.ts.ejs +68 -0
  120. package/module-templates/type/controller.ts.ejs +56 -0
  121. package/module-templates/type/service.ts.ejs +28 -0
  122. package/module-templates/valibot/controller.ts.ejs +68 -0
  123. package/package.json +40 -22
  124. package/dist/dev/diffSchema.d.mts +0 -43
  125. package/dist/dev/ensureClient.d.mts +0 -5
  126. package/dist/dev/ensureClient.mjs +0 -31
  127. package/dist/dev/isMetadataEmpty.d.mts +0 -2
  128. package/dist/dev/isMetadataEmpty.mjs +0 -4
  129. package/dist/dev/writeOneSchemaFile.d.mts +0 -11
  130. package/dist/generateClient.d.mts +0 -7
  131. package/dist/generateClient.mjs +0 -97
  132. package/dist/getProjectInfo/getConfig.d.mts +0 -11
  133. package/dist/getProjectInfo/getConfig.mjs +0 -29
  134. package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +0 -4
  135. package/dist/postinstall.d.mts +0 -1
  136. package/dist/postinstall.mjs +0 -24
  137. package/templates/controller.ejs +0 -52
  138. package/templates/service.ejs +0 -27
  139. package/templates/worker.ejs +0 -24
  140. /package/dist/getProjectInfo/{importUncachedModule.d.mts → getConfig/importUncachedModule.d.mts} +0 -0
  141. /package/dist/getProjectInfo/{importUncachedModuleWorker.d.mts → getConfig/importUncachedModuleWorker.d.mts} +0 -0
  142. /package/dist/getProjectInfo/{importUncachedModuleWorker.mjs → getConfig/importUncachedModuleWorker.mjs} +0 -0
@@ -0,0 +1,52 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import getRelativeSrcRoot from '../getProjectInfo/getConfig/getRelativeSrcRoot.mjs';
4
+ function getCode(validationLibrary) {
5
+ if (validationLibrary === 'valibot') {
6
+ return `
7
+ import { createStandardValidation } from 'vovk';
8
+ import { toJsonSchema } from '@valibot/to-json-schema';
9
+
10
+ const withValibot = createStandardValidation({
11
+ toJSONSchema: (model) => toJsonSchema(model, {
12
+ overrideSchema(context) {
13
+ if (context.valibotSchema.type === 'file') {
14
+ return { type: 'string', format: 'binary' };
15
+ }
16
+ },
17
+ }),
18
+ });
19
+
20
+ export default withValibot;
21
+ `.trimStart();
22
+ }
23
+ if (validationLibrary === 'arktype') {
24
+ return `
25
+ import { createStandardValidation } from 'vovk';
26
+ import type { type } from 'arktype';
27
+
28
+ const withArk = createStandardValidation({
29
+ toJSONSchema: (model: type) => model.toJsonSchema({
30
+ fallback: {
31
+ proto: (ctx) => ctx.proto === File ? {
32
+ type: "string",
33
+ format: "binary",
34
+ } : ctx.base,
35
+ default: (ctx) => ctx.base
36
+ }
37
+ })
38
+ });
39
+
40
+ export default withArk;
41
+ `.trimStart();
42
+ }
43
+ throw new Error(`Unknown validation library: ${validationLibrary}`);
44
+ }
45
+ export async function createStandardSchemaValidatorFile({ root, validationLibrary, }) {
46
+ const code = getCode(validationLibrary);
47
+ const srcRoot = (await getRelativeSrcRoot({ cwd: root })) ?? '.';
48
+ const libDir = path.resolve(root, srcRoot, 'lib');
49
+ const filePath = path.join(libDir, `${validationLibrary === 'arktype' ? 'withArk' : 'withValibot'}.ts`);
50
+ await fs.mkdir(libDir, { recursive: true });
51
+ await fs.writeFile(filePath, code);
52
+ }
@@ -2,7 +2,6 @@ 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
6
5
  /**
7
6
  * Retrieves a list of files in the 'templates' folder of an NPM package.
8
7
  * @param packageName - The name of the NPM package.
@@ -22,8 +21,14 @@ export default async function getTemplateFilesFromPackage(packageName, channel =
22
21
  // Extract the tarball in memory and collect template files
23
22
  const templateFiles = await extractTemplatesFromTarball(tarballBuffer);
24
23
  const entries = templateFiles
25
- .filter((fileName) => fileName.startsWith('templates/') && !fileName.endsWith('/') && fileName.endsWith('.ejs'))
26
- .map((fileName) => [fileName.substring('templates/'.length).replace(/\.ejs$/, ''), `${packageName}/${fileName}`]);
24
+ .filter((fileName) => fileName.startsWith('module-templates/') && !fileName.endsWith('/') && fileName.endsWith('.ts.ejs'))
25
+ .map((fileName) => [
26
+ fileName
27
+ .substring('module-templates/'.length)
28
+ .replace(/\.ts\.ejs$/, '')
29
+ .toLowerCase(),
30
+ `${packageName}/${fileName}`,
31
+ ]);
27
32
  return Object.fromEntries(entries);
28
33
  }
29
34
  /**
@@ -37,8 +42,8 @@ function extractTemplatesFromTarball(tarballBuffer) {
37
42
  const files = [];
38
43
  extract.on('entry', (header, stream, next) => {
39
44
  const filePath = header.name;
40
- // Check if the file is in the 'templates' folder
41
- if (filePath.startsWith('package/templates/')) {
45
+ // Check if the file is in the 'module-templates' folder
46
+ if (filePath.startsWith('package/module-templates/')) {
42
47
  files.push(filePath.replace('package/', ''));
43
48
  }
44
49
  stream.on('end', () => next());
@@ -1,8 +1,8 @@
1
- import type { InitOptions } from '../types.mjs';
2
1
  import getLogger from '../utils/getLogger.mjs';
2
+ import type { InitOptions } from '../types.mjs';
3
3
  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, validateOnClient, dryRun, channel, }: InitOptions): Promise<void>;
7
+ main({ prefix, yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, lang, dryRun, channel, }: InitOptions): Promise<void>;
8
8
  }
@@ -1,42 +1,57 @@
1
- import { confirm, select } from '@inquirer/prompts';
1
+ import { confirm, select, checkbox } from '@inquirer/prompts';
2
2
  import path from 'node:path';
3
3
  import fs from 'node:fs/promises';
4
- import getConfigPaths from '../getProjectInfo/getConfigAbsolutePaths.mjs';
5
4
  import chalk from 'chalk';
5
+ import NPMCliPackageJson from '@npmcli/package-json';
6
+ import getConfigPaths from '../getProjectInfo/getConfig/getConfigAbsolutePaths.mjs';
6
7
  import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
7
8
  import installDependencies, { getPackageManager } from './installDependencies.mjs';
8
9
  import getLogger from '../utils/getLogger.mjs';
9
10
  import createConfig from './createConfig.mjs';
10
- import updateNPMScripts from './updateNPMScripts.mjs';
11
+ import updateNPMScripts, { getDevScript } from './updateNPMScripts.mjs';
11
12
  import checkTSConfigForExperimentalDecorators from './checkTSConfigForExperimentalDecorators.mjs';
12
13
  import updateTypeScriptConfig from './updateTypeScriptConfig.mjs';
13
14
  import updateDependenciesWithoutInstalling from './updateDependenciesWithoutInstalling.mjs';
14
15
  import logUpdateDependenciesError from './logUpdateDependenciesError.mjs';
15
16
  import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
17
+ import { createStandardSchemaValidatorFile } from './createStandardSchemaValidatorFile.mjs';
16
18
  export class Init {
17
19
  root;
18
20
  log;
19
- async #init({ configPaths, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, dryRun, channel, }) {
21
+ async #init({ configPaths, pkgJson, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, lang, dryRun, channel, }) {
20
22
  const { log, root } = this;
21
- const dependencies = ['vovk', 'vovk-client'];
23
+ const dependencies = ['vovk', 'vovk-client', 'vovk-ajv'];
22
24
  const devDependencies = ['vovk-cli'];
25
+ if (lang?.includes('py')) {
26
+ devDependencies.push('vovk-python');
27
+ }
28
+ if (lang?.includes('rs')) {
29
+ devDependencies.push('vovk-rust');
30
+ }
23
31
  // delete older config files
24
32
  if (configPaths.length) {
25
33
  await Promise.all(configPaths.map((configPath) => fs.rm(configPath)));
26
34
  log.debug(`Deleted existing config file${configPaths.length > 1 ? 's' : ''} at ${configPaths.join(', ')}`);
27
35
  }
28
36
  if (validationLibrary) {
29
- dependencies.push(validationLibrary);
30
37
  dependencies.push(...({
31
- 'vovk-zod': ['zod'],
32
- 'vovk-yup': ['yup'],
33
- 'vovk-dto': ['class-validator', 'class-transformer'],
38
+ zod: ['zod', 'vovk-zod'],
39
+ 'class-validator': [
40
+ 'class-validator',
41
+ 'class-transformer',
42
+ 'dto-mapped-types',
43
+ 'reflect-metadata',
44
+ 'vovk-dto',
45
+ ],
46
+ yup: ['yup', 'vovk-yup'],
47
+ valibot: ['valibot', '@valibot/to-json-schema'],
48
+ arktype: ['arktype'],
34
49
  }[validationLibrary] ?? []));
35
50
  }
36
51
  if (updateScripts) {
37
52
  try {
38
- if (!dryRun)
39
- await updateNPMScripts(root, updateScripts);
53
+ if (!dryRun && pkgJson)
54
+ await updateNPMScripts(pkgJson, root, updateScripts);
40
55
  log.info('Updated scripts at package.json');
41
56
  }
42
57
  catch (error) {
@@ -48,15 +63,23 @@ export class Init {
48
63
  }
49
64
  if (updateTsConfig) {
50
65
  try {
66
+ const compilerOptions = {
67
+ experimentalDecorators: true,
68
+ };
69
+ if (validationLibrary === 'class-validator') {
70
+ compilerOptions.emitDecoratorMetadata = true;
71
+ }
51
72
  if (!dryRun)
52
- await updateTypeScriptConfig(root);
53
- log.info('Added "experimentalDecorators" to tsconfig.json');
73
+ await updateTypeScriptConfig(root, compilerOptions);
74
+ log.info(`Added ${Object.keys(compilerOptions)
75
+ .map((k) => `"${k}"`)
76
+ .join(' and ')} to tsconfig.json`);
54
77
  }
55
78
  catch (error) {
56
79
  log.error(`Failed to update tsconfig.json: ${error.message}`);
57
80
  }
58
81
  }
59
- if (!dryRun) {
82
+ if (!dryRun && pkgJson) {
60
83
  let depsUpdated = false;
61
84
  try {
62
85
  await updateDependenciesWithoutInstalling({
@@ -70,7 +93,17 @@ export class Init {
70
93
  }
71
94
  catch (e) {
72
95
  const error = e;
73
- logUpdateDependenciesError({ log, error, useNpm, useYarn, usePnpm, useBun, dependencies, devDependencies });
96
+ logUpdateDependenciesError({
97
+ log,
98
+ error,
99
+ useNpm,
100
+ useYarn,
101
+ usePnpm,
102
+ useBun,
103
+ dependencies,
104
+ devDependencies,
105
+ channel,
106
+ });
74
107
  }
75
108
  if (depsUpdated) {
76
109
  const packageManager = getPackageManager({ useNpm, useYarn, usePnpm, useBun });
@@ -92,32 +125,40 @@ export class Init {
92
125
  log.info('Dependencies installed successfully');
93
126
  }
94
127
  catch (error) {
95
- log.warn(`Failed to install dependencies: ${error.message}. Please, install them manually with ${chalkHighlightThing(packageManager + ' install')}`);
128
+ log.warn(`Failed to install dependencies. ${error.message}. Please, install them manually with ${chalkHighlightThing(packageManager + ' install')}`);
96
129
  }
97
130
  }
98
131
  }
99
132
  }
133
+ if (validationLibrary === 'valibot' || validationLibrary === 'arktype') {
134
+ createStandardSchemaValidatorFile({
135
+ root,
136
+ validationLibrary,
137
+ });
138
+ }
100
139
  try {
101
140
  const { configAbsolutePath } = await createConfig({
102
141
  root,
103
142
  log,
104
- options: { validationLibrary, validateOnClient, channel, dryRun },
143
+ options: { validationLibrary, channel, lang, dryRun },
105
144
  });
106
- log.info('Config created successfully at ' + configAbsolutePath);
145
+ log.info('Config created successfully at ' + chalkHighlightThing(configAbsolutePath));
107
146
  }
108
147
  catch (error) {
109
148
  log.error(`Failed to create config: ${error.message}`);
110
149
  }
111
150
  }
112
- async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, dryRun, channel, }) {
151
+ async main({ prefix, yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, lang, dryRun, channel, }) {
113
152
  const cwd = process.cwd();
114
- const root = path.resolve(cwd, prefix);
115
- const log = getLogger(logLevel);
153
+ const root = path.resolve(cwd, prefix ?? '.');
154
+ const log = getLogger(logLevel ?? 'info');
155
+ const pkgJson = await NPMCliPackageJson.load(root).catch(() => null);
116
156
  this.root = root;
117
157
  this.log = log;
118
158
  const configPaths = await getConfigPaths({ cwd, relativePath: prefix });
119
159
  if (yes) {
120
- return this.#init({ configPaths }, {
160
+ return this.#init({ configPaths, pkgJson, cwd }, {
161
+ prefix: prefix ?? '.',
121
162
  useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
122
163
  useYarn: useYarn ?? false,
123
164
  usePnpm: usePnpm ?? false,
@@ -125,17 +166,18 @@ export class Init {
125
166
  skipInstall: skipInstall ?? false,
126
167
  updateTsConfig: updateTsConfig ?? true,
127
168
  updateScripts: updateScripts ?? 'implicit',
128
- validationLibrary: validationLibrary?.toLocaleLowerCase() === 'none' ? null : (validationLibrary ?? 'vovk-zod'),
129
- validateOnClient: validateOnClient ?? true,
169
+ validationLibrary: validationLibrary?.toLocaleLowerCase() === 'none' ? null : (validationLibrary ?? 'zod'),
130
170
  dryRun: dryRun ?? false,
131
171
  channel: channel ?? 'latest',
172
+ lang: lang ?? [],
132
173
  });
133
174
  }
134
175
  if (!(await getFileSystemEntryType(path.join(root, 'package.json')))) {
135
- throw new Error(`package.json not found at ${root}. Run "npx create-next-app" to create a new Next.js project.`);
176
+ log.warn(`${chalkHighlightThing('package.json')} not found at ${chalkHighlightThing(root)}. Run "npx create-next-app" to create a new Next.js project
177
+ .`);
136
178
  }
137
- if (!(await getFileSystemEntryType(path.join(root, 'tsconfig.json')))) {
138
- throw new Error(`tsconfig.json not found at ${root}. Run "npx tsc --init" to create a new tsconfig.json file.`);
179
+ else if (pkgJson && !(await getFileSystemEntryType(path.join(root, 'tsconfig.json')))) {
180
+ log.warn(`${chalkHighlightThing('tsconfig.json')} not found at ${chalkHighlightThing(root)}. Run "npx tsc --init" to create a new tsconfig.json file.`);
139
181
  }
140
182
  if (configPaths.length) {
141
183
  if (!(await confirm({
@@ -149,71 +191,80 @@ export class Init {
149
191
  : (validationLibrary ??
150
192
  (await select({
151
193
  message: 'Choose validation library',
152
- default: 'vovk-zod',
194
+ default: 'zod',
153
195
  choices: [
154
196
  {
155
- name: 'vovk-zod',
156
- value: 'vovk-zod',
197
+ name: 'Zod',
198
+ value: 'zod',
157
199
  description: 'Use Zod for data validation',
158
200
  },
159
201
  {
160
- name: 'vovk-yup',
161
- value: 'vovk-yup',
162
- description: 'Use Yup for data validation',
202
+ name: 'class-validator',
203
+ value: 'class-validator',
204
+ description: 'Use class-validator for data validation',
205
+ },
206
+ {
207
+ name: 'ArkType',
208
+ value: 'arktype',
209
+ description: 'Use ArkType for data validation.',
163
210
  },
164
211
  {
165
- name: 'vovk-dto',
166
- value: 'vovk-dto',
167
- description: 'Use class-validator and class-transformer for data validation',
212
+ name: 'Valibot',
213
+ value: 'valibot',
214
+ description: 'Use Valibot for data validation.',
168
215
  },
169
216
  { name: 'None', value: null, description: 'Install validation library later' },
170
217
  ],
171
218
  })));
172
- if (validationLibrary) {
173
- validateOnClient =
174
- validateOnClient ??
175
- (await confirm({
176
- message: 'Do you want to enable client validation?',
177
- }));
178
- }
179
- updateScripts =
180
- updateScripts ??
181
- (await select({
182
- message: 'Do you want to update package.json by adding "generate" and "dev" NPM scripts?',
183
- default: 'implicit',
184
- choices: [
185
- {
186
- name: 'Yes, use "concurrently" implicitly',
187
- 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(`"vovk dev --next-dev"`)}`,
189
- },
190
- {
191
- name: 'Yes, use "concurrently" explicitly',
192
- 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(`"PORT=3000 concurrently 'next dev' 'vovk dev' --kill-others"`)}`,
194
- },
195
- {
196
- name: 'No',
197
- value: undefined,
198
- description: 'Add the NPM scripts manually',
199
- },
200
- ],
201
- }));
202
- if (typeof updateTsConfig === 'undefined') {
219
+ updateScripts ??= !pkgJson
220
+ ? undefined
221
+ : await select({
222
+ message: 'Do you want to update "dev" and add "prebuild" NPM scripts at package.json?',
223
+ default: 'implicit',
224
+ choices: [
225
+ {
226
+ name: 'Yes, use "concurrently" implicitly',
227
+ value: 'implicit',
228
+ 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')}"`)} and the "prebuild" script will run "vovk generate"`,
229
+ },
230
+ {
231
+ name: 'Yes, use "concurrently" explicitly',
232
+ value: 'explicit',
233
+ 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')}"`)} and the "prebuild" script will run "vovk generate"`,
234
+ },
235
+ {
236
+ name: 'No',
237
+ value: undefined,
238
+ description: 'Add the NPM scripts manually',
239
+ },
240
+ ],
241
+ });
242
+ if (typeof updateTsConfig === 'undefined' && pkgJson) {
203
243
  let shouldAsk = false;
204
244
  try {
205
- shouldAsk = !(await checkTSConfigForExperimentalDecorators(root));
245
+ shouldAsk = !(await checkTSConfigForExperimentalDecorators(root)); // TODO also check for emitDecoratorMetadata when vovk-dto is used
206
246
  }
207
247
  catch (error) {
208
- log.error(`Failed to check tsconfig.json for experimentalDecorators: ${error.message}`);
248
+ log.error(`Failed to check tsconfig.json for "experimentalDecorators": ${error.message}`);
209
249
  }
210
250
  if (shouldAsk) {
251
+ const keys = ['experimentalDecorators'];
252
+ if (validationLibrary === 'class-validator') {
253
+ keys.push('emitDecoratorMetadata');
254
+ }
211
255
  updateTsConfig = await confirm({
212
- message: 'Do you want to add experimentalDecorators to tsconfig.json?',
256
+ message: `Do you want to add ${keys.map((k) => `"${k}"`).join(' and ')} to tsconfig.json? (recommended)`,
213
257
  });
214
258
  }
215
259
  }
216
- await this.#init({ configPaths }, {
260
+ lang ??= await checkbox({
261
+ message: 'Do you want to generate RPC client for other languages besides TypeScript (experimental)?',
262
+ choices: [
263
+ { name: 'Python', value: 'py' },
264
+ { name: 'Rust', value: 'rs' },
265
+ ],
266
+ });
267
+ await this.#init({ configPaths, pkgJson, cwd }, {
217
268
  useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
218
269
  useYarn: useYarn ?? false,
219
270
  usePnpm: usePnpm ?? false,
@@ -222,7 +273,7 @@ export class Init {
222
273
  updateTsConfig,
223
274
  updateScripts,
224
275
  validationLibrary,
225
- validateOnClient,
276
+ lang,
226
277
  dryRun,
227
278
  channel,
228
279
  });
@@ -1,4 +1,5 @@
1
1
  import { spawn } from 'node:child_process';
2
+ import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
2
3
  export function getPackageManager(options) {
3
4
  if (options.useNpm)
4
5
  return 'npm';
@@ -12,9 +13,10 @@ export function getPackageManager(options) {
12
13
  }
13
14
  export default async function installDependencies({ log, cwd, options, }) {
14
15
  const packageManager = getPackageManager(options);
15
- log.info(`Installing dependencies at ${cwd} using ${packageManager}...`);
16
+ log.info(`Installing dependencies at ${chalkHighlightThing(cwd)} using ${chalkHighlightThing(packageManager)}...`);
16
17
  await new Promise((resolve, reject) => {
17
- const child = spawn(packageManager, ['install'], { cwd, stdio: 'inherit' });
18
+ const args = packageManager === 'yarn' ? ['install', '--non-interactive'] : ['install'];
19
+ const child = spawn(packageManager, args, { cwd, stdio: 'inherit' });
18
20
  child.on('close', (code) => {
19
21
  if (code === 0) {
20
22
  resolve();
@@ -1,5 +1,6 @@
1
+ import type { InitOptions } from '../types.mjs';
1
2
  import type getLogger from '../utils/getLogger.mjs';
2
- export default function logUpdateDependenciesError({ useNpm, useYarn, usePnpm, useBun, log, dependencies, devDependencies, error, }: {
3
+ export default function logUpdateDependenciesError({ useNpm, useYarn, usePnpm, useBun, log, dependencies, devDependencies, error, channel, }: {
3
4
  useNpm?: boolean;
4
5
  useYarn?: boolean;
5
6
  usePnpm?: boolean;
@@ -8,4 +9,5 @@ export default function logUpdateDependenciesError({ useNpm, useYarn, usePnpm, u
8
9
  dependencies: string[];
9
10
  devDependencies: string[];
10
11
  error: Error;
12
+ channel: InitOptions['channel'];
11
13
  }): void;
@@ -1,8 +1,14 @@
1
1
  import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
2
2
  import { getPackageManager } from './installDependencies.mjs';
3
- export default function logUpdateDependenciesError({ useNpm, useYarn, usePnpm, useBun, log, dependencies, devDependencies, error, }) {
3
+ export default function logUpdateDependenciesError({ useNpm, useYarn, usePnpm, useBun, log, dependencies, devDependencies, error, channel, }) {
4
4
  const packageManager = getPackageManager({ useNpm, useYarn, usePnpm, useBun });
5
5
  const installCommands = [];
6
+ const addChannel = (packageName) => {
7
+ const isVovk = packageName.startsWith('vovk') && packageName !== 'dto-mapped-types';
8
+ return isVovk ? (!channel || channel !== 'latest' ? `${packageName}@${channel}` : packageName) : packageName;
9
+ };
10
+ dependencies = dependencies.map(addChannel);
11
+ devDependencies = devDependencies.map(addChannel);
6
12
  if (dependencies.length > 0) {
7
13
  let depInstallCmd = '';
8
14
  switch (packageManager) {
@@ -2,22 +2,52 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import chalk from 'chalk';
4
4
  import getNPMPackageMetadata from '../utils/getNPMPackageMetadata.mjs';
5
- async function updateDeps({ packageJson, packageNames, channel, key, }) {
5
+ async function updateDeps({ packageJson, packageNames, channel, key, log, }) {
6
6
  return Promise.all(packageNames.map(async (packageName) => {
7
- const metadata = await getNPMPackageMetadata(packageName);
8
- const isVovk = packageName.startsWith('vovk');
9
- const latestVersion = metadata['dist-tags'][isVovk ? (channel ?? 'latest') : 'latest'];
10
- if (!packageJson[key]) {
11
- packageJson[key] = {};
7
+ let name;
8
+ let version;
9
+ if (packageName.startsWith('@')) {
10
+ // Handle scoped packages (@org/name@version)
11
+ const lastAtIndex = packageName.lastIndexOf('@');
12
+ if (lastAtIndex > 0) {
13
+ name = packageName.substring(0, lastAtIndex);
14
+ version = packageName.substring(lastAtIndex + 1);
15
+ }
16
+ else {
17
+ name = packageName;
18
+ version = undefined;
19
+ }
20
+ }
21
+ else {
22
+ // Handle regular packages (name@version)
23
+ const parts = packageName.split('@');
24
+ name = parts[0];
25
+ version = parts[1];
26
+ }
27
+ if (version) {
28
+ packageJson[key] ??= {};
29
+ packageJson[key][name] = version;
30
+ return;
12
31
  }
13
- packageJson[key][packageName] = `^${latestVersion}`;
32
+ let metadata;
33
+ try {
34
+ metadata = await getNPMPackageMetadata(name);
35
+ }
36
+ catch (error) {
37
+ log.error(`Failed to fetch metadata for package ${name}@${channel ?? 'latest'}: ${error}`);
38
+ return;
39
+ }
40
+ const isVovk = name.startsWith('vovk') && name !== 'dto-mapped-types';
41
+ const latestVersion = metadata['dist-tags'][isVovk ? (channel ?? 'latest') : 'latest'];
42
+ packageJson[key] ??= {};
43
+ packageJson[key][name] = `^${latestVersion}`;
14
44
  }));
15
45
  }
16
46
  export default async function updateDependenciesWithoutInstalling({ log, dir, dependencyNames, devDependencyNames, channel, }) {
17
47
  const packageJsonPath = path.join(dir, 'package.json');
18
48
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
19
- await updateDeps({ packageJson, packageNames: dependencyNames, channel, key: 'dependencies' });
20
- await updateDeps({ packageJson, packageNames: devDependencyNames, channel, key: 'devDependencies' });
49
+ await updateDeps({ packageJson, packageNames: dependencyNames, channel, log, key: 'dependencies' });
50
+ await updateDeps({ packageJson, packageNames: devDependencyNames, channel, log, key: 'devDependencies' });
21
51
  await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
22
52
  log.info('Added dependencies to package.json:');
23
53
  for (const dependency of dependencyNames) {
@@ -1 +1,3 @@
1
- export default function updateNPMScripts(root: string, updateScriptsMode: 'implicit' | 'explicit'): Promise<void>;
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
- import NPMCliPackageJson from '@npmcli/package-json';
2
- export default async function updateNPMScripts(root, updateScriptsMode) {
3
- const pkgJson = await NPMCliPackageJson.load(root);
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
- generate: 'vovk generate',
8
- dev: updateScriptsMode === 'explicit'
9
- ? "PORT=3000 concurrently 'next dev' 'vovk dev' --kill-others"
10
- : 'vovk dev --next-dev',
12
+ dev: getDevScript(pkgJson, updateScriptsMode),
13
+ prebuild: 'vovk generate',
11
14
  },
12
15
  });
13
16
  await pkgJson.save();
@@ -1 +1,4 @@
1
- export default function updateTypeScriptConfig(root: string): Promise<void>;
1
+ export default function updateTypeScriptConfig(root: string, compilerOptions: {
2
+ experimentalDecorators?: true;
3
+ emitDecoratorMetadata?: true;
4
+ }): Promise<void>;
@@ -2,14 +2,18 @@ import path from 'node:path';
2
2
  import fs from 'node:fs/promises';
3
3
  import * as jsonc from 'jsonc-parser';
4
4
  import prettify from '../utils/prettify.mjs';
5
- export default async function updateTypeScriptConfig(root) {
5
+ export default async function updateTypeScriptConfig(root, compilerOptions) {
6
6
  const tsconfigPath = path.join(root, 'tsconfig.json');
7
7
  const tsconfigContent = await fs.readFile(tsconfigPath, 'utf8');
8
- // Use jsonc-parser to generate edits and modify the experimentalDecorators property
9
- const edits = jsonc.modify(tsconfigContent, ['compilerOptions', 'experimentalDecorators'], true, {
10
- formattingOptions: {},
11
- });
12
- // Apply the edits to the original content
13
- const updatedContent = await prettify(jsonc.applyEdits(tsconfigContent, edits), tsconfigPath);
8
+ let updatedContent = tsconfigContent;
9
+ // Apply each compiler option
10
+ for (const [key, value] of Object.entries(compilerOptions)) {
11
+ const edits = jsonc.modify(updatedContent, ['compilerOptions', key], value, {
12
+ formattingOptions: {},
13
+ });
14
+ updatedContent = jsonc.applyEdits(updatedContent, edits);
15
+ }
16
+ // Prettify the final content
17
+ updatedContent = await prettify(updatedContent, tsconfigPath);
14
18
  await fs.writeFile(tsconfigPath, updatedContent, 'utf8');
15
19
  }
@@ -1,2 +1,2 @@
1
1
  import { Command } from 'commander';
2
- export default function initProgram(program: Command): Command;
2
+ export declare function initProgram(program: Command): Command;