vovk-cli 0.0.1-draft.5 → 0.0.1-draft.53

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 (80) hide show
  1. package/README.md +29 -1
  2. package/client-templates/main/main.d.ts.ejs +15 -0
  3. package/client-templates/main/main.js.ejs +16 -0
  4. package/client-templates/module/module.d.ts.ejs +15 -0
  5. package/client-templates/module/module.js.ejs +20 -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/dev/ensureClient.d.mts +5 -0
  11. package/dist/dev/ensureClient.mjs +30 -0
  12. package/dist/{watcher → dev}/ensureSchemaFiles.d.mts +3 -0
  13. package/dist/{watcher → dev}/ensureSchemaFiles.mjs +6 -4
  14. package/dist/dev/index.d.mts +6 -0
  15. package/dist/{watcher → dev}/index.mjs +128 -62
  16. package/dist/{watcher → dev}/isMetadataEmpty.mjs +1 -1
  17. package/dist/{watcher → dev}/logDiffResult.d.mts +2 -2
  18. package/dist/dev/logDiffResult.mjs +57 -0
  19. package/dist/{watcher → dev}/writeOneSchemaFile.d.mts +1 -1
  20. package/dist/{watcher → dev}/writeOneSchemaFile.mjs +3 -3
  21. package/dist/generate/getClientTemplates.d.mts +11 -0
  22. package/dist/generate/getClientTemplates.mjs +27 -0
  23. package/dist/generate/index.d.mts +12 -0
  24. package/dist/generate/index.mjs +79 -0
  25. package/dist/getProjectInfo/getConfig.mjs +5 -5
  26. package/dist/getProjectInfo/getConfigAbsolutePaths.mjs +2 -2
  27. package/dist/getProjectInfo/getRelativeSrcRoot.mjs +3 -3
  28. package/dist/getProjectInfo/getUserConfig.mjs +3 -1
  29. package/dist/getProjectInfo/importUncachedModule.mjs +0 -1
  30. package/dist/getProjectInfo/importUncachedModuleWorker.mjs +0 -1
  31. package/dist/getProjectInfo/index.d.mts +2 -1
  32. package/dist/getProjectInfo/index.mjs +14 -10
  33. package/dist/index.d.mts +1 -27
  34. package/dist/index.mjs +47 -60
  35. package/dist/init/checkTSConfigForExperimentalDecorators.mjs +2 -2
  36. package/dist/init/createConfig.d.mts +3 -4
  37. package/dist/init/createConfig.mjs +6 -8
  38. package/dist/init/getTemplateFilesFromPackage.d.mts +2 -1
  39. package/dist/init/getTemplateFilesFromPackage.mjs +4 -5
  40. package/dist/init/index.d.mts +1 -2
  41. package/dist/init/index.mjs +46 -93
  42. package/dist/init/installDependencies.d.mts +4 -1
  43. package/dist/init/installDependencies.mjs +2 -2
  44. package/dist/init/logUpdateDependenciesError.d.mts +11 -0
  45. package/dist/init/logUpdateDependenciesError.mjs +45 -0
  46. package/dist/init/updateDependenciesWithoutInstalling.d.mts +3 -2
  47. package/dist/init/updateDependenciesWithoutInstalling.mjs +13 -8
  48. package/dist/init/updateNPMScripts.d.mts +3 -1
  49. package/dist/init/updateNPMScripts.mjs +10 -6
  50. package/dist/init/updateTypeScriptConfig.mjs +2 -2
  51. package/dist/initProgram.d.mts +2 -0
  52. package/dist/initProgram.mjs +21 -0
  53. package/dist/locateSegments.d.mts +7 -1
  54. package/dist/locateSegments.mjs +9 -6
  55. package/dist/new/addClassToSegmentCode.d.mts +1 -2
  56. package/dist/new/addClassToSegmentCode.mjs +9 -5
  57. package/dist/new/addCommonTerms.mjs +1 -0
  58. package/dist/new/index.d.mts +2 -2
  59. package/dist/new/index.mjs +4 -4
  60. package/dist/new/newModule.d.mts +4 -4
  61. package/dist/new/newModule.mjs +45 -33
  62. package/dist/new/newSegment.mjs +6 -6
  63. package/dist/new/render.mjs +2 -5
  64. package/dist/postinstall.mjs +16 -17
  65. package/dist/types.d.mts +42 -9
  66. package/dist/utils/debounceWithArgs.d.mts +1 -1
  67. package/dist/utils/debounceWithArgs.mjs +24 -9
  68. package/dist/utils/formatLoggedSegmentName.mjs +1 -1
  69. package/dist/utils/getAvailablePort.mjs +3 -2
  70. package/dist/utils/getFileSystemEntryType.mjs +1 -1
  71. package/package.json +20 -17
  72. package/templates/controller.ejs +12 -11
  73. package/templates/service.ejs +6 -6
  74. package/dist/generateClient.d.mts +0 -7
  75. package/dist/generateClient.mjs +0 -97
  76. package/dist/watcher/diffSchema.d.mts +0 -43
  77. package/dist/watcher/index.d.mts +0 -6
  78. package/dist/watcher/logDiffResult.mjs +0 -90
  79. package/templates/worker.ejs +0 -1
  80. /package/dist/{watcher → dev}/isMetadataEmpty.d.mts +0 -0
