vovk-cli 0.0.1-draft.3 → 0.0.1-draft.300

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 (152) 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 +25 -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 +25 -0
  8. package/client-templates/mjs/index.mjs.ejs +23 -0
  9. package/client-templates/packageJson/package.json.ejs +1 -0
  10. package/client-templates/readme/README.md.ejs +38 -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 +33 -0
  16. package/dist/bundle/index.d.mts +8 -0
  17. package/dist/bundle/index.mjs +90 -0
  18. package/dist/dev/diffSegmentSchema.d.mts +36 -0
  19. package/dist/{watcher/diffSchema.mjs → dev/diffSegmentSchema.mjs} +4 -12
  20. package/dist/{watcher → dev}/ensureSchemaFiles.d.mts +3 -0
  21. package/dist/{watcher → dev}/ensureSchemaFiles.mjs +17 -21
  22. package/dist/dev/index.d.mts +9 -0
  23. package/dist/dev/index.mjs +388 -0
  24. package/dist/dev/logDiffResult.d.mts +3 -0
  25. package/dist/dev/logDiffResult.mjs +57 -0
  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/writeOneSegmentSchemaFile.mjs +32 -0
  30. package/dist/generate/ensureClient.d.mts +3 -0
  31. package/dist/generate/ensureClient.mjs +32 -0
  32. package/dist/generate/generate.d.mts +15 -0
  33. package/dist/generate/generate.mjs +291 -0
  34. package/dist/generate/getClientTemplateFiles.d.mts +20 -0
  35. package/dist/generate/getClientTemplateFiles.mjs +89 -0
  36. package/dist/generate/getProjectFullSchema.d.mts +7 -0
  37. package/dist/generate/getProjectFullSchema.mjs +65 -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 +189 -0
  42. package/dist/generate/mergePackages.d.mts +7 -0
  43. package/dist/generate/mergePackages.mjs +55 -0
  44. package/dist/generate/writeOneClientFile.d.mts +36 -0
  45. package/dist/generate/writeOneClientFile.mjs +120 -0
  46. package/dist/getProjectInfo/getConfig/getConfigAbsolutePaths.d.mts +5 -0
  47. package/dist/getProjectInfo/{getConfigAbsolutePaths.mjs → getConfig/getConfigAbsolutePaths.mjs} +6 -3
  48. package/dist/getProjectInfo/{getRelativeSrcRoot.d.mts → getConfig/getRelativeSrcRoot.d.mts} +1 -1
  49. package/dist/getProjectInfo/getConfig/getRelativeSrcRoot.mjs +12 -0
  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} +7 -5
  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 +120 -0
  57. package/dist/getProjectInfo/getConfig/index.mjs +94 -0
  58. package/dist/getProjectInfo/index.d.mts +12 -9
  59. package/dist/getProjectInfo/index.mjs +22 -23
  60. package/dist/index.d.mts +2 -24
  61. package/dist/index.mjs +106 -69
  62. package/dist/init/checkTSConfigForExperimentalDecorators.mjs +2 -2
  63. package/dist/init/createConfig.d.mts +3 -4
  64. package/dist/init/createConfig.mjs +22 -16
  65. package/dist/init/getTemplateFilesFromPackage.d.mts +2 -1
  66. package/dist/init/getTemplateFilesFromPackage.mjs +13 -9
  67. package/dist/init/index.d.mts +2 -3
  68. package/dist/init/index.mjs +119 -138
  69. package/dist/init/installDependencies.d.mts +4 -1
  70. package/dist/init/installDependencies.mjs +6 -4
  71. package/dist/init/logUpdateDependenciesError.d.mts +13 -0
  72. package/dist/init/logUpdateDependenciesError.mjs +51 -0
  73. package/dist/init/updateDependenciesWithoutInstalling.d.mts +3 -2
  74. package/dist/init/updateDependenciesWithoutInstalling.mjs +50 -15
  75. package/dist/init/updateNPMScripts.d.mts +3 -1
  76. package/dist/init/updateNPMScripts.mjs +10 -7
  77. package/dist/init/updateTypeScriptConfig.d.mts +4 -1
  78. package/dist/init/updateTypeScriptConfig.mjs +13 -9
  79. package/dist/initProgram.d.mts +2 -0
  80. package/dist/initProgram.mjs +22 -0
  81. package/dist/locateSegments.d.mts +8 -1
  82. package/dist/locateSegments.mjs +16 -6
  83. package/dist/new/addClassToSegmentCode.d.mts +1 -2
  84. package/dist/new/addClassToSegmentCode.mjs +9 -5
  85. package/dist/new/addCommonTerms.mjs +1 -0
  86. package/dist/new/index.d.mts +2 -2
  87. package/dist/new/index.mjs +14 -3
  88. package/dist/new/newModule.d.mts +7 -2
  89. package/dist/new/newModule.mjs +61 -35
  90. package/dist/new/newSegment.d.mts +4 -2
  91. package/dist/new/newSegment.mjs +22 -13
  92. package/dist/new/render.d.mts +9 -9
  93. package/dist/new/render.mjs +38 -13
  94. package/dist/types.d.mts +73 -28
  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 -9
  101. package/dist/utils/formatLoggedSegmentName.d.mts +3 -1
  102. package/dist/utils/formatLoggedSegmentName.mjs +4 -3
  103. package/dist/utils/getAvailablePort.mjs +3 -2
  104. package/dist/utils/getFileSystemEntryType.mjs +1 -1
  105. package/dist/utils/getPackageJson.d.mts +3 -0
  106. package/dist/utils/getPackageJson.mjs +22 -0
  107. package/dist/utils/getPublicModuleNameFromPath.d.mts +4 -0
  108. package/dist/utils/getPublicModuleNameFromPath.mjs +9 -0
  109. package/dist/utils/normalizeOpenAPIMixins.d.mts +7 -0
  110. package/dist/utils/normalizeOpenAPIMixins.mjs +67 -0
  111. package/dist/utils/pickSegmentFullSchema.d.mts +3 -0
  112. package/dist/utils/pickSegmentFullSchema.mjs +15 -0
  113. package/dist/utils/removeUnlistedDirectories.d.mts +10 -0
  114. package/dist/utils/removeUnlistedDirectories.mjs +61 -0
  115. package/dist/utils/resolveAbsoluteModulePath.d.mts +2 -0
  116. package/dist/utils/resolveAbsoluteModulePath.mjs +32 -0
  117. package/module-templates/controller.ts.ejs +56 -0
  118. package/module-templates/service.ts.ejs +28 -0
  119. package/package.json +42 -21
  120. package/dist/generateClient.d.mts +0 -7
  121. package/dist/generateClient.mjs +0 -97
  122. package/dist/getProjectInfo/directoryExists.d.mts +0 -1
  123. package/dist/getProjectInfo/directoryExists.mjs +0 -10
  124. package/dist/getProjectInfo/getConfig.d.mts +0 -11
  125. package/dist/getProjectInfo/getConfig.mjs +0 -29
  126. package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +0 -4
  127. package/dist/getProjectInfo/getRelativeSrcRoot.mjs +0 -12
  128. package/dist/postinstall.d.mts +0 -1
  129. package/dist/postinstall.mjs +0 -22
  130. package/dist/watcher/diffSchema.d.mts +0 -43
  131. package/dist/watcher/index.d.mts +0 -6
  132. package/dist/watcher/index.mjs +0 -295
  133. package/dist/watcher/isMetadataEmpty.d.mts +0 -2
  134. package/dist/watcher/isMetadataEmpty.mjs +0 -4
  135. package/dist/watcher/logDiffResult.d.mts +0 -3
  136. package/dist/watcher/logDiffResult.mjs +0 -90
  137. package/dist/watcher/writeOneSchemaFile.d.mts +0 -11
  138. package/dist/watcher/writeOneSchemaFile.mjs +0 -27
  139. package/templates/controller.ejs +0 -50
  140. package/templates/service.ejs +0 -7
  141. package/templates/worker.ejs +0 -1
  142. package/templates_old/MyThingController.c.only.template.ts +0 -32
  143. package/templates_old/MyThingController.c.template.ts +0 -34
  144. package/templates_old/MyThingService.s.template.ts +0 -18
  145. package/templates_old/controller.ejs +0 -85
  146. package/templates_old/service.ejs +0 -9
  147. package/templates_old/worker.ejs +0 -9
  148. package/templates_old/zod/MyThingController.c.only.template.ts +0 -32
  149. package/templates_old/zod/MyThingController.c.template.ts +0 -39
  150. package/templates_old/zod/MyThingService.s.template.ts +0 -18
  151. /package/dist/getProjectInfo/{importUncachedModule.d.mts → getConfig/importUncachedModule.d.mts} +0 -0
  152. /package/dist/getProjectInfo/{importUncachedModuleWorker.d.mts → getConfig/importUncachedModuleWorker.d.mts} +0 -0
