vovk-cli 0.0.1-draft.7 → 0.0.1-draft.70

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 (81) hide show
  1. package/README.md +29 -1
  2. package/client-templates/main/main.cjs.ejs +15 -0
  3. package/client-templates/main/main.d.cts.ejs +14 -0
  4. package/client-templates/module/module.d.mts.ejs +14 -0
  5. package/client-templates/module/module.mjs.ejs +24 -0
  6. package/client-templates/python/__init__.py +276 -0
  7. package/client-templates/ts/index.ts.ejs +25 -0
  8. package/dist/dev/diffSchema.d.mts +36 -0
  9. package/dist/{watcher → dev}/diffSchema.mjs +3 -11
  10. package/dist/{watcher → dev}/ensureSchemaFiles.d.mts +3 -0
  11. package/dist/{watcher → dev}/ensureSchemaFiles.mjs +26 -15
  12. package/dist/dev/index.d.mts +6 -0
  13. package/dist/{watcher → dev}/index.mjs +129 -62
  14. package/dist/{watcher → dev}/isMetadataEmpty.mjs +1 -1
  15. package/dist/{watcher → dev}/logDiffResult.d.mts +2 -2
  16. package/dist/dev/logDiffResult.mjs +57 -0
  17. package/dist/{watcher → dev}/writeOneSchemaFile.d.mts +1 -1
  18. package/dist/{watcher → dev}/writeOneSchemaFile.mjs +2 -2
  19. package/dist/generate/ensureClient.d.mts +5 -0
  20. package/dist/generate/ensureClient.mjs +27 -0
  21. package/dist/generate/getClientTemplates.d.mts +14 -0
  22. package/dist/generate/getClientTemplates.mjs +28 -0
  23. package/dist/generate/index.d.mts +13 -0
  24. package/dist/generate/index.mjs +80 -0
  25. package/dist/getProjectInfo/getConfig.d.mts +3 -3
  26. package/dist/getProjectInfo/getConfig.mjs +8 -5
  27. package/dist/getProjectInfo/getConfigAbsolutePaths.mjs +2 -2
  28. package/dist/getProjectInfo/getRelativeSrcRoot.mjs +1 -1
  29. package/dist/getProjectInfo/getUserConfig.mjs +3 -1
  30. package/dist/getProjectInfo/importUncachedModule.mjs +0 -1
  31. package/dist/getProjectInfo/importUncachedModuleWorker.mjs +0 -1
  32. package/dist/getProjectInfo/index.d.mts +14 -5
  33. package/dist/getProjectInfo/index.mjs +21 -13
  34. package/dist/index.d.mts +1 -27
  35. package/dist/index.mjs +59 -65
  36. package/dist/init/checkTSConfigForExperimentalDecorators.mjs +2 -2
  37. package/dist/init/createConfig.d.mts +3 -4
  38. package/dist/init/createConfig.mjs +9 -8
  39. package/dist/init/getTemplateFilesFromPackage.d.mts +2 -1
  40. package/dist/init/getTemplateFilesFromPackage.mjs +4 -5
  41. package/dist/init/index.d.mts +2 -3
  42. package/dist/init/index.mjs +61 -97
  43. package/dist/init/installDependencies.d.mts +4 -1
  44. package/dist/init/installDependencies.mjs +2 -2
  45. package/dist/init/logUpdateDependenciesError.d.mts +11 -0
  46. package/dist/init/logUpdateDependenciesError.mjs +45 -0
  47. package/dist/init/updateDependenciesWithoutInstalling.d.mts +3 -2
  48. package/dist/init/updateDependenciesWithoutInstalling.mjs +13 -8
  49. package/dist/init/updateNPMScripts.d.mts +3 -1
  50. package/dist/init/updateNPMScripts.mjs +10 -6
  51. package/dist/init/updateTypeScriptConfig.mjs +2 -2
  52. package/dist/initProgram.d.mts +2 -0
  53. package/dist/initProgram.mjs +22 -0
  54. package/dist/locateSegments.d.mts +7 -1
  55. package/dist/locateSegments.mjs +9 -6
  56. package/dist/new/addClassToSegmentCode.d.mts +1 -2
  57. package/dist/new/addClassToSegmentCode.mjs +9 -5
  58. package/dist/new/addCommonTerms.mjs +1 -0
  59. package/dist/new/index.d.mts +2 -2
  60. package/dist/new/index.mjs +3 -3
  61. package/dist/new/newModule.d.mts +3 -3
  62. package/dist/new/newModule.mjs +38 -27
  63. package/dist/new/newSegment.mjs +8 -6
  64. package/dist/new/render.mjs +2 -5
  65. package/dist/postinstall.mjs +16 -19
  66. package/dist/types.d.mts +48 -9
  67. package/dist/utils/debounceWithArgs.d.mts +1 -1
  68. package/dist/utils/debounceWithArgs.mjs +24 -9
  69. package/dist/utils/formatLoggedSegmentName.mjs +1 -1
  70. package/dist/utils/getAvailablePort.mjs +3 -2
  71. package/dist/utils/getFileSystemEntryType.mjs +1 -1
  72. package/package.json +21 -18
  73. package/templates/controller.ejs +12 -11
  74. package/templates/service.ejs +6 -6
  75. package/dist/generateClient.d.mts +0 -7
  76. package/dist/generateClient.mjs +0 -97
  77. package/dist/watcher/diffSchema.d.mts +0 -43
  78. package/dist/watcher/index.d.mts +0 -6
  79. package/dist/watcher/logDiffResult.mjs +0 -90
  80. package/templates/worker.ejs +0 -1
  81. /package/dist/{watcher → dev}/isMetadataEmpty.d.mts +0 -0
