vovk-cli 0.0.1-draft.26 → 0.0.1-draft.261

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 (128) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +29 -1
  3. package/client-templates/cjs/index.cjs.ejs +14 -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 +20 -0
  9. package/client-templates/packageJson/package.json.ejs +1 -0
  10. package/client-templates/readme/README.md.ejs +35 -0
  11. package/client-templates/schemaCjs/schema.cjs.ejs +26 -0
  12. package/client-templates/schemaCjs/schema.d.cts.ejs +10 -0
  13. package/client-templates/schemaJson/schema.json.ejs +1 -0
  14. package/client-templates/schemaTs/schema.ts.ejs +35 -0
  15. package/client-templates/ts/index.ts.ejs +30 -0
  16. package/dist/bundle/index.d.mts +8 -0
  17. package/dist/bundle/index.mjs +86 -0
  18. package/dist/dev/diffSegmentSchema.d.mts +36 -0
  19. package/dist/dev/{diffSchema.mjs → diffSegmentSchema.mjs} +4 -12
  20. package/dist/dev/ensureSchemaFiles.d.mts +3 -0
  21. package/dist/dev/ensureSchemaFiles.mjs +15 -31
  22. package/dist/dev/index.d.mts +5 -2
  23. package/dist/dev/index.mjs +173 -78
  24. package/dist/dev/logDiffResult.d.mts +1 -1
  25. package/dist/dev/logDiffResult.mjs +6 -43
  26. package/dist/dev/writeMetaJson.d.mts +2 -0
  27. package/dist/dev/writeMetaJson.mjs +17 -0
  28. package/dist/dev/writeOneSegmentSchemaFile.d.mts +12 -0
  29. package/dist/dev/{writeOneSchemaFile.mjs → writeOneSegmentSchemaFile.mjs} +10 -6
  30. package/dist/generate/ensureClient.d.mts +3 -0
  31. package/dist/generate/ensureClient.mjs +32 -0
  32. package/dist/generate/generate.d.mts +12 -0
  33. package/dist/generate/generate.mjs +266 -0
  34. package/dist/generate/getClientTemplateFiles.d.mts +22 -0
  35. package/dist/generate/getClientTemplateFiles.mjs +89 -0
  36. package/dist/generate/getProjectFullSchema.d.mts +3 -0
  37. package/dist/generate/getProjectFullSchema.mjs +64 -0
  38. package/dist/generate/getTemplateClientImports.d.mts +18 -0
  39. package/dist/generate/getTemplateClientImports.mjs +38 -0
  40. package/dist/generate/index.d.mts +33 -0
  41. package/dist/generate/index.mjs +185 -0
  42. package/dist/generate/mergePackages.d.mts +9 -0
  43. package/dist/generate/mergePackages.mjs +78 -0
  44. package/dist/generate/writeOneClientFile.d.mts +33 -0
  45. package/dist/generate/writeOneClientFile.mjs +101 -0
  46. package/dist/getProjectInfo/getConfig/getConfigAbsolutePaths.d.mts +5 -0
  47. package/dist/getProjectInfo/{getConfigAbsolutePaths.mjs → getConfig/getConfigAbsolutePaths.mjs} +4 -1
  48. package/dist/getProjectInfo/{getRelativeSrcRoot.d.mts → getConfig/getRelativeSrcRoot.d.mts} +1 -1
  49. package/dist/getProjectInfo/{getRelativeSrcRoot.mjs → getConfig/getRelativeSrcRoot.mjs} +2 -2
  50. package/dist/getProjectInfo/getConfig/getTemplateDefs.d.mts +16 -0
  51. package/dist/getProjectInfo/getConfig/getTemplateDefs.mjs +98 -0
  52. package/dist/getProjectInfo/{getUserConfig.d.mts → getConfig/getUserConfig.d.mts} +3 -2
  53. package/dist/getProjectInfo/{getUserConfig.mjs → getConfig/getUserConfig.mjs} +6 -4
  54. package/dist/getProjectInfo/{importUncachedModule.mjs → getConfig/importUncachedModule.mjs} +1 -5
  55. package/dist/getProjectInfo/{importUncachedModuleWorker.mjs → getConfig/importUncachedModuleWorker.mjs} +0 -1
  56. package/dist/getProjectInfo/getConfig/index.d.mts +102 -0
  57. package/dist/getProjectInfo/getConfig/index.mjs +90 -0
  58. package/dist/getProjectInfo/index.d.mts +7 -9
  59. package/dist/getProjectInfo/index.mjs +13 -22
  60. package/dist/index.d.mts +2 -2
  61. package/dist/index.mjs +95 -37
  62. package/dist/init/createConfig.d.mts +2 -2
  63. package/dist/init/createConfig.mjs +18 -12
  64. package/dist/init/getTemplateFilesFromPackage.mjs +10 -5
  65. package/dist/init/index.d.mts +2 -2
  66. package/dist/init/index.mjs +90 -54
  67. package/dist/init/installDependencies.mjs +4 -2
  68. package/dist/init/logUpdateDependenciesError.d.mts +3 -1
  69. package/dist/init/logUpdateDependenciesError.mjs +7 -1
  70. package/dist/init/updateDependenciesWithoutInstalling.mjs +41 -11
  71. package/dist/init/updateNPMScripts.d.mts +3 -1
  72. package/dist/init/updateNPMScripts.mjs +10 -7
  73. package/dist/init/updateTypeScriptConfig.d.mts +4 -1
  74. package/dist/init/updateTypeScriptConfig.mjs +11 -7
  75. package/dist/initProgram.d.mts +1 -1
  76. package/dist/initProgram.mjs +17 -16
  77. package/dist/locateSegments.d.mts +8 -1
  78. package/dist/locateSegments.mjs +14 -4
  79. package/dist/new/addClassToSegmentCode.d.mts +1 -2
  80. package/dist/new/addClassToSegmentCode.mjs +3 -3
  81. package/dist/new/addCommonTerms.mjs +1 -0
  82. package/dist/new/index.d.mts +1 -1
  83. package/dist/new/index.mjs +3 -2
  84. package/dist/new/newModule.d.mts +2 -1
  85. package/dist/new/newModule.mjs +18 -16
  86. package/dist/new/newSegment.d.mts +2 -1
  87. package/dist/new/newSegment.mjs +19 -10
  88. package/dist/new/render.d.mts +7 -3
  89. package/dist/new/render.mjs +29 -8
  90. package/dist/types.d.mts +50 -41
  91. package/dist/utils/compileJSONSchemaToTypeScriptType.d.mts +3 -0
  92. package/dist/utils/compileJSONSchemaToTypeScriptType.mjs +53 -0
  93. package/dist/utils/debounceWithArgs.d.mts +2 -2
  94. package/dist/utils/debounceWithArgs.mjs +24 -9
  95. package/dist/utils/formatLoggedSegmentName.d.mts +3 -1
  96. package/dist/utils/formatLoggedSegmentName.mjs +3 -2
  97. package/dist/utils/getAvailablePort.mjs +1 -1
  98. package/dist/utils/getPublicModuleNameFromPath.d.mts +4 -0
  99. package/dist/utils/getPublicModuleNameFromPath.mjs +9 -0
  100. package/dist/utils/normalizeOpenAPIMixins.d.mts +5 -0
  101. package/dist/utils/normalizeOpenAPIMixins.mjs +49 -0
  102. package/dist/utils/pickSegmentFullSchema.d.mts +3 -0
  103. package/dist/utils/pickSegmentFullSchema.mjs +15 -0
  104. package/dist/utils/removeUnlistedDirectories.d.mts +10 -0
  105. package/dist/utils/removeUnlistedDirectories.mjs +61 -0
  106. package/dist/utils/resolveAbsoluteModulePath.d.mts +2 -0
  107. package/dist/utils/resolveAbsoluteModulePath.mjs +32 -0
  108. package/module-templates/controller.ts.ejs +56 -0
  109. package/module-templates/service.ts.ejs +28 -0
  110. package/package.json +42 -22
  111. package/dist/dev/diffSchema.d.mts +0 -43
  112. package/dist/dev/ensureClient.d.mts +0 -5
  113. package/dist/dev/ensureClient.mjs +0 -31
  114. package/dist/dev/isMetadataEmpty.d.mts +0 -2
  115. package/dist/dev/isMetadataEmpty.mjs +0 -4
  116. package/dist/dev/writeOneSchemaFile.d.mts +0 -11
  117. package/dist/generateClient.d.mts +0 -7
  118. package/dist/generateClient.mjs +0 -97
  119. package/dist/getProjectInfo/getConfig.d.mts +0 -11
  120. package/dist/getProjectInfo/getConfig.mjs +0 -29
  121. package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +0 -4
  122. package/dist/postinstall.d.mts +0 -1
  123. package/dist/postinstall.mjs +0 -24
  124. package/templates/controller.ejs +0 -51
  125. package/templates/service.ejs +0 -27
  126. package/templates/worker.ejs +0 -24
  127. /package/dist/getProjectInfo/{importUncachedModule.d.mts → getConfig/importUncachedModule.d.mts} +0 -0
  128. /package/dist/getProjectInfo/{importUncachedModuleWorker.d.mts → getConfig/importUncachedModuleWorker.d.mts} +0 -0
