vovk-cli 0.0.1-draft.12 → 0.0.1-draft.121

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