@@ -1,81 +1,26 @@
1
- #!/usr/bin/env node
2
- /*
3
- npx vovk-cli init
4
- - Check if the project is already initialized
5
- - Do you want to reinitialize the project?
6
- - Yes
7
- - No (exit)
8
- - Check for package.json, if not found, show error and exit
9
- - Check for tsconfig.json, if not found, show error and exit
10
- - Check Next.js installed
11
- - Choose validation library: add to the installation list
12
- - vovk-zod
13
- - Further installation notes: install zod
14
- - vovk-yup
15
- - Further installation notes: install yup
16
- - vovk-dto
17
- - Further installation notes: install class-validator and class-transformer
18
- - None
19
- - If validation library is not None,
20
- - Do you want to enable client validation?
21
- - Yes
22
- - Add client validation to the config
23
- - No
24
- - Do you want to update NPM scripts?
25
- - Yes
26
- - Update NPM scripts
27
- - No
28
- - Do you want to use explicit concurrently?
29
- - Yes (recommended)
30
- - Add concurrently to the installation list
31
- - No
32
- - if experimentalDecorators is not found in tsconfig.json,
33
- - Do you want to add experimentalDecorators to tsconfig.json?
34
- - Yes
35
- - Add experimentalDecorators to tsconfig.json
36
- - No
37
- - Do you want to create route file with example service and controller? (NO NEED)
38
- - Yes
39
- - Create route file with example controller
40
- - No, I will create it myself
41
- - End
42
- - If there are any packages to install, install them
43
- - Show installation notes
44
- - If there are any files to create, create
45
- - If there are any config files to update, update
46
- - If example route file is NOT created, show example route file and controller
47
- - Show how to run the project
48
- - If npm scripts are updated
49
- - npm run dev
50
- - If npm scripts are NOT updated
51
- - If concurrently is installed
52
- - concurrently "vovk dev" "next dev"
53
- - If concurrently is NOT installed
54
- - vovk dev --next-dev
55
- - Open http://localhost:3000/api/hello-world
56
- - Show how to make a request to the example route
57
- - Show success message
58
- */
59
1
  import { confirm, select } from '@inquirer/prompts';
60
- import path from 'path';
61
- import fs from 'fs/promises';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs/promises';
62
4
  import getConfigPaths from '../getProjectInfo/getConfigAbsolutePaths.mjs';
63
5
  import chalk from 'chalk';
64
6
  import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
65
- import installDependencies from './installDependencies.mjs';
7
+ import installDependencies, { getPackageManager } from './installDependencies.mjs';
66
8
  import getLogger from '../utils/getLogger.mjs';
67
9
  import createConfig from './createConfig.mjs';
68
- import updateNPMScripts from './updateNPMScripts.mjs';
10
+ import updateNPMScripts, { getDevScript } from './updateNPMScripts.mjs';
69
11
  import checkTSConfigForExperimentalDecorators from './checkTSConfigForExperimentalDecorators.mjs';
70
12
  import updateTypeScriptConfig from './updateTypeScriptConfig.mjs';