@@ -1,13 +1,14 @@
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';
@@ -16,27 +17,35 @@ import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
16
17
  export class Init {
17
18
  root;
18
19
  log;
19
- async #init({ configPaths, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, dryRun, channel, }) {
20
+ async #init({ configPaths, pkgJson, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, reactQuery, lang, dryRun, channel, }) {
20
21
  const { log, root } = this;
21
- const dependencies = ['vovk'];
22
+ const dependencies = ['vovk', 'vovk-client'];
22
23
  const devDependencies = ['vovk-cli'];
24
+ if (lang?.includes('py')) {
25
+ dependencies.push('vovk-python');
26
+ }
27
+ if (lang?.includes('rs')) {
28
+ dependencies.push('vovk-rust');
29
+ }
23
30
  // delete older config files
24
31
  if (configPaths.length) {
25
32
  await Promise.all(configPaths.map((configPath) => fs.rm(configPath)));
26
33
  log.debug(`Deleted existing config file${configPaths.length > 1 ? 's' : ''} at ${configPaths.join(', ')}`);
27
34
  }
28
35
  if (validationLibrary) {
29
- dependencies.push(validationLibrary);
30
- dependencies.push(...({
36
+ dependencies.push(validationLibrary, 'vovk-ajv', ...({
31
37
  'vovk-zod': ['zod'],
32
38
  'vovk-yup': ['yup'],
33
- 'vovk-dto': ['class-validator', 'class-transformer'],
39
+ 'vovk-dto': ['class-validator', 'class-transformer', 'dto-mapped-types', 'reflect-metadata'],
34
40
  }[validationLibrary] ?? []));
35
41
  }
42
+ if (reactQuery) {
43
+ dependencies.push('vovk-react-query', '@tanstack/react-query');
44
+ }
36
45
  if (updateScripts) {
37
46
  try {
38
47
  if (!dryRun)
39
- await updateNPMScripts(root, updateScripts);
48
+ await updateNPMScripts(pkgJson, root, updateScripts);
40
49
  log.info('Updated scripts at package.json');
41
50
  }
42
51
  catch (error) {
@@ -48,9 +57,17 @@ export class Init {
48
57
  }
49
58
  if (updateTsConfig) {
50
59
  try {
60
+ const compilerOptions = {
61
+ experimentalDecorators: true,
62
+ };
63
+ if (validationLibrary === 'vovk-dto') {
64
+ compilerOptions.emitDecoratorMetadata = true;
65
+ }
51
66
  if (!dryRun)
52
- await updateTypeScriptConfig(root);
53
- log.info('Added "experimentalDecorators" to tsconfig.json');
67
+ await updateTypeScriptConfig(root, compilerOptions);
68
+ log.info(`Added ${Object.keys(compilerOptions)
69
+ .map((k) => `"${k}"`)
70
+ .join(' and ')} to tsconfig.json`);
54
71
  }
55
72
  catch (error) {
56
73
  log.error(`Failed to update tsconfig.json: ${error.message}`);
@@ -70,7 +87,17 @@ export class Init {
70
87
  }
71
88
  catch (e) {
72
89
  const error = e;
73
- logUpdateDependenciesError({ log, error, useNpm, useYarn, usePnpm, useBun, dependencies, devDependencies });
90
+ logUpdateDependenciesError({
91
+ log,
92
+ error,
93
+ useNpm,
94
+ useYarn,
95
+ usePnpm,
96
+ useBun,
97
+ dependencies,
98
+ devDependencies,
99
+ channel,
100
+ });
74
101
  }
75
102
  if (depsUpdated) {
76
103
  const packageManager = getPackageManager({ useNpm, useYarn, usePnpm, useBun });
@@ -92,7 +119,7 @@ export class Init {
92
119
  log.info('Dependencies installed successfully');
93
120
  }
94
121
  catch (error) {
95
- log.warn(`Failed to install dependencies: ${error.message}. Please, install them manually with ${chalkHighlightThing(packageManager + ' install')}`);
122
+ log.warn(`Failed to install dependencies. ${error.message}. Please, install them manually with ${chalkHighlightThing(packageManager + ' install')}`);
96
123
  }
97
124
  }
98
125
  }
@@ -101,23 +128,24 @@ export class Init {
101
128
  const { configAbsolutePath } = await createConfig({
102
129
  root,
103
130
  log,
104
- options: { validationLibrary, validateOnClient, channel, dryRun },
131
+ options: { validationLibrary, reactQuery, channel, lang, dryRun },
105
132
  });
106
- log.info('Config created successfully at ' + configAbsolutePath);
133
+ log.info('Config created successfully at ' + chalkHighlightThing(configAbsolutePath));
107
134
  }
108
135
  catch (error) {
109
136
  log.error(`Failed to create config: ${error.message}`);
110
137
  }
111
138
  }
112
- async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, dryRun, channel, }) {
139
+ async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, reactQuery, lang, dryRun, channel, }) {
113
140
  const cwd = process.cwd();
114
141
  const root = path.resolve(cwd, prefix);
115
142
  const log = getLogger(logLevel);
143
+ const pkgJson = await NPMCliPackageJson.load(root);
116
144
  this.root = root;
117
145
  this.log = log;
118
146
  const configPaths = await getConfigPaths({ cwd, relativePath: prefix });
119
147
  if (yes) {
120
- return this.#init({ configPaths }, {
148
+ return this.#init({ configPaths, pkgJson }, {
121
149
  useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
122
150
  useYarn: useYarn ?? false,
123
151
  usePnpm: usePnpm ?? false,
@@ -126,9 +154,10 @@ export class Init {
126
154
  updateTsConfig: updateTsConfig ?? true,
127
155
  updateScripts: updateScripts ?? 'implicit',
128
156
  validationLibrary: validationLibrary?.toLocaleLowerCase() === 'none' ? null : (validationLibrary ?? 'vovk-zod'),
129
- validateOnClient: validateOnClient ?? true,
157
+ reactQuery: reactQuery ?? true,
130
158
  dryRun: dryRun ?? false,
131
159
  channel: channel ?? 'latest',
160
+ lang: lang ?? [],
132
161
  });
133
162
  }
134
163
  if (!(await getFileSystemEntryType(path.join(root, 'package.json')))) {
@@ -164,56 +193,62 @@ export class Init {
164
193
  {
165
194
  name: 'vovk-dto',
166
195
  value: 'vovk-dto',
167
- description: 'Use class-validator and class-transformer for data validation',
196
+ description: 'Use class-validator for data validation. Also installs class-transformer, dto-mapped-types and reflect-metadata',
168
197
  },
169
198
  { name: 'None', value: null, description: 'Install validation library later' },
170
199
  ],
171
200
  })));
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
- }));
201
+ updateScripts ??= await select({
202
+ message: 'Do you want to update "dev" NPM script at package.json?',
203
+ default: 'implicit',
204
+ choices: [
205
+ {
206
+ name: 'Yes, use "concurrently" implicitly',
207
+ value: 'implicit',
208
+ 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')}"`)}`,
209
+ },
210
+ {
211
+ name: 'Yes, use "concurrently" explicitly',
212
+ value: 'explicit',
213
+ 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')}"`)}`,
214
+ },
215
+ {
216
+ name: 'No',
217
+ value: undefined,
218
+ description: 'Add the NPM scripts manually',
219
+ },
220
+ ],
221
+ });
222
+ reactQuery ??= await confirm({
223
+ default: false,
224
+ message: 'Do you want to use @tanstack/react-query for data fetching at React components?',
225
+ });
202
226
  if (typeof updateTsConfig === 'undefined') {
203
227
  let shouldAsk = false;
204
228
  try {
205
- shouldAsk = !(await checkTSConfigForExperimentalDecorators(root));
229
+ shouldAsk = !(await checkTSConfigForExperimentalDecorators(root)); // TODO also check for emitDecoratorMetadata when vovk-dto is used
206
230
  }
207
231
  catch (error) {
208
- log.error(`Failed to check tsconfig.json for experimentalDecorators: ${error.message}`);
232
+ log.error(`Failed to check tsconfig.json for "experimentalDecorators": ${error.message}`);
209
233
  }
210
234
  if (shouldAsk) {
235
+ const keys = ['experimentalDecorators'];
236
+ if (validationLibrary === 'vovk-dto') {
237
+ keys.push('emitDecoratorMetadata');
238
+ }
211
239
  updateTsConfig = await confirm({
212
- message: 'Do you want to add experimentalDecorators to tsconfig.json?',
240
+ message: `Do you want to add ${keys.map((k) => `"${k}"`).join(' and ')} to tsconfig.json?`,
213
241
  });
214
242
  }
215
243
  }