@@ -1,6 +1,7 @@
1
+ import { InitOptions } from '../types.mjs';
1
2
  /**
2
3
  * Retrieves a list of files in the 'templates' folder of an NPM package.
3
4
  * @param packageName - The name of the NPM package.
4
5
  * @returns A promise that resolves to an array of file paths.
5
6
  */
6
- export default function getTemplatesFiles(packageName: string, channel?: string): Promise<Record<string, string>>;
7
+ export default function getTemplateFilesFromPackage(packageName: string, channel?: InitOptions['channel']): Promise<Record<string, string>>;
@@ -1,14 +1,13 @@
1
- import { createGunzip } from 'zlib';
1
+ import { Readable } from 'node:stream';
2
+ import { createGunzip } from 'node:zlib';
2
3
  import tar from 'tar-stream';
3
- import { Readable } from 'stream';
4
4
  import getNPMPackageMetadata from '../utils/getNPMPackageMetadata.mjs';
5
5
  /**
6
6
  * Retrieves a list of files in the 'templates' folder of an NPM package.
7
7
  * @param packageName - The name of the NPM package.
8
8
  * @returns A promise that resolves to an array of file paths.
9
9
  */
10
- export default async function getTemplatesFiles(packageName, channel = 'beta' // TODO change to latest
11
- ) {
10
+ export default async function getTemplateFilesFromPackage(packageName, channel = 'latest') {
12
11
  const metadata = await getNPMPackageMetadata(packageName);
13
12
  const latestVersion = metadata['dist-tags'][channel];
14
13
  const tarballUrl = metadata.versions[latestVersion].dist.tarball;
@@ -22,8 +21,14 @@ export default async function getTemplatesFiles(packageName, channel = 'beta' //
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,9 +42,8 @@ function extractTemplatesFromTarball(tarballBuffer) {
37
42
  const files = [];
38
43
  extract.on('entry', (header, stream, next) => {
39
44
  const filePath = header.name;
40
- // TODO revisit comments
41
- // Check if the file is in the 'templates' folder
42
- if (filePath.startsWith('package/templates/')) {
45
+ // Check if the file is in the 'module-templates' folder
46
+ if (filePath.startsWith('package/module-templates/')) {
43
47
  files.push(filePath.replace('package/', ''));
44
48
  }
45
49
  stream.on('end', () => next());
@@ -1,9 +1,8 @@
1
- #!/usr/bin/env node
2
- import type { InitOptions } from '../index.mjs';
3
1
  import getLogger from '../utils/getLogger.mjs';
2
+ import type { InitOptions } from '../types.mjs';
4
3
  export declare class Init {
5
4
  #private;
6
5
  root: string;
7
6
  log: ReturnType<typeof getLogger>;
8
- main(prefix: string, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, dryRun, channel, }: InitOptions): Promise<void>;
7
+ main(prefix: string, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, reactQuery, lang, dryRun, channel, }: InitOptions): Promise<void>;
9
8
  }
@@ -1,98 +1,50 @@
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
- import { confirm, select } from '@inquirer/prompts';
60
- import path from 'path';
61
- import fs from 'fs/promises';
62
- import getConfigPaths from '../getProjectInfo/getConfigAbsolutePaths.mjs';
1
+ import { confirm, select, checkbox } from '@inquirer/prompts';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs/promises';
63
4
  import chalk from 'chalk';
5
+ import NPMCliPackageJson from '@npmcli/package-json';
6
+ import getConfigPaths from '../getProjectInfo/getConfig/getConfigAbsolutePaths.mjs';
64
7
  import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
65
- import installDependencies from './installDependencies.mjs';
8
+ import installDependencies, { getPackageManager } from './installDependencies.mjs';
66
9
  import getLogger from '../utils/getLogger.mjs';
67
10
  import createConfig from './createConfig.mjs';
68
- import updateNPMScripts from './updateNPMScripts.mjs';
11
+ import updateNPMScripts, { getDevScript } from './updateNPMScripts.mjs';
69
12
  import checkTSConfigForExperimentalDecorators from './checkTSConfigForExperimentalDecorators.mjs';
70
13
  import updateTypeScriptConfig from './updateTypeScriptConfig.mjs';
71
14
  import updateDependenciesWithoutInstalling from './updateDependenciesWithoutInstalling.mjs';
15
+ import logUpdateDependenciesError from './logUpdateDependenciesError.mjs';
16
+ import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
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, reactQuery, lang, dryRun, channel, }) {
76
21
  const { log, root } = this;
77
- const dependencies = ['vovk'];
22
+ const dependencies = ['vovk', 'vovk-client', 'vovk-ajv', 'openapi3-ts'];
78
23
  const devDependencies = ['vovk-cli'];
24
+ if (lang?.includes('py')) {
25
+ devDependencies.push('vovk-python');
26
+ }
27
+ if (lang?.includes('rs')) {
28
+ devDependencies.push('vovk-rust');
29
+ }
79
30
  // delete older config files
80
31
  if (configPaths.length) {
81
32
  await Promise.all(configPaths.map((configPath) => fs.rm(configPath)));
82
33
  log.debug(`Deleted existing config file${configPaths.length > 1 ? 's' : ''} at ${configPaths.join(', ')}`);
83
34
  }
84
35
  if (validationLibrary) {
85
- dependencies.push(validationLibrary);
86
- dependencies.push(...({
36
+ dependencies.push(validationLibrary, ...({
87
37
  'vovk-zod': ['zod'],
88
- 'vovk-yup': ['yup'],
89
- 'vovk-dto': ['class-validator', 'class-transformer'],
38
+ 'vovk-dto': ['class-validator', 'class-transformer', 'dto-mapped-types', 'reflect-metadata'],
90
39
  }[validationLibrary] ?? []));
91
40
  }
41
+ if (reactQuery) {
42
+ dependencies.push('vovk-react-query', '@tanstack/react-query');
43
+ }
92
44
  if (updateScripts) {
93
45
  try {
94
46
  if (!dryRun)
95
- await updateNPMScripts(root, updateScripts);
47
+ await updateNPMScripts(pkgJson, root, updateScripts);
96
48
  log.info('Updated scripts at package.json');
97
49
  }
98
50
  catch (error) {
@@ -104,15 +56,24 @@ export class Init {
104
56
  }
105
57
  if (updateTsConfig) {
106
58
  try {
59
+ const compilerOptions = {
60
+ experimentalDecorators: true,
61
+ };
62
+ if (validationLibrary === 'vovk-dto') {
63
+ compilerOptions.emitDecoratorMetadata = true;
64
+ }
107
65
  if (!dryRun)
108
- await updateTypeScriptConfig(root);
109
- log.info('Added "experimentalDecorators" to tsconfig.json');
66
+ await updateTypeScriptConfig(root, compilerOptions);
67
+ log.info(`Added ${Object.keys(compilerOptions)
68
+ .map((k) => `"${k}"`)
69
+ .join(' and ')} to tsconfig.json`);
110
70
  }
111
71
  catch (error) {
112
72
  log.error(`Failed to update tsconfig.json: ${error.message}`);
113
73
  }
114
74
  }
115
75
  if (!dryRun) {
76
+ let depsUpdated = false;
116
77
  try {
117
78
  await updateDependenciesWithoutInstalling({
118
79
  log,
@@ -121,27 +82,44 @@ export class Init {
121
82
  devDependencyNames: devDependencies,
122
83
  channel: channel ?? 'latest',
123
84
  });
124
- log.info('Updated dependencies and devDependencies in package.json');
85
+ depsUpdated = true;
125
86
  }
126
- catch (error) {
127
- log.error(`Failed to update dependencies: ${error.message}. Please `);
87
+ catch (e) {
88
+ const error = e;
89
+ logUpdateDependenciesError({
90
+ log,
91
+ error,
92
+ useNpm,
93
+ useYarn,
94
+ usePnpm,
95
+ useBun,
96
+ dependencies,
97
+ devDependencies,
98
+ channel,
99
+ });
128
100
  }
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');
101
+ if (depsUpdated) {
102
+ const packageManager = getPackageManager({ useNpm, useYarn, usePnpm, useBun });
103
+ if (skipInstall) {
104
+ log.info(`Installation skipped. Please, install them manually with ${chalkHighlightThing(packageManager + ' install')}`);
142
105
  }
143
- catch (error) {
144
- log.error(`Failed to install dependencies: ${error.message}`);
106
+ else {
107
+ try {
108
+ await installDependencies({
109
+ log,
110
+ cwd: root,
111
+ options: {
112
+ useNpm,
113
+ useYarn,
114
+ usePnpm,
115
+ useBun,
116
+ },
117
+ });
118
+ log.info('Dependencies installed successfully');
119
+ }
120
+ catch (error) {
121
+ log.warn(`Failed to install dependencies. ${error.message}. Please, install them manually with ${chalkHighlightThing(packageManager + ' install')}`);
122
+ }
145
123
  }
146
124
  }
147
125
  }
@@ -149,24 +127,24 @@ export class Init {
149
127
  const { configAbsolutePath } = await createConfig({
150
128
  root,
151
129
  log,
152
- options: { validationLibrary, validateOnClient },
153
- dryRun,
130
+ options: { validationLibrary, reactQuery, channel, lang, dryRun },
154
131
  });
155
- log.info('Config created successfully at ' + configAbsolutePath);
132
+ log.info('Config created successfully at ' + chalkHighlightThing(configAbsolutePath));
156
133
  }
157
134
  catch (error) {
158
135
  log.error(`Failed to create config: ${error.message}`);
159
136
  }
160
137
  }
161
- async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, dryRun, channel, }) {
138
+ async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, reactQuery, lang, dryRun, channel, }) {
162
139
  const cwd = process.cwd();
163
140
  const root = path.resolve(cwd, prefix);
164
141
  const log = getLogger(logLevel);
142
+ const pkgJson = await NPMCliPackageJson.load(root);
165
143
  this.root = root;
166
144
  this.log = log;
167
145
  const configPaths = await getConfigPaths({ cwd, relativePath: prefix });
168
146
  if (yes) {
169
- return this.#init({ configPaths }, {
147
+ return this.#init({ configPaths, pkgJson }, {
170
148
  useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
171
149
  useYarn: useYarn ?? false,
172
150
  usePnpm: usePnpm ?? false,
@@ -175,9 +153,10 @@ export class Init {
175
153
  updateTsConfig: updateTsConfig ?? true,
176
154
  updateScripts: updateScripts ?? 'implicit',
177
155
  validationLibrary: validationLibrary?.toLocaleLowerCase() === 'none' ? null : (validationLibrary ?? 'vovk-zod'),
178
- validateOnClient: validateOnClient ?? true,
156
+ reactQuery: reactQuery ?? true,
179
157
  dryRun: dryRun ?? false,
180
158
  channel: channel ?? 'latest',
159
+ lang: lang ?? [],
181
160
  });
182
161
  }
183
162
  if (!(await getFileSystemEntryType(path.join(root, 'package.json')))) {
@@ -205,64 +184,65 @@ export class Init {
205
184
  value: 'vovk-zod',
206
185
  description: 'Use Zod for data validation',
207
186
  },
208
- {
209
- name: 'vovk-yup',
210
- value: 'vovk-yup',
211
- description: 'Use Yup for data validation',
212
- },
213
187
  {
214
188
  name: 'vovk-dto',
215
189
  value: 'vovk-dto',
216
- description: 'Use class-validator and class-transformer for data validation',
190
+ description: 'Use class-validator for data validation. Also installs class-transformer, dto-mapped-types and reflect-metadata',
217
191
  },
218
192
  { name: 'None', value: null, description: 'Install validation library later' },
219
193
  ],
220
194
  })));
221
- if (validationLibrary) {
222
- validateOnClient =
223
- validateOnClient ??
224
- (await confirm({
225
- message: 'Do you want to enable client validation?',
226
- }));
227
- }
228
- updateScripts =
229
- updateScripts ??
230
- (await select({
231
- message: 'Do you want to update package.json by adding "generate" and "dev" scripts?',
232
- default: 'implicit',
233
- choices: [
234
- {
235
- 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
- value: 'implicit',
238
- },
239
- {
240
- name: 'Yes, use "concurrently" explicitly',
241
- 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"`)}`,
243
- },
244
- {
245
- name: 'No',
246
- value: undefined,
247
- description: 'Add the scripts manually',
248
- },
249
- ],
250
- }));
195
+ updateScripts ??= await select({
196
+ message: 'Do you want to update "dev" NPM script at package.json?',
197
+ default: 'implicit',
198
+ choices: [
199
+ {
200
+ name: 'Yes, use "concurrently" implicitly',
201
+ value: 'implicit',
202
+ 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')}"`)}`,
203
+ },
204
+ {
205
+ name: 'Yes, use "concurrently" explicitly',
206
+ value: 'explicit',
207
+ 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')}"`)}`,
208
+ },
209
+ {
210
+ name: 'No',
211
+ value: undefined,
212
+ description: 'Add the NPM scripts manually',
213
+ },
214
+ ],
215
+ });
216
+ reactQuery ??= await confirm({
217
+ default: false,
218
+ message: 'Do you want to use @tanstack/react-query for data fetching at React components?',
219
+ });
251
220
  if (typeof updateTsConfig === 'undefined') {
252
221
  let shouldAsk = false;
253
222
  try {
254
- shouldAsk = !(await checkTSConfigForExperimentalDecorators(root));
223
+ shouldAsk = !(await checkTSConfigForExperimentalDecorators(root)); // TODO also check for emitDecoratorMetadata when vovk-dto is used
255
224
  }
256
225
  catch (error) {
257
- log.error(`Failed to check tsconfig.json for experimentalDecorators: ${error.message}`);
226
+ log.error(`Failed to check tsconfig.json for "experimentalDecorators": ${error.message}`);
258
227
  }
259
228
  if (shouldAsk) {
229
+ const keys = ['experimentalDecorators'];
230
+ if (validationLibrary === 'vovk-dto') {
231
+ keys.push('emitDecoratorMetadata');
232
+ }
260
233
  updateTsConfig = await confirm({
261
- message: 'Do you want to add experimentalDecorators to tsconfig.json?',
234
+ message: `Do you want to add ${keys.map((k) => `"${k}"`).join(' and ')} to tsconfig.json? (recommended)`,
262
235
  });
263
236
  }
264
237
  }
265
- await this.#init({ configPaths }, {
238
+ lang ??= await checkbox({
239
+ message: 'Do you want to generate RPC client for other languages besides TypeScript?',
240
+ choices: [
241
+ { name: 'Python', value: 'py' },
242
+ { name: 'Rust', value: 'rs' },
243
+ ],
244
+ });
245
+ await this.#init({ configPaths, pkgJson }, {
266
246
  useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
267
247
  useYarn: useYarn ?? false,
268
248
  usePnpm: usePnpm ?? false,
@@ -271,7 +251,8 @@ export class Init {
271
251
  updateTsConfig,
272
252
  updateScripts,
273
253
  validationLibrary,
274
- validateOnClient,
254
+ reactQuery,
255
+ lang,
275
256
  dryRun,
276
257
  channel,
277
258
  });
@@ -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,6 @@
1
- import { spawn } from 'child_process';
2
- function getPackageManager(options) {
1
+ import { spawn } from 'node:child_process';
2
+ import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
3
+ export function getPackageManager(options) {
3
4
  if (options.useNpm)
4
5
  return 'npm';
5
6
  if (options.useYarn)
@@ -12,9 +13,10 @@ 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();
@@ -0,0 +1,13 @@
1
+ import type { InitOptions } from '../types.mjs';
2
+ import type getLogger from '../utils/getLogger.mjs';
3
+ export default function logUpdateDependenciesError({ useNpm, useYarn, usePnpm, useBun, log, dependencies, devDependencies, error, channel, }: {
4
+ useNpm?: boolean;
5
+ useYarn?: boolean;
6
+ usePnpm?: boolean;
7
+ useBun?: boolean;
8
+ log: ReturnType<typeof getLogger>;
9
+ dependencies: string[];
10
+ devDependencies: string[];
11
+ error: Error;
12
+ channel: InitOptions['channel'];
13
+ }): void;
@@ -0,0 +1,51 @@
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, channel, }) {
4
+ const packageManager = getPackageManager({ useNpm, useYarn, usePnpm, useBun });
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);
12
+ if (dependencies.length > 0) {
13
+ let depInstallCmd = '';
14
+ switch (packageManager) {
15
+ case 'npm':
16
+ depInstallCmd = `npm install ${dependencies.join(' ')}`;
17
+ break;
18
+ case 'yarn':
19
+ depInstallCmd = `yarn add ${dependencies.join(' ')}`;
20
+ break;
21
+ case 'pnpm':
22
+ depInstallCmd = `pnpm add ${dependencies.join(' ')}`;
23
+ break;
24
+ case 'bun':
25
+ depInstallCmd = `bun add ${dependencies.join(' ')}`;
26
+ break;
27
+ }
28
+ installCommands.push(depInstallCmd);
29
+ }
30
+ if (devDependencies.length > 0) {
31
+ let devDepInstallCmd = '';
32
+ switch (packageManager) {
33
+ case 'npm':
34
+ devDepInstallCmd = `npm install -D ${devDependencies.join(' ')}`;
35
+ break;
36
+ case 'yarn':
37
+ devDepInstallCmd = `yarn add --dev ${devDependencies.join(' ')}`;
38
+ break;
39
+ case 'pnpm':
40
+ devDepInstallCmd = `pnpm add -D ${devDependencies.join(' ')}`;
41
+ break;
42
+ case 'bun':
43
+ devDepInstallCmd = `bun add -d ${devDependencies.join(' ')}`;
44
+ break;
45
+ }
46
+ installCommands.push(devDepInstallCmd);
47
+ }
48
+ const installCmd = installCommands.join(' && ');
49
+ // Log the error with the appropriate manual installation instructions
50
+ log.warn(`Failed to update dependencies: ${error.message}. Please, install them manually with ${chalkHighlightThing(installCmd)}`);
51
+ }
@@ -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,25 +1,60 @@
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
- async function updateDeps({ packageJson, packageNames, channel, key, }) {
5
+ async function updateDeps({ packageJson, packageNames, channel, key, log, }) {
5
6
  return Promise.all(packageNames.map(async (packageName) => {
6
- if (packageJson[key]?.[packageName])
7
- return; // Skip if already present
8
- const metadata = await getNPMPackageMetadata(packageName);
9
- const isVovk = packageName.startsWith('vovk');
10
- const latestVersion = metadata['dist-tags'][isVovk ? channel : 'latest'];
11
- if (!packageJson[key]) {
12
- 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
+ }
13
20
  }
14
- packageJson[key][packageName] = `^${latestVersion}`;
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;
31
+ }
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}`;
15
44
  }));
16
45
  }
17
46
  export default async function updateDependenciesWithoutInstalling({ log, dir, dependencyNames, devDependencyNames, channel, }) {
18
47
  const packageJsonPath = path.join(dir, 'package.json');
19
48
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
20
- await updateDeps({ packageJson, packageNames: dependencyNames, channel, key: 'dependencies' });
21
- log.debug('Updated dependencies in package.json');
22
- await updateDeps({ packageJson, packageNames: devDependencyNames, channel, key: 'devDependencies' });
23
- log.debug('Updated devDependencies in package.json');
49
+ await updateDeps({ packageJson, packageNames: dependencyNames, channel, log, key: 'dependencies' });
50
+ await updateDeps({ packageJson, packageNames: devDependencyNames, channel, log, key: 'devDependencies' });
24
51
  await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
52
+ log.info('Added dependencies to package.json:');
53
+ for (const dependency of dependencyNames) {
54
+ log.raw.info(` - ${chalk.cyan(dependency)}`);
55
+ }
56
+ log.info('Added devDependencies to package.json:');
57
+ for (const dependency of devDependencyNames) {
58
+ log.raw.info(` - ${chalk.cyan(dependency)}`);
59
+ }
25
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>;