71
13
  import updateDependenciesWithoutInstalling from './updateDependenciesWithoutInstalling.mjs';
14
+ import logUpdateDependenciesError from './logUpdateDependenciesError.mjs';
15
+ import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
16
+ import NPMCliPackageJson from '@npmcli/package-json';
72
17
  export class Init {
73
18
  root;
74
19
  log;
75
- async #init({ configPaths, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, dryRun, channel, }) {
20
+ async #init({ configPaths, pkgJson, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, reactQuery, dryRun, channel, }) {
76
21
  const { log, root } = this;
77
- const dependencies = ['vovk'];
78
- const devDependencies = ['vovk-cli'];
22
+ const dependencies = ['vovk', 'vovk-client'];
23
+ const devDependencies = ['vovk-cli', 'openapi3-ts'];
79
24
  // delete older config files
80
25
  if (configPaths.length) {
81
26
  await Promise.all(configPaths.map((configPath) => fs.rm(configPath)));
@@ -86,13 +31,17 @@ export class Init {
86
31
  dependencies.push(...({
87
32
  'vovk-zod': ['zod'],
88
33
  'vovk-yup': ['yup'],
89
- 'vovk-dto': ['class-validator', 'class-transformer'],
34
+ 'vovk-dto': ['class-validator', 'class-transformer', 'vovk-mapped-types', 'reflect-metadata'],
90
35
  }[validationLibrary] ?? []));
91
36
  }
37
+ if (reactQuery) {
38
+ dependencies.push('vovk-react-query');
39
+ dependencies.push('@tanstack/react-query');
40
+ }
92
41
  if (updateScripts) {
93
42
  try {
94
43
  if (!dryRun)
95
- await updateNPMScripts(root, updateScripts);
44
+ await updateNPMScripts(pkgJson, root, updateScripts);
96
45
  log.info('Updated scripts at package.json');
97
46
  }
98
47
  catch (error) {
@@ -113,6 +62,7 @@ export class Init {
113
62
  }
114
63
  }
115
64
  if (!dryRun) {
65
+ let depsUpdated = false;
116
66
  try {
117
67
  await updateDependenciesWithoutInstalling({
118
68
  log,
@@ -121,27 +71,34 @@ export class Init {
121
71
  devDependencyNames: devDependencies,
122
72
  channel: channel ?? 'latest',
123
73
  });
124
- log.info('Updated dependencies and devDependencies in package.json');
74
+ depsUpdated = true;
125
75
  }
126
- catch (error) {
127
- log.error(`Failed to update dependencies: ${error.message}. Please `);
76
+ catch (e) {
77
+ const error = e;
78
+ logUpdateDependenciesError({ log, error, useNpm, useYarn, usePnpm, useBun, dependencies, devDependencies });
128
79
  }
129
- if (!skipInstall) {
130
- try {
131
- await installDependencies({
132
- log,
133
- cwd: root,
134
- options: {
135
- useNpm,
136
- useYarn,
137
- usePnpm,
138
- useBun,
139
- },
140
- });
141
- log.info('Dependencies installed successfully');
80
+ if (depsUpdated) {
81
+ const packageManager = getPackageManager({ useNpm, useYarn, usePnpm, useBun });
82
+ if (skipInstall) {
83
+ log.info(`Installation skipped. Please, install them manually with ${chalkHighlightThing(packageManager + ' install')}`);
142
84
  }
143
- catch (error) {
144
- log.error(`Failed to install dependencies: ${error.message}`);
85
+ else {
86
+ try {
87
+ await installDependencies({
88
+ log,
89
+ cwd: root,
90
+ options: {
91
+ useNpm,
92
+ useYarn,
93
+ usePnpm,
94
+ useBun,
95
+ },
96
+ });
97
+ log.info('Dependencies installed successfully');
98
+ }
99
+ catch (error) {
100
+ log.warn(`Failed to install dependencies: ${error.message}. Please, install them manually with ${chalkHighlightThing(packageManager + ' install')}`);
101
+ }
145
102
  }
146
103
  }
147
104
  }
@@ -149,8 +106,7 @@ export class Init {
149
106
  const { configAbsolutePath } = await createConfig({
150
107
  root,
151
108
  log,
152
- options: { validationLibrary, validateOnClient },
153
- dryRun,
109
+ options: { validationLibrary, validateOnClient, reactQuery, channel, dryRun },
154
110
  });
155
111
  log.info('Config created successfully at ' + configAbsolutePath);
156
112
  }
@@ -158,15 +114,16 @@ export class Init {
158
114
  log.error(`Failed to create config: ${error.message}`);
159
115
  }
160
116
  }
161
- async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, dryRun, channel, }) {
117
+ async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, reactQuery, dryRun, channel, }) {
162
118
  const cwd = process.cwd();
163
119
  const root = path.resolve(cwd, prefix);
164
120
  const log = getLogger(logLevel);
121
+ const pkgJson = await NPMCliPackageJson.load(root);
165
122
  this.root = root;
166
123
  this.log = log;
167
124
  const configPaths = await getConfigPaths({ cwd, relativePath: prefix });
168
125
  if (yes) {
169
- return this.#init({ configPaths }, {
126
+ return this.#init({ configPaths, pkgJson }, {
170
127
  useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
171
128
  useYarn: useYarn ?? false,
172
129
  usePnpm: usePnpm ?? false,
@@ -176,6 +133,7 @@ export class Init {
176
133
  updateScripts: updateScripts ?? 'implicit',
177
134
  validationLibrary: validationLibrary?.toLocaleLowerCase() === 'none' ? null : (validationLibrary ?? 'vovk-zod'),
178
135
  validateOnClient: validateOnClient ?? true,
136
+ reactQuery: reactQuery ?? true,
179
137
  dryRun: dryRun ?? false,
180
138
  channel: channel ?? 'latest',
181
139
  });
@@ -213,7 +171,7 @@ export class Init {
213
171
  {
214
172
  name: 'vovk-dto',
215
173
  value: 'vovk-dto',
216
- description: 'Use class-validator and class-transformer for data validation',
174
+ description: 'Use class-validator for data validation. Also installs class-transformer, vovk-mapped-types and reflect-metadata',
217
175
  },
218
176
  { name: 'None', value: null, description: 'Install validation library later' },
219
177
  ],
@@ -228,41 +186,46 @@ export class Init {
228
186
  updateScripts =
229
187
  updateScripts ??
230
188
  (await select({
231
- message: 'Do you want to update package.json by adding "generate" and "dev" scripts?',
189
+ message: 'Do you want to update package.json by adding "generate" and updating "dev" NPM scripts?',
232
190
  default: 'implicit',
233
191
  choices: [
234
192
  {
235
193
  name: 'Yes, use "concurrently" implicitly',
236
- 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"`)}`,
237
194
  value: 'implicit',
195
+ 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')}"`)}`,
238
196
  },
239
197
  {
240
198
  name: 'Yes, use "concurrently" explicitly',
241
199
  value: 'explicit',
242
- 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 'vovk dev' 'next dev' --kill-others"`)}`,
200
+ 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')}"`)}`,
243
201
  },
244
202
  {
245
203
  name: 'No',
246
204
  value: undefined,
247
- description: 'Add the scripts manually',
205
+ description: 'Add the NPM scripts manually',
248
206
  },
249
207
  ],
250
208
  }));
209
+ reactQuery =
210
+ reactQuery ??
211
+ (await confirm({
212
+ message: 'Do you want to use @tanstack/react-query for data fetching at React components?',
213
+ }));
251
214
  if (typeof updateTsConfig === 'undefined') {
252
215
  let shouldAsk = false;
253
216
  try {
254
217
  shouldAsk = !(await checkTSConfigForExperimentalDecorators(root));
255
218
  }
256
219
  catch (error) {
257
- log.error(`Failed to check tsconfig.json for experimentalDecorators: ${error.message}`);
220
+ log.error(`Failed to check tsconfig.json for "experimentalDecorators": ${error.message}`);
258
221
  }
259
222
  if (shouldAsk) {
260
223
  updateTsConfig = await confirm({
261
- message: 'Do you want to add experimentalDecorators to tsconfig.json?',
224
+ message: 'Do you want to add "experimentalDecorators" option to tsconfig.json?',
262
225
  });
263
226
  }
264
227
  }
265
- await this.#init({ configPaths }, {
228
+ await this.#init({ configPaths, pkgJson }, {
266
229
  useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
267
230
  useYarn: useYarn ?? false,
268
231
  usePnpm: usePnpm ?? false,
@@ -272,6 +235,7 @@ export class Init {
272
235
  updateScripts,
273
236
  validationLibrary,
274
237
  validateOnClient,
238
+ reactQuery,
275
239
  dryRun,
276
240
  channel,
277
241
  });
@@ -1,7 +1,10 @@
1
- import { InitOptions } from '../index.mjs';
2
1
  import getLogger from '../utils/getLogger.mjs';
2
+ import type { InitOptions } from '../types.mjs';
3
+ type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';
4
+ export declare function getPackageManager(options: Pick<InitOptions, 'useNpm' | 'useYarn' | 'usePnpm' | 'useBun'>): PackageManager;
3
5
  export default function installDependencies({ log, cwd, options, }: {
4
6
  log: ReturnType<typeof getLogger>;
5
7
  cwd: string;
6
8
  options: Pick<InitOptions, 'useNpm' | 'useYarn' | 'usePnpm' | 'useBun'>;
7
9
  }): Promise<void>;
10
+ export {};
@@ -1,5 +1,5 @@
1
- import { spawn } from 'child_process';
2
- function getPackageManager(options) {
1
+ import { spawn } from 'node:child_process';
2
+ export function getPackageManager(options) {
3
3
  if (options.useNpm)
4
4
  return 'npm';
5
5
  if (options.useYarn)
@@ -0,0 +1,11 @@
1
+ import type getLogger from '../utils/getLogger.mjs';
2
+ export default function logUpdateDependenciesError({ useNpm, useYarn, usePnpm, useBun, log, dependencies, devDependencies, error, }: {
3
+ useNpm?: boolean;
4
+ useYarn?: boolean;
5
+ usePnpm?: boolean;
6
+ useBun?: boolean;
7
+ log: ReturnType<typeof getLogger>;
8
+ dependencies: string[];
9
+ devDependencies: string[];
10
+ error: Error;
11
+ }): void;
@@ -0,0 +1,45 @@
1
+ import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
2
+ import { getPackageManager } from './installDependencies.mjs';
3
+ export default function logUpdateDependenciesError({ useNpm, useYarn, usePnpm, useBun, log, dependencies, devDependencies, error, }) {
4
+ const packageManager = getPackageManager({ useNpm, useYarn, usePnpm, useBun });
5
+ const installCommands = [];
6
+ if (dependencies.length > 0) {
7
+ let depInstallCmd = '';
8
+ switch (packageManager) {
9
+ case 'npm':
10
+ depInstallCmd = `npm install ${dependencies.join(' ')}`;
11
+ break;
12
+ case 'yarn':
13
+ depInstallCmd = `yarn add ${dependencies.join(' ')}`;
14
+ break;
15
+ case 'pnpm':
16
+ depInstallCmd = `pnpm add ${dependencies.join(' ')}`;
17
+ break;
18
+ case 'bun':
19
+ depInstallCmd = `bun add ${dependencies.join(' ')}`;
20
+ break;
21
+ }
22
+ installCommands.push(depInstallCmd);
23
+ }
24
+ if (devDependencies.length > 0) {
25
+ let devDepInstallCmd = '';
26
+ switch (packageManager) {
27
+ case 'npm':
28
+ devDepInstallCmd = `npm install -D ${devDependencies.join(' ')}`;
29
+ break;
30
+ case 'yarn':
31
+ devDepInstallCmd = `yarn add --dev ${devDependencies.join(' ')}`;
32
+ break;
33
+ case 'pnpm':
34
+ devDepInstallCmd = `pnpm add -D ${devDependencies.join(' ')}`;
35
+ break;
36
+ case 'bun':
37
+ devDepInstallCmd = `bun add -d ${devDependencies.join(' ')}`;
38
+ break;
39
+ }
40
+ installCommands.push(devDepInstallCmd);
41
+ }
42
+ const installCmd = installCommands.join(' && ');
43
+ // Log the error with the appropriate manual installation instructions
44
+ log.warn(`Failed to update dependencies: ${error.message}. Please, install them manually with ${chalkHighlightThing(installCmd)}`);
45
+ }
@@ -1,8 +1,9 @@
1
- import getLogger from '../utils/getLogger.mjs';
1
+ import type getLogger from '../utils/getLogger.mjs';
2
+ import { InitOptions } from '../types.mjs';
2
3
  export default function updateDependenciesWithoutInstalling({ log, dir, dependencyNames, devDependencyNames, channel, }: {
3
4
  log: ReturnType<typeof getLogger>;
4
5
  dir: string;
5
6
  dependencyNames: string[];
6
7
  devDependencyNames: string[];
7
- channel: 'latest' | 'beta' | 'dev';
8
+ channel: InitOptions['channel'];
8
9
  }): Promise<void>;
@@ -1,13 +1,12 @@
1
- import fs from 'fs/promises';
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import chalk from 'chalk';
2
4
  import getNPMPackageMetadata from '../utils/getNPMPackageMetadata.mjs';
3
- import path from 'path';
4
5
  async function updateDeps({ packageJson, packageNames, channel, key, }) {
5
6
  return Promise.all(packageNames.map(async (packageName) => {
6
- if (packageJson[key]?.[packageName])
7
- return; // Skip if already present
8
7
  const metadata = await getNPMPackageMetadata(packageName);
9
- const isVovk = packageName.startsWith('vovk');
10
- const latestVersion = metadata['dist-tags'][isVovk ? channel : 'latest'];
8
+ const isVovk = packageName.startsWith('vovk') && packageName !== 'vovk-mapped-types';
9
+ const latestVersion = metadata['dist-tags'][isVovk ? (channel ?? 'latest') : 'latest'];
11
10
  if (!packageJson[key]) {
12
11
  packageJson[key] = {};
13
12
  }
@@ -18,8 +17,14 @@ export default async function updateDependenciesWithoutInstalling({ log, dir, de
18
17
  const packageJsonPath = path.join(dir, 'package.json');
19
18
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
20
19
  await updateDeps({ packageJson, packageNames: dependencyNames, channel, key: 'dependencies' });
21
- log.debug('Updated dependencies in package.json');
22
20
  await updateDeps({ packageJson, packageNames: devDependencyNames, channel, key: 'devDependencies' });
23
- log.debug('Updated devDependencies in package.json');
24
21
  await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
22
+ log.info('Added dependencies to package.json:');
23
+ for (const dependency of dependencyNames) {
24
+ log.raw.info(` - ${chalk.cyan(dependency)}`);
25
+ }
26
+ log.info('Added devDependencies to package.json:');
27
+ for (const dependency of devDependencyNames) {
28
+ log.raw.info(` - ${chalk.cyan(dependency)}`);
29
+ }
25
30
  }
@@ -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,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: {
11
+ ...pkgJson.content.scripts,
6
12
  generate: 'vovk generate',
7
- dev: updateScriptsMode === 'explicit'
8
- ? "PORT=3000 concurrently 'vovk dev' 'next dev' --kill-others"
9
- : 'vovk dev --next-dev',
13
+ dev: getDevScript(pkgJson, updateScriptsMode),
10
14
  },
11
15
  });
12
16
  await pkgJson.save();
@@ -1,5 +1,5 @@
1
- import path from 'path';
2
- import fs from 'fs/promises';
1
+ import path from 'node:path';
2
+ import fs from 'node:fs/promises';
3
3
  import * as jsonc from 'jsonc-parser';
4
4
  import prettify from '../utils/prettify.mjs';
5
5
  export default async function updateTypeScriptConfig(root) {
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export default function initProgram(program: Command): Command;
@@ -0,0 +1,22 @@
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('--validate-on-client', 'path to validateOnClient file')
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')
21
+ .action((prefix = '.', options) => new Init().main(prefix, options));
22
+ }
@@ -1,5 +1,11 @@
1
+ import type { VovkConfig } from './types.mjs';
1
2
  export type Segment = {
2
3
  routeFilePath: string;
3
4
  segmentName: string;
5
+ segmentImportPath: string;
4
6
  };
5
- export default function locateSegments(dir: string, rootDir?: string): Promise<Segment[]>;
7
+ export default function locateSegments({ dir, rootDir, config, }: {
8
+ dir: string;
9
+ rootDir?: string;
10
+ config: Required<VovkConfig> | null;
11
+ }): Promise<Segment[]>;
@@ -1,8 +1,10 @@
1
- import { promises as fs } from 'fs';
2
- import * as path from 'path';
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
3
  import getFileSystemEntryType from './utils/getFileSystemEntryType.mjs';
4
- export default async function locateSegments(dir, rootDir = dir) {
4
+ // config: null is used for testing
5
+ export default async function locateSegments({ dir, rootDir, config, }) {
5
6
  let results = [];
7
+ rootDir = rootDir ?? dir;
6
8
  // Read the contents of the directory
7
9
  const list = await fs.readdir(dir);
8
10
  // Iterate through each item in the directory
@@ -16,12 +18,13 @@ export default async function locateSegments(dir, rootDir = dir) {
16
18
  const routeFilePath = path.join(filePath, 'route.ts');
17
19
  if (await getFileSystemEntryType(routeFilePath)) {
18
20
  // Calculate the basePath relative to the root directory
19
- const segmentName = path.relative(rootDir, dir);
20
- results.push({ routeFilePath, segmentName });
21
+ const segmentName = path.relative(rootDir, dir).replace(/\\/g, '/'); // windows fix
22
+ const segmentImportPath = path.relative(config?.clientOutDir ?? '.__', routeFilePath);
23
+ results.push({ routeFilePath, segmentName, segmentImportPath });
21
24
  }
22
25
  }
23
26
  // Recursively search inside subdirectories
24
- const subDirResults = await locateSegments(filePath, rootDir);
27
+ const subDirResults = await locateSegments({ dir: filePath, rootDir, config });
25
28
  results = results.concat(subDirResults);
26
29
  }
27
30
  }
@@ -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,6 +1,10 @@
1
- import { Project, SyntaxKind } from 'ts-morph';
2
- export default function addClassToSegmentCode(segmentSourceCode, { sourceName, compiledName, type, importPath, }) {
3
- const project = new Project();
1
+ import { Project, QuoteKind, SyntaxKind } from 'ts-morph';
2
+ export default function addClassToSegmentCode(segmentSourceCode, { sourceName, compiledName, importPath, }) {
3
+ const project = new Project({
4
+ manipulationSettings: {
5
+ quoteKind: QuoteKind.Single,
6
+ },
7
+ });
4
8
  const sourceFile = project.createSourceFile('route.ts', segmentSourceCode, { overwrite: true });
5
9
  // Add the import if it doesn't exist
6
10
  let importDeclaration = sourceFile.getImportDeclaration((imp) => {
@@ -12,8 +16,8 @@ export default function addClassToSegmentCode(segmentSourceCode, { sourceName, c
12
16
  moduleSpecifier: importPath,
13
17
  });
14
18
  }
15
- // Get the variable declaration for controllers or workers
16
- const variableDeclaration = sourceFile.getVariableDeclaration(`${type}s`);
19
+ // Get the variable declaration for controllers
20
+ const variableDeclaration = sourceFile.getVariableDeclaration('controllers');
17
21
  if (variableDeclaration) {
18
22
  const initializer = variableDeclaration.getInitializer();
19
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
- import type { NewOptions } from '../index.mjs';
2
- export default function newComponents(components: string[], { dryRun, dirName, template, overwrite, noSegmentUpdate }: NewOptions): Promise<void>;
1
+ import type { NewOptions } from '../types.mjs';
2
+ export default function newComponents(components: string[], { dryRun, dir, templates, overwrite, noSegmentUpdate }: 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, dirName, template, overwrite, noSegmentUpdate }) {
3
+ export default async function newComponents(components, { dryRun, dir, templates, overwrite, noSegmentUpdate }) {
4
4
  if (components[0] === 'segment' || components[0] === 'segments') {
5
5
  // vovk new segment [segmentName]
6
6
  let segmentNames = components
@@ -26,8 +26,8 @@ export default async function newComponents(components, { dryRun, dirName, templ
26
26
  await newModule({
27
27
  what,
28
28
  moduleNameWithOptionalSegment,
29
- dirName,
30
- template,
29
+ dir,
30
+ templates,
31
31
  overwrite,
32
32
  noSegmentUpdate,
33
33
  dryRun,
@@ -1,9 +1,9 @@
1
- export default function newModule({ what, moduleNameWithOptionalSegment, dryRun, dirName: dirNameFlag, template: templateFlag, noSegmentUpdate, overwrite, }: {
1
+ export default function newModule({ what, moduleNameWithOptionalSegment, dryRun, dir: dirFlag, templates: templatesFlag, noSegmentUpdate, overwrite, }: {
2
2
  what: string[];
3
3
  moduleNameWithOptionalSegment: string;
4
4
  dryRun?: boolean;
5
- dirName?: string;
6
- template?: string;
5
+ dir?: string;
6
+ templates?: string[];
7
7
  noSegmentUpdate?: boolean;
8
8
  overwrite?: boolean;
9
9
  }): Promise<void>;