216
- await this.#init({ configPaths }, {
244
+ lang ??= await checkbox({
245
+ message: 'Do you want to generate RPC client for other languages besides TypeScript?',
246
+ choices: [
247
+ { name: 'Python', value: 'py' },
248
+ { name: 'Rust', value: 'rs' },
249
+ ],
250
+ });
251
+ await this.#init({ configPaths, pkgJson }, {
217
252
  useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
218
253
  useYarn: useYarn ?? false,
219
254
  usePnpm: usePnpm ?? false,
@@ -222,7 +257,8 @@ export class Init {
222
257
  updateTsConfig,
223
258
  updateScripts,
224
259
  validationLibrary,
225
- validateOnClient,
260
+ reactQuery,
261
+ lang,
226
262
  dryRun,
227
263
  channel,
228
264
  });
@@ -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,29 +2,59 @@ 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) {
24
- log.raw.log(` - ${chalk.cyan(dependency)}`);
54
+ log.raw.info(` - ${chalk.cyan(dependency)}`);
25
55
  }
26
56
  log.info('Added devDependencies to package.json:');
27
57
  for (const dependency of devDependencyNames) {
28
- log.raw.log(` - ${chalk.cyan(dependency)}`);
58
+ log.raw.info(` - ${chalk.cyan(dependency)}`);
29
59
  }