@@ -1,7 +1,6 @@
1
1
  import { parentPort, workerData as wData } from 'node:worker_threads';
2
2
  import { pathToFileURL } from 'node:url';
3
3
  void (async () => {
4
- // TODO Comments
5
4
  if (!parentPort)
6
5
  return;
7
6
  const workerData = wData;
@@ -6,11 +6,12 @@ export default function getProjectInfo({ port: givenPort, clientOutDir, cwd, }?:
6
6
  }): Promise<{
7
7
  cwd: string;
8
8
  port: string;
9
- apiEntryPoint: string;
9
+ apiRoot: string;
10
10
  apiDir: string;
11
11
  srcRoot: string;
12
12
  schemaOutImportPath: string;
13
13
  fetcherClientImportPath: string;
14
+ createRPCImportPath: string;
14
15
  validateOnClientImportPath: string | null;
15
16
  config: Required<import("../types.mjs").VovkConfig>;
16
17
  log: {
@@ -1,4 +1,4 @@
1
- import path from 'path';
1
+ import path from 'node:path';
2
2
  import getConfig from './getConfig.mjs';
3
3
  import getLogger from '../utils/getLogger.mjs';
4
4
  export default async function getProjectInfo({ port: givenPort, clientOutDir, cwd = process.cwd(), } = {}) {
@@ -6,15 +6,18 @@ export default async function getProjectInfo({ port: givenPort, clientOutDir, cw
6
6
  // Make PORT available to the config file at getConfig
7
7
  process.env.PORT = port;
8
8
  const { config, srcRoot, configAbsolutePaths, userConfig, error } = await getConfig({ clientOutDir, cwd });
9
- const apiEntryPoint = `${config.origin ?? ''}/${config.rootEntry}`;
9
+ const apiRoot = `${config.origin ?? ''}/${config.rootEntry}`;
10
10
  const apiDir = path.join(srcRoot, 'app', config.rootEntry);
11
- const schemaOutImportPath = path.relative(config.clientOutDir, config.schemaOutDir);
12
- const fetcherClientImportPath = config.fetcher.startsWith('.')
13
- ? path.relative(config.clientOutDir, config.fetcher)
14
- : config.fetcher;
15
- const validateOnClientImportPath = config.validateOnClient?.startsWith('.')
16
- ? path.relative(config.clientOutDir, config.validateOnClient)
17
- : config.validateOnClient;
11
+ const schemaOutImportPath = path.relative(config.clientOutDir, config.schemaOutDir).replace(/\\/g, '/'); // windows fix
12
+ const fetcherClientImportPath = config.fetcherPath.startsWith('.')
13
+ ? path.relative(config.clientOutDir, config.fetcherPath)
14
+ : config.fetcherPath;
15
+ const createRPCImportPath = config.createRPCPath.startsWith('.')
16
+ ? path.relative(config.clientOutDir, config.createRPCPath)
17
+ : config.createRPCPath;
18
+ const validateOnClientImportPath = config.validateOnClientPath?.startsWith('.')
19
+ ? path.relative(config.clientOutDir, config.validateOnClientPath)
20
+ : config.validateOnClientPath;
18
21
  const log = getLogger(config.logLevel);
19
22
  if (configAbsolutePaths.length > 1) {
20
23
  log.warn(`Multiple config files found. Using the first one: ${configAbsolutePaths[0]}`);
@@ -25,11 +28,12 @@ export default async function getProjectInfo({ port: givenPort, clientOutDir, cw
25
28
  return {
26
29
  cwd,
27
30
  port,
28
- apiEntryPoint,
31
+ apiRoot,
29
32
  apiDir,
30
33
  srcRoot,
31
34
  schemaOutImportPath,
32
35
  fetcherClientImportPath,
36
+ createRPCImportPath,
33
37
  validateOnClientImportPath,
34
38
  config,
35
39
  log,
package/dist/index.d.mts CHANGED
@@ -1,30 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import type { LogLevelNames } from 'loglevel';
4
- import type { VovkConfig, VovkEnv } from './types.mjs';
5
2
  import 'dotenv/config';
3
+ import type { VovkConfig, VovkEnv } from './types.mjs';
6
4
  export type { VovkConfig, VovkEnv };
7
- export interface InitOptions {
8
- yes?: boolean;
9
- logLevel: LogLevelNames;
10
- useNpm?: boolean;
11
- useYarn?: boolean;
12
- usePnpm?: boolean;
13
- useBun?: boolean;
14
- skipInstall?: boolean;
15
- updateTsConfig?: boolean;
16
- updateScripts?: 'implicit' | 'explicit';
17
- validationLibrary?: string | null;
18
- validateOnClient?: boolean;
19
- dryRun?: boolean;
20
- channel?: 'latest' | 'beta' | 'dev';
21
- }
22
- export interface NewOptions {
23
- dryRun?: boolean;
24
- template?: string;
25
- dirName?: string;
26
- fileName?: string;
27
- overwrite?: boolean;
28
- }
29
- declare const program: Command;
30
- export declare function initProgram(p: typeof program, command: string): Command;
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 { pathToFileURL } from 'node:url';
5
+ import 'dotenv/config';
3
6
  import { Command } from 'commander';
4
- import { readFileSync } from 'fs';
5
7
  import concurrently from 'concurrently';
6
8
  import getAvailablePort from './utils/getAvailablePort.mjs';
7
9
  import getProjectInfo from './getProjectInfo/index.mjs';
8
- import generateClient from './generateClient.mjs';
10
+ import generate from './generate/index.mjs';
9
11
  import locateSegments from './locateSegments.mjs';
10
- import { VovkCLIWatcher } from './watcher/index.mjs';
11
- import { Init } from './init/index.mjs';
12
+ import { VovkDev } from './dev/index.mjs';
12
13
  import newComponents from './new/index.mjs';
13
- import 'dotenv/config';
14
+ import initProgram from './initProgram.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)
21
+ .alias('d')
22
+ .description('start schema watcher (optional flag --next-dev to start it with Next.js)')
23
+ .option('--next-dev', 'start schema watcher and Next.js with automatic port allocation')
24
+ .option('--exit', 'kill the processe when schema and client is generated')
22
25
  .allowUnknownOption(true)
23
26
  .action(async (options, command) => {
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
- {
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
  {
42
43
  command: `npx next dev ${command.args.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,41 @@ 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')
74
+ .alias('g')
63
75
  .description('Generate client')
64
- .option('--client-out <path>', 'Path to output directory')
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('--full-schema [fileName]', 'generate client with full schema')
79
+ .option('--prettify', 'prettify output files')
65
80
  .action(async (options) => {
66
- const projectInfo = await getProjectInfo({ clientOutDir: options.clientOut });
81
+ const { clientOutDir, templates, prettify, fullSchema } = options;
82
+ const projectInfo = await getProjectInfo({ clientOutDir });
67
83
  const { cwd, config, apiDir } = projectInfo;
68
- const segments = await locateSegments(apiDir);
84
+ const segments = await locateSegments({ dir: apiDir, config });
69
85
  const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
70
- const schema = (await import(path.join(schemaOutAbsolutePath, 'index.js')));
71
- await generateClient(projectInfo, segments, schema.default);
86
+ const schemaImportUrl = pathToFileURL(path.join(schemaOutAbsolutePath, 'index.js')).href;
87
+ const { default: segmentsSchema } = (await import(schemaImportUrl));
88
+ await generate({ projectInfo, segments, segmentsSchema, templates, prettify, fullSchema });
72
89
  });
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
90
  program
95
91
  .command('new [components...]')
96
92
  .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', 'Override config template')
100
- .option('--dir-name', 'Override dirName in template file')
101
- .option('--file-name', 'Override fileName in template file')
102
- .option('--dry-run', 'Do not write files to disk')
93
+ .description('create new components. "vovk new [...components] [segmentName/]moduleName" to create a new module or "vovk new segment [segmentName]" to create a new segment')
94
+ .option('-o, --overwrite', 'overwrite existing files')
95
+ .option('--template, --templates <templates...>', 'override config template; accepts an array of strings that correspond the order of the components')
96
+ .option('--dir <dirname>', 'override dirName in template file; relative to the root of the project')
97
+ .option('--no-segment-update', 'do not update segment files when creating a new module')
98
+ .option('--dry-run', 'do not write files to disk')
103
99
  .action((components, options) => newComponents(components, options));
104
100
  program
105
101
  .command('help')
106
102
  .description('Show help message')
107
103
  .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
104
  program.parse(process.argv);
118
105
  if (!process.argv.slice(2).length) {
119
106
  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, validateOnClient, 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' | 'validateOnClient' | '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, validateOnClient, 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,15 +14,13 @@ 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
19
  if (validateOnClient) {
22
- config.validateOnClient = `${validationLibrary}/validateOnClient`;
20
+ config.validateOnClientPath = `${validationLibrary}/validateOnClient`;
23
21
  }
24
22
  try {
25
- const validationTemplates = await getTemplateFilesFromPackage(validationLibrary);
23
+ const validationTemplates = await getTemplateFilesFromPackage(validationLibrary, channel);
26
24
  Object.assign(templates, validationTemplates);
27
25
  }
28
26
  catch (error) {
@@ -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,5 +1,4 @@
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;
@@ -1,80 +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
- 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, dryRun, channel, }) {
76
21
  const { log, root } = this;
77
- const dependencies = ['vovk'];
22
+ const dependencies = ['vovk', 'vovk-client'];
78
23
  const devDependencies = ['vovk-cli'];
79
24
  // delete older config files
80
25
  if (configPaths.length) {
@@ -86,13 +31,13 @@ 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
  }
92
37
  if (updateScripts) {
93
38
  try {
94
39
  if (!dryRun)
95
- await updateNPMScripts(root, updateScripts);
40
+ await updateNPMScripts(pkgJson, root, updateScripts);
96
41
  log.info('Updated scripts at package.json');
97
42
  }
98
43
  catch (error) {
@@ -113,6 +58,7 @@ export class Init {
113
58
  }
114
59
  }
115
60
  if (!dryRun) {
61
+ let depsUpdated = false;
116
62
  try {
117
63
  await updateDependenciesWithoutInstalling({
118
64
  log,
@@ -121,27 +67,34 @@ export class Init {
121
67
  devDependencyNames: devDependencies,
122
68
  channel: channel ?? 'latest',
123
69
  });
124
- log.info('Updated dependencies and devDependencies in package.json');
70
+ depsUpdated = true;
125
71
  }
126
- catch (error) {
127
- log.error(`Failed to update dependencies: ${error.message}. Please `);
72
+ catch (e) {
73
+ const error = e;
74
+ logUpdateDependenciesError({ log, error, useNpm, useYarn, usePnpm, useBun, dependencies, devDependencies });
128
75
  }
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');
76
+ if (depsUpdated) {
77
+ const packageManager = getPackageManager({ useNpm, useYarn, usePnpm, useBun });
78
+ if (skipInstall) {
79
+ log.info(`Installation skipped. Please, install them manually with ${chalkHighlightThing(packageManager + ' install')}`);
142
80
  }
143
- catch (error) {
144
- log.error(`Failed to install dependencies: ${error.message}`);
81
+ else {
82
+ try {
83
+ await installDependencies({
84
+ log,
85
+ cwd: root,
86
+ options: {
87
+ useNpm,
88
+ useYarn,
89
+ usePnpm,
90
+ useBun,
91
+ },
92
+ });
93
+ log.info('Dependencies installed successfully');
94
+ }
95
+ catch (error) {
96
+ log.warn(`Failed to install dependencies: ${error.message}. Please, install them manually with ${chalkHighlightThing(packageManager + ' install')}`);
97
+ }
145
98
  }
146
99
  }
147
100
  }
@@ -149,8 +102,7 @@ export class Init {
149
102
  const { configAbsolutePath } = await createConfig({
150
103
  root,
151
104
  log,
152
- options: { validationLibrary, validateOnClient },
153
- dryRun,
105
+ options: { validationLibrary, validateOnClient, channel, dryRun },
154
106
  });
155
107
  log.info('Config created successfully at ' + configAbsolutePath);
156
108
  }
@@ -162,11 +114,12 @@ export class Init {
162
114
  const cwd = process.cwd();
163
115
  const root = path.resolve(cwd, prefix);
164
116
  const log = getLogger(logLevel);
117
+ const pkgJson = await NPMCliPackageJson.load(root);
165
118
  this.root = root;
166
119
  this.log = log;
167
120
  const configPaths = await getConfigPaths({ cwd, relativePath: prefix });
168
121
  if (yes) {
169
- return this.#init({ configPaths }, {
122
+ return this.#init({ configPaths, pkgJson }, {
170
123
  useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
171
124
  useYarn: useYarn ?? false,
172
125
  usePnpm: usePnpm ?? false,
@@ -213,7 +166,7 @@ export class Init {
213
166
  {
214
167
  name: 'vovk-dto',
215
168
  value: 'vovk-dto',
216
- description: 'Use class-validator and class-transformer for data validation',
169
+ description: 'Use class-validator for data validation. Also installs class-transformer, vovk-mapped-types and reflect-metadata',
217
170
  },
218
171
  { name: 'None', value: null, description: 'Install validation library later' },
219
172
  ],
@@ -228,23 +181,23 @@ export class Init {
228
181
  updateScripts =
229
182
  updateScripts ??
230
183
  (await select({
231
- message: 'Do you want to update package.json by adding "generate" and "dev" scripts?',
184
+ message: 'Do you want to update package.json by adding "generate" and "dev" NPM scripts?',
232
185
  default: 'implicit',
233
186
  choices: [
234
187
  {
235
188
  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
189
  value: 'implicit',
190
+ 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
191
  },
239
192
  {
240
193
  name: 'Yes, use "concurrently" explicitly',
241
194
  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"`)}`,
195
+ 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
196
  },
244
197
  {
245
198
  name: 'No',
246
199
  value: undefined,
247
- description: 'Add the scripts manually',
200
+ description: 'Add the NPM scripts manually',
248
201
  },
249
202
  ],
250
203
  }));
@@ -262,7 +215,7 @@ export class Init {
262
215
  });
263
216
  }
264
217
  }
265
- await this.#init({ configPaths }, {
218
+ await this.#init({ configPaths, pkgJson }, {
266
219
  useNpm: useNpm ?? (!useYarn && !usePnpm && !useBun),
267
220
  useYarn: useYarn ?? false,
268
221
  usePnpm: usePnpm ?? false,
@@ -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;