30
60
  }
@@ -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,12 +1,15 @@
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
- generate: 'vovk generate',
7
- dev: updateScriptsMode === 'explicit'
8
- ? "PORT=3000 concurrently 'next dev' 'vovk 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();
@@ -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;
@@ -1,21 +1,22 @@
1
1
  import { Init } from './init/index.mjs';
2
2
  // reused at vovk-init
3
- export default function initProgram(program) {
3
+ export function initProgram(program) {
4
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', 'Validate on client')
18
- .option('--channel <channel>', 'Channel to use for fetching packages', 'latest')
19
- .option('--dry-run', 'Do not write files to disk')
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('--lang <languages...>', 'generate client for other programming languages by default ("py" for Python and "rs" for Rust are supported)')
17
+ .option('--validation-library <library>', 'validation library to use ("vovk-zod", "vovk-yup", "vovk-dto" or another); set to "none" to skip')
18
+ .option('--react-query', 'use @tanstack/react-query for data fetching inside components')
19
+ .option('--channel <channel>', 'channel to use for fetching packages', 'latest')
20
+ .option('--dry-run', 'do not write files to disk')
20
21
  .action((prefix = '.', options) => new Init().main(prefix, options));
21
22
  }
@@ -1,5 +1,12 @@
1
+ import type { VovkStrictConfig } from 'vovk';
2
+ import type { ProjectInfo } from './getProjectInfo/index.mjs';
1
3
  export type Segment = {
2
4
  routeFilePath: string;
3
5
  segmentName: string;
4
6
  };
5
- export default function locateSegments(dir: string, rootDir?: string): Promise<Segment[]>;
7
+ export declare function locateSegments({ dir, rootDir, config, log, }: {
8
+ dir: string | null;
9
+ rootDir?: string;
10
+ config: VovkStrictConfig | null;
11
+ log: ProjectInfo['log'];
12
+ }): Promise<Segment[]>;
@@ -1,10 +1,20 @@
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 = dir) {
4
+ export async function locateSegments({ dir, rootDir, config, log, }) {
5
5
  let results = [];
6
+ if (!dir)
7
+ return results; // If dir is null, return empty results because this isn't a Next.js app
8
+ rootDir = rootDir ?? dir;
9
+ let list = [];
6
10
  // Read the contents of the directory
7
- const list = await fs.readdir(dir);
11
+ try {
12
+ list = (await fs.readdir(dir)).toSorted();
13
+ }
14
+ catch {
15
+ // do nothing
16
+ return results;
17
+ }
8
18
  // Iterate through each item in the directory
9
19
  for (const file of list) {
10
20
  const filePath = path.join(dir, file);
@@ -16,12 +26,12 @@ export default async function locateSegments(dir, rootDir = dir) {
16
26
  const routeFilePath = path.join(filePath, 'route.ts');
17
27
  if (await getFileSystemEntryType(routeFilePath)) {
18
28
  // Calculate the basePath relative to the root directory
19
- const segmentName = path.relative(rootDir, dir);
29
+ const segmentName = path.relative(rootDir, dir).replace(/\\/g, '/'); // windows fix
20
30
  results.push({ routeFilePath, segmentName });
21
31
  }
22
32
  }
23
33
  // Recursively search inside subdirectories
24
- const subDirResults = await locateSegments(filePath, rootDir);
34
+ const subDirResults = await locateSegments({ dir: filePath, rootDir, config, log });
25
35
  results = results.concat(subDirResults);
26
36
  }
27
37
  }
@@ -1,6 +1,5 @@
1
- export default function addClassToSegmentCode(segmentSourceCode: string, { sourceName, compiledName, type, importPath, }: {
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, type, importPath, }) {
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 or workers
20
- const variableDeclaration = sourceFile.getVariableDeclaration(`${type}s`);
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) {
@@ -2,6 +2,7 @@ import pluralize from 'pluralize';
2
2
  // feel free to open a direct PR if you have more common terms to add
3
3
  const terms = [
4
4
  ['entity', 'entities'],
5
+ ['entry', 'entries'],
5
6
  ['regex', 'regexes'],
6
7
  ['index', 'indices'],
7
8
  ['matrix', 'matrices'],
@@ -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 declare function newComponents(components: string[], { dryRun, dir, templates, overwrite, noSegmentUpdate, empty, static: isStaticSegment }: NewOptions): Promise<void>;
@@ -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 async function newComponents(components, { dryRun, dir, templates, overwrite, noSegmentUpdate, empty, static: isStaticSegment }) {
4
4
  if (components[0] === 'segment' || components[0] === 'segments') {
5
5
  // vovk new segment [segmentName]
6
6
  let segmentNames = components
@@ -10,7 +10,7 @@ export default async function newComponents(components, { dryRun, dir, templates
10
10
  segmentNames = [''];
11
11
  }
12
12
  for (const segmentName of segmentNames) {
13
- await newSegment({ segmentName, overwrite, dryRun });
13
+ await newSegment({ segmentName, isStaticSegment, overwrite, dryRun });
14
14
  }
15
15
  }
16
16
  else {
@@ -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
  }
@@ -1,4 +1,4 @@
1
- export default function newModule({ what, moduleNameWithOptionalSegment, dryRun, dir: dirFlag, templates: templatesFlag, noSegmentUpdate, overwrite, }: {
1
+ export default function newModule({ what, moduleNameWithOptionalSegment, dryRun, dir: dirFlag, templates: templatesFlag, noSegmentUpdate, overwrite, empty, }: {
2
2
  what: string[];
3
3
  moduleNameWithOptionalSegment: string;
4
4
  dryRun?: boolean;
@@ -6,4 +6,5 @@ export default function newModule({ what, moduleNameWithOptionalSegment, dryRun,
6
6
  templates?: string[];
7
7
  noSegmentUpdate?: boolean;
8
8
  overwrite?: boolean;
9
+ empty?: boolean;
9
10
  }): Promise<void>;