vovk-cli 0.0.1-draft.2 → 0.0.1-draft.20

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 (70) hide show
  1. package/README.md +1 -1
  2. package/dist/{watcher → dev}/diffSchema.d.mts +1 -1
  3. package/dist/dev/ensureClient.d.mts +5 -0
  4. package/dist/dev/ensureClient.mjs +31 -0
  5. package/dist/{watcher → dev}/ensureSchemaFiles.mjs +20 -8
  6. package/dist/{watcher → dev}/index.d.mts +1 -1
  7. package/dist/{watcher → dev}/index.mjs +25 -22
  8. package/dist/{watcher → dev}/logDiffResult.d.mts +2 -2
  9. package/dist/{watcher → dev}/logDiffResult.mjs +13 -9
  10. package/dist/{watcher → dev}/writeOneSchemaFile.d.mts +1 -1
  11. package/dist/{watcher → dev}/writeOneSchemaFile.mjs +4 -3
  12. package/dist/generateClient.d.mts +1 -1
  13. package/dist/generateClient.mjs +6 -6
  14. package/dist/getProjectInfo/getConfigAbsolutePaths.mjs +2 -2
  15. package/dist/getProjectInfo/getRelativeSrcRoot.mjs +4 -4
  16. package/dist/getProjectInfo/getUserConfig.mjs +1 -1
  17. package/dist/getProjectInfo/index.mjs +1 -1
  18. package/dist/index.d.mts +2 -24
  19. package/dist/index.mjs +12 -37
  20. package/dist/init/checkTSConfigForExperimentalDecorators.mjs +2 -2
  21. package/dist/init/createConfig.d.mts +3 -4
  22. package/dist/init/createConfig.mjs +5 -5
  23. package/dist/init/getTemplateFilesFromPackage.d.mts +2 -1
  24. package/dist/init/getTemplateFilesFromPackage.mjs +3 -4
  25. package/dist/init/index.d.mts +1 -2
  26. package/dist/init/index.mjs +36 -85
  27. package/dist/init/installDependencies.d.mts +4 -1
  28. package/dist/init/installDependencies.mjs +2 -2
  29. package/dist/init/logUpdateDependenciesError.d.mts +11 -0
  30. package/dist/init/logUpdateDependenciesError.mjs +45 -0
  31. package/dist/init/updateDependenciesWithoutInstalling.d.mts +3 -2
  32. package/dist/init/updateDependenciesWithoutInstalling.mjs +12 -5
  33. package/dist/init/updateNPMScripts.mjs +1 -1
  34. package/dist/init/updateTypeScriptConfig.mjs +2 -2
  35. package/dist/initProgram.d.mts +2 -0
  36. package/dist/initProgram.mjs +21 -0
  37. package/dist/locateSegments.mjs +2 -2
  38. package/dist/new/addClassToSegmentCode.mjs +6 -2
  39. package/dist/new/index.d.mts +2 -2
  40. package/dist/new/index.mjs +13 -3
  41. package/dist/new/newModule.d.mts +6 -2
  42. package/dist/new/newModule.mjs +50 -26
  43. package/dist/new/newSegment.d.mts +3 -2
  44. package/dist/new/newSegment.mjs +5 -5
  45. package/dist/new/render.d.mts +3 -7
  46. package/dist/new/render.mjs +11 -7
  47. package/dist/postinstall.mjs +5 -3
  48. package/dist/types.d.mts +38 -2
  49. package/dist/utils/formatLoggedSegmentName.mjs +1 -1
  50. package/dist/utils/getAvailablePort.mjs +3 -2
  51. package/dist/utils/getFileSystemEntryType.mjs +1 -1
  52. package/package.json +8 -6
  53. package/templates/controller.ejs +18 -17
  54. package/templates/service.ejs +24 -4
  55. package/templates/worker.ejs +24 -1
  56. package/dist/getProjectInfo/directoryExists.d.mts +0 -1
  57. package/dist/getProjectInfo/directoryExists.mjs +0 -10
  58. package/templates_old/MyThingController.c.only.template.ts +0 -32
  59. package/templates_old/MyThingController.c.template.ts +0 -34
  60. package/templates_old/MyThingService.s.template.ts +0 -18
  61. package/templates_old/controller.ejs +0 -85
  62. package/templates_old/service.ejs +0 -9
  63. package/templates_old/worker.ejs +0 -9
  64. package/templates_old/zod/MyThingController.c.only.template.ts +0 -32
  65. package/templates_old/zod/MyThingController.c.template.ts +0 -39
  66. package/templates_old/zod/MyThingService.s.template.ts +0 -18
  67. /package/dist/{watcher → dev}/diffSchema.mjs +0 -0
  68. /package/dist/{watcher → dev}/ensureSchemaFiles.d.mts +0 -0
  69. /package/dist/{watcher → dev}/isMetadataEmpty.d.mts +0 -0
  70. /package/dist/{watcher → dev}/isMetadataEmpty.mjs +0 -0
package/README.md CHANGED
@@ -1 +1 @@
1
- Description is coming soon.
1
+ Description is coming soon.
@@ -1,5 +1,5 @@
1
1
  import type { VovkSchema } from 'vovk';
2
- import { _VovkControllerSchema, _VovkWorkerSchema } from 'vovk/types';
2
+ import type { _VovkControllerSchema, _VovkWorkerSchema } from 'vovk/types';
3
3
  interface HandlersDiff {
4
4
  nameOfClass: string;
5
5
  added: string[];
@@ -0,0 +1,5 @@
1
+ import type { ProjectInfo } from '../getProjectInfo/index.mjs';
2
+ export default function ensureClient(projectInfo: ProjectInfo): Promise<{
3
+ written: boolean;
4
+ path: string;
5
+ }>;
@@ -0,0 +1,31 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs/promises';
3
+ export default async function ensureClient(projectInfo) {
4
+ const { config, cwd, log } = projectInfo;
5
+ const now = Date.now();
6
+ const clientoOutDirAbsolutePath = path.join(cwd, config.clientOutDir);
7
+ const dts = `// auto-generated
8
+ // This is a temporary placeholder to avoid errors if client is imported before it's generated.
9
+ // If you still see this text, the client is not generated yet because of an unknown problem.
10
+ // Feel free to report an issue at https://github.com/finom/vovk/issues`;
11
+ const js = dts;
12
+ const ts = dts;
13
+ const localJsAbsolutePath = path.join(clientoOutDirAbsolutePath, 'client.js');
14
+ const localDtsAbsolutePath = path.join(clientoOutDirAbsolutePath, 'client.d.ts');
15
+ const localTsAbsolutePath = path.join(clientoOutDirAbsolutePath, 'index.ts');
16
+ const existingJs = await fs.readFile(localJsAbsolutePath, 'utf-8').catch(() => null);
17
+ const existingDts = await fs.readFile(localDtsAbsolutePath, 'utf-8').catch(() => null);
18
+ const existingTs = await fs.readFile(localTsAbsolutePath, 'utf-8').catch(() => null);
19
+ if (existingJs && existingDts && existingTs) {
20
+ return { written: false, path: clientoOutDirAbsolutePath };
21
+ }
22
+ await fs.mkdir(clientoOutDirAbsolutePath, { recursive: true });
23
+ if (!existingJs)
24
+ await fs.writeFile(localJsAbsolutePath, js);
25
+ if (!existingDts)
26
+ await fs.writeFile(localDtsAbsolutePath, dts);
27
+ if (!existingTs)
28
+ await fs.writeFile(localTsAbsolutePath, ts);
29
+ log.info(`Empty client files are generated in ${Date.now() - now}ms`);
30
+ return { written: true, path: clientoOutDirAbsolutePath };
31
+ }
@@ -1,5 +1,5 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
3
  import debounce from 'lodash/debounce.js';
4
4
  import writeOneSchemaFile, { ROOT_SEGMENT_SCHEMA_NAME } from './writeOneSchemaFile.mjs';
5
5
  import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
@@ -7,17 +7,29 @@ export default async function ensureSchemaFiles(projectInfo, schemaOutAbsolutePa
7
7
  const now = Date.now();
8
8
  let hasChanged = false;
9
9
  // Create index.js file
10
- const indexContent = segmentNames
10
+ const indexContent = `// auto-generated
11
+ ${segmentNames
11
12
  .map((segmentName) => {
12
13
  return `module.exports['${segmentName}'] = require('./${segmentName || ROOT_SEGMENT_SCHEMA_NAME}.json');`;
13
14
  })
14
- .join('\n');
15
- const dTsContent = `import type { VovkSchema } from 'vovk';
16
- declare const segmentSchema: Record<string, VovkSchema>;
15
+ .join('\n')}`;
16
+ const dTsContent = `// auto-generated
17
+ import type { VovkSchema } from 'vovk';
18
+ declare const segmentSchema: {
19
+ ${segmentNames.map((segmentName) => ` '${segmentName}': VovkSchema;`).join('\n')}
20
+ };
17
21
  export default segmentSchema;`;
22
+ const jsAbsolutePath = path.join(schemaOutAbsolutePath, 'index.js');
23
+ const dTsAbsolutePath = path.join(schemaOutAbsolutePath, 'index.d.ts');
24
+ const existingJs = await fs.readFile(jsAbsolutePath, 'utf-8').catch(() => null);
25
+ const existingDTs = await fs.readFile(dTsAbsolutePath, 'utf-8').catch(() => null);
18
26
  await fs.mkdir(schemaOutAbsolutePath, { recursive: true });
19
- await fs.writeFile(path.join(schemaOutAbsolutePath, 'index.js'), indexContent);
20
- await fs.writeFile(path.join(schemaOutAbsolutePath, 'index.d.ts'), dTsContent);
27
+ if (existingJs !== indexContent) {
28
+ await fs.writeFile(jsAbsolutePath, indexContent);
29
+ }
30
+ if (existingDTs !== dTsContent) {
31
+ await fs.writeFile(dTsAbsolutePath, dTsContent);
32
+ }
21
33
  // Create JSON files (if not exist) with name [segmentName].json (where segmentName can include /, which means the folder structure can be nested)
22
34
  await Promise.all(segmentNames.map(async (segmentName) => {
23
35
  const { isCreated } = await writeOneSchemaFile({
@@ -1,4 +1,4 @@
1
- export declare class VovkCLIWatcher {
1
+ export declare class VovkDev {
2
2
  #private;
3
3
  start({ clientOutDir }?: {
4
4
  clientOutDir?: string;
@@ -1,20 +1,21 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
1
3
  import * as chokidar from 'chokidar';
2
- import fs from 'fs/promises';
3
- import getProjectInfo from '../getProjectInfo/index.mjs';
4
- import path from 'path';
4
+ import { Agent, setGlobalDispatcher } from 'undici';
5
+ import keyBy from 'lodash/keyBy.js';
6
+ import capitalize from 'lodash/capitalize.js';
7
+ import debounce from 'lodash/debounce.js';
8
+ import isEmpty from 'lodash/isEmpty.js';
5
9
  import { debouncedEnsureSchemaFiles } from './ensureSchemaFiles.mjs';
6
10
  import writeOneSchemaFile from './writeOneSchemaFile.mjs';
7
11
  import logDiffResult from './logDiffResult.mjs';
12
+ import ensureClient from './ensureClient.mjs';
13
+ import getProjectInfo from '../getProjectInfo/index.mjs';
8
14
  import generateClient from '../generateClient.mjs';
9
15
  import locateSegments from '../locateSegments.mjs';
10
16
  import debounceWithArgs from '../utils/debounceWithArgs.mjs';
11
- import debounce from 'lodash/debounce.js';
12
- import isEmpty from 'lodash/isEmpty.js';
13
17
  import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
14
- import keyBy from 'lodash/keyBy.js';
15
- import capitalize from 'lodash/capitalize.js';
16
- import { Agent, setGlobalDispatcher } from 'undici';
17
- export class VovkCLIWatcher {
18
+ export class VovkDev {
18
19
  #projectInfo;
19
20
  #segments = [];
20
21
  #schemas = {};
@@ -27,7 +28,7 @@ export class VovkCLIWatcher {
27
28
  const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
28
29
  const apiDirAbsolutePath = path.join(cwd, apiDir);
29
30
  const getSegmentName = (filePath) => path.relative(apiDirAbsolutePath, filePath).replace(segmentReg, '');
30
- log.debug(`Watching segments in ${apiDirAbsolutePath}`);
31
+ log.debug(`Watching segments at ${apiDirAbsolutePath}`);
31
32
  this.#segmentWatcher = chokidar
32
33
  .watch(apiDirAbsolutePath, {
33
34
  persistent: true,
@@ -51,7 +52,6 @@ export class VovkCLIWatcher {
51
52
  void this.#requestSchema(getSegmentName(filePath));
52
53
  }
53
54
  })
54
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
55
55
  .on('addDir', async (dirPath) => {
56
56
  log.debug(`Directory ${dirPath} has been added to segments folder`);
57
57
  this.#segments = await locateSegments(apiDirAbsolutePath);
@@ -59,7 +59,6 @@ export class VovkCLIWatcher {
59
59
  void this.#requestSchema(segmentName);
60
60
  }
61
61
  })
62
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
63
62
  .on('unlinkDir', async (dirPath) => {
64
63
  log.debug(`Directory ${dirPath} has been removed from segments folder`);
65
64
  this.#segments = await locateSegments(apiDirAbsolutePath);
@@ -81,13 +80,13 @@ export class VovkCLIWatcher {
81
80
  log.debug('Segments watcher is ready');
82
81
  })
83
82
  .on('error', (error) => {
84
- log.error(`Error watching segments folder: ${error.message}`);
83
+ log.error(`Error watching segments folder: ${error?.message ?? 'Unknown error'}`);
85
84
  });
86
85
  };
87
86
  #watchModules = () => {
88
87
  const { config, cwd, log } = this.#projectInfo;
89
88
  const modulesDirAbsolutePath = path.join(cwd, config.modulesDir);
90
- log.debug(`Watching modules in ${modulesDirAbsolutePath}`);
89
+ log.debug(`Watching modules at ${modulesDirAbsolutePath}`);
91
90
  const processControllerChange = debounceWithArgs(this.#processControllerChange, 500);
92
91
  this.#modulesWatcher = chokidar
93
92
  .watch(modulesDirAbsolutePath, {
@@ -119,13 +118,14 @@ export class VovkCLIWatcher {
119
118
  log.debug('Modules watcher is ready');
120
119
  })
121
120
  .on('error', (error) => {
122
- log.error(`Error watching modules folder: ${error.message}`);
121
+ log.error(`Error watching modules folder: ${error?.message ?? 'Unknown error'}`);
123
122
  });
124
123
  };
125
124
  #watchConfig = () => {
126
125
  const { log, cwd } = this.#projectInfo;
127
126
  log.debug(`Watching config files`);
128
127
  let isInitial = true;
128
+ let isReady = false;
129
129
  const handle = debounce(async () => {
130
130
  this.#projectInfo = await getProjectInfo();
131
131
  if (!isInitial) {
@@ -148,18 +148,21 @@ export class VovkCLIWatcher {
148
148
  .on('change', () => void handle())
149
149
  .on('unlink', () => void handle())
150
150
  .on('ready', () => {
151
+ if (isReady)
152
+ return;
153
+ // for some reason this watcher triggers ready event twice
151
154
  log.debug('Config files watcher is ready');
155
+ isReady = true;
152
156
  })
153
- .on('error', (error) => {
154
- log.error(`Error watching config files: ${error.message}`);
155
- });
157
+ .on('error', (error) => log.error(`Error watching config files: ${error?.message ?? 'Unknown error'}`));
156
158
  void handle();
157
159
  };
158
- #watch() {
160
+ async #watch() {
159
161
  if (this.#isWatching)
160
162
  throw new Error('Already watching');
161
163
  const { log } = this.#projectInfo;
162
164
  log.debug(`Starting segments and modules watcher. Detected initial segments: ${JSON.stringify(this.#segments.map((s) => s.segmentName))}.`);
165
+ await ensureClient(this.#projectInfo);
163
166
  // automatically watches segments and modules
164
167
  this.#watchConfig();
165
168
  }
@@ -275,16 +278,16 @@ export class VovkCLIWatcher {
275
278
  const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
276
279
  this.#segments = await locateSegments(apiDirAbsolutePath);
277
280
  await debouncedEnsureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, this.#segments.map((s) => s.segmentName));
278
- // Request schema every segment in 3 seconds in order to update schema and start watching
281
+ // Request schema every segment in 5 seconds in order to update schema and start watching
279
282
  setTimeout(() => {
280
283
  for (const { segmentName } of this.#segments) {
281
284
  void this.#requestSchema(segmentName);
282
285
  }
283
286
  this.#watch();
284
- }, 3000);
287
+ }, 5000);
285
288
  }
286
289
  }
287
290
  const env = process.env;
288
291
  if (env.__VOVK_START_WATCHER_IN_STANDALONE_MODE__ === 'true') {
289
- void new VovkCLIWatcher().start();
292
+ void new VovkDev().start();
290
293
  }
@@ -1,3 +1,3 @@
1
- import { ProjectInfo } from '../getProjectInfo/index.mjs';
2
- import { DiffResult } from './diffSchema.mjs';
1
+ import type { DiffResult } from './diffSchema.mjs';
2
+ import type { ProjectInfo } from '../getProjectInfo/index.mjs';
3
3
  export default function logDiffResult(segmentName: string, diffResult: DiffResult, projectInfo: ProjectInfo): void;
@@ -1,3 +1,4 @@
1
+ import chalk from 'chalk';
1
2
  import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
2
3
  import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
3
4
  export default function logDiffResult(segmentName, diffResult, projectInfo) {
@@ -37,48 +38,51 @@ export default function logDiffResult(segmentName, diffResult, projectInfo) {
37
38
  });
38
39
  });
39
40
  const LIMIT = diffNormalized.length < 12 ? diffNormalized.length : 10;
41
+ const addedText = chalk.green('added');
42
+ const removedText = chalk.red('removed');
43
+ const changedText = chalk.cyan('changed');
40
44
  for (const diffNormalizedItem of diffNormalized.slice(0, LIMIT)) {
41
45
  switch (diffNormalizedItem.what) {
42
46
  case 'worker':
43
47
  switch (diffNormalizedItem.type) {
44
48
  case 'added':
45
- projectInfo.log.info(`Schema for worker ${chalkHighlightThing(diffNormalizedItem.name)} has been added at ${formatLoggedSegmentName(segmentName)}`);
49
+ projectInfo.log.info(`Schema for worker ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
46
50
  break;
47
51
  case 'removed':
48
- projectInfo.log.info(`Schema for worker ${chalkHighlightThing(diffNormalizedItem.name)} has been removed from ${formatLoggedSegmentName(segmentName)}`);
52
+ projectInfo.log.info(`Schema for worker ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
49
53
  break;
50
54
  }
51
55
  break;
52
56
  case 'controller':
53
57
  switch (diffNormalizedItem.type) {
54
58
  case 'added':
55
- projectInfo.log.info(`Schema for controller ${chalkHighlightThing(diffNormalizedItem.name)} has been added at ${formatLoggedSegmentName(segmentName)}`);
59
+ projectInfo.log.info(`Schema for controller ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
56
60
  break;
57
61
  case 'removed':
58
- projectInfo.log.info(`Schema for controller ${chalkHighlightThing(diffNormalizedItem.name)} has been removed from ${formatLoggedSegmentName(segmentName)}`);
62
+ projectInfo.log.info(`Schema for controller ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
59
63
  break;
60
64
  }
61
65
  break;
62
66
  case 'workerHandler':
63
67
  switch (diffNormalizedItem.type) {
64
68
  case 'added':
65
- projectInfo.log.info(`Schema for worker method ${chalkHighlightThing(diffNormalizedItem.name)} has been added at ${formatLoggedSegmentName(segmentName)}`);
69
+ projectInfo.log.info(`Schema for worker method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
66
70
  break;
67
71
  case 'removed':
68
- projectInfo.log.info(`Schema for worker method ${chalkHighlightThing(diffNormalizedItem.name)} has been removed from ${formatLoggedSegmentName(segmentName)}`);
72
+ projectInfo.log.info(`Schema for worker method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
69
73
  break;
70
74
  }
71
75
  break;
72
76
  case 'controllerHandler':
73
77
  switch (diffNormalizedItem.type) {
74
78
  case 'added':
75
- projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been added at ${formatLoggedSegmentName(segmentName)}`);
79
+ projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
76
80
  break;
77
81
  case 'removed':
78
- projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been removed from ${formatLoggedSegmentName(segmentName)}`);
82
+ projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
79
83
  break;
80
84
  case 'changed':
81
- projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been changed at ${formatLoggedSegmentName(segmentName)}`);
85
+ projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${changedText} at ${formatLoggedSegmentName(segmentName)}`);
82
86
  break;
83
87
  }
84
88
  break;
@@ -1,5 +1,5 @@
1
1
  import type { VovkSchema } from 'vovk';
2
- import { DiffResult } from './diffSchema.mjs';
2
+ import { type DiffResult } from './diffSchema.mjs';
3
3
  export declare const ROOT_SEGMENT_SCHEMA_NAME = "_root";
4
4
  export default function writeOneSchemaFile({ schemaOutAbsolutePath, schema, skipIfExists, }: {
5
5
  schemaOutAbsolutePath: string;
@@ -1,10 +1,11 @@
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 diffSchema from './diffSchema.mjs';
4
+ import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
4
5
  export const ROOT_SEGMENT_SCHEMA_NAME = '_root';
5
6
  export default async function writeOneSchemaFile({ schemaOutAbsolutePath, schema, skipIfExists = false, }) {
6
7
  const segmentPath = path.join(schemaOutAbsolutePath, `${schema.segmentName || ROOT_SEGMENT_SCHEMA_NAME}.json`);
7
- if (skipIfExists) {
8
+ if (skipIfExists && (await getFileSystemEntryType(segmentPath))) {
8
9
  try {
9
10
  await fs.stat(segmentPath);
10
11
  return { isCreated: false, diffResult: null };
@@ -1,6 +1,6 @@
1
+ import type { VovkSchema } from 'vovk';
1
2
  import type { ProjectInfo } from './getProjectInfo/index.mjs';
2
3
  import type { Segment } from './locateSegments.mjs';
3
- import type { VovkSchema } from 'vovk';
4
4
  export default function generateClient(projectInfo: ProjectInfo, segments: Segment[], segmentsSchema: Record<string, VovkSchema>): Promise<{
5
5
  written: boolean;
6
6
  path: string;
@@ -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 formatLoggedSegmentName from './utils/formatLoggedSegmentName.mjs';
4
4
  import prettify from './utils/prettify.mjs';
5
5
  export default async function generateClient(projectInfo, segments, segmentsSchema) {
@@ -48,11 +48,11 @@ type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
48
48
  ts += `
49
49
  ${validateOnClientImportPath ? `import validateOnClient from '${validateOnClientImportPath}';\n` : '\nconst validateOnClient = undefined;'}
50
50
  type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
51
- const prefix = '${apiEntryPoint}';
51
+ const apiRoot = '${apiEntryPoint}';
52
52
  `;
53
53
  js += `
54
54
  const { default: validateOnClient = null } = ${validateOnClientImportPath ? `require('${validateOnClientImportPath}')` : '{}'};
55
- const prefix = '${apiEntryPoint}';
55
+ const apiRoot = '${apiEntryPoint}';
56
56
  `;
57
57
  for (let i = 0; i < segments.length; i++) {
58
58
  const { segmentName } = segments[i];
@@ -64,8 +64,8 @@ const prefix = '${apiEntryPoint}';
64
64
  continue;
65
65
  for (const key of Object.keys(schema.controllers)) {
66
66
  dts += `export const ${key}: ReturnType<typeof clientizeController<Controllers${i}["${key}"], Options>>;\n`;
67
- js += `exports.${key} = clientizeController(schema['${segmentName}'].controllers.${key}, '${segmentName}', { fetcher, validateOnClient, defaultOptions: { prefix } });\n`;
68
- ts += `export const ${key} = clientizeController<Controllers${i}["${key}"], Options>(schema['${segmentName}'].controllers.${key}, '${segmentName}', { fetcher, validateOnClient, defaultOptions: { prefix } });\n`;
67
+ js += `exports.${key} = clientizeController(schema['${segmentName}'].controllers.${key}, '${segmentName}', { fetcher, validateOnClient, defaultOptions: { apiRoot } });\n`;
68
+ ts += `export const ${key} = clientizeController<Controllers${i}["${key}"], Options>(schema['${segmentName}'].controllers.${key}, '${segmentName}', { fetcher, validateOnClient, defaultOptions: { apiRoot } });\n`;
69
69
  }
70
70
  for (const key of Object.keys(schema.workers)) {
71
71
  dts += `export const ${key}: ReturnType<typeof promisifyWorker<Workers${i}["${key}"]>>;\n`;
@@ -1,5 +1,5 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
3
  export default async function getConfigAbsolutePaths({ cwd, relativePath, }) {
4
4
  const rootDir = path.resolve(cwd, relativePath || '');
5
5
  const baseName = 'vovk.config';
@@ -1,11 +1,11 @@
1
- import path from 'path';
2
- import directoryExists from './directoryExists.mjs';
1
+ import path from 'node:path';
2
+ import getFileSystemEntryType, { FileSystemEntryType } from '../utils/getFileSystemEntryType.mjs';
3
3
  export default async function getRelativeSrcRoot({ cwd }) {
4
4
  // Next.js Docs: src/app or src/pages will be ignored if app or pages are present in the root directory.
5
- if (await directoryExists(path.join(cwd, 'app'))) {
5
+ if ((await getFileSystemEntryType(path.join(cwd, 'app'))) === FileSystemEntryType.DIRECTORY) {
6
6
  return '.';
7
7
  }
8
- else if (await directoryExists(path.join(cwd, 'src/app'))) {
8
+ else if ((await getFileSystemEntryType(path.join(cwd, 'src/app'))) === FileSystemEntryType.DIRECTORY) {
9
9
  return './src';
10
10
  }
11
11
  throw new Error(`${cwd} Could not find app router directory. Check Next.js docs for more info.`);
@@ -11,7 +11,7 @@ async function getUserConfig({ cwd, }) {
11
11
  try {
12
12
  userConfig = await importUncachedModule(configPath);
13
13
  }
14
- catch (e) {
14
+ catch {
15
15
  try {
16
16
  const cacheBuster = Date.now();
17
17
  ({ default: userConfig } = (await import(`${configPath}?cache=${cacheBuster}`)));
@@ -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(), } = {}) {
package/dist/index.d.mts CHANGED
@@ -1,26 +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';
2
+ import type { VovkConfig, VovkDevEnv } from './types.mjs';
5
3
  import 'dotenv/config';
6
- 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
- }
25
- declare const program: Command;
26
- export declare function initProgram(p: typeof program, command: string): Command;
4
+ export type { VovkConfig, VovkDevEnv };
package/dist/index.mjs CHANGED
@@ -1,19 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import path from 'path';
2
+ import path from 'node:path';
3
+ import { readFileSync } from 'node:fs';
3
4
  import { Command } from 'commander';
4
- import { readFileSync } from 'fs';
5
5
  import concurrently from 'concurrently';
6
6
  import getAvailablePort from './utils/getAvailablePort.mjs';
7
7
  import getProjectInfo from './getProjectInfo/index.mjs';
8
8
  import generateClient from './generateClient.mjs';
9
9
  import locateSegments from './locateSegments.mjs';
10
- import { VovkCLIWatcher } from './watcher/index.mjs';
11
- import { Init } from './init/index.mjs';
10
+ import { VovkDev } from './dev/index.mjs';
12
11
  import newComponents from './new/index.mjs';
13
12
  import 'dotenv/config';
13
+ import initProgram from './initProgram.mjs';
14
14
  const program = new Command();
15
15
  const packageJSON = JSON.parse(readFileSync(path.join(import.meta.dirname, '../package.json'), 'utf-8'));
16
16
  program.name('vovk').description('Vovk CLI').version(packageJSON.version);
17
+ initProgram(program, 'init ');
17
18
  program
18
19
  .command('dev')
19
20
  .description('Start schema watcher (optional flag --next-dev to start it with Next.js)')
@@ -36,8 +37,8 @@ program
36
37
  if (options.nextDev) {
37
38
  const { result } = concurrently([
38
39
  {
39
- command: `node ${import.meta.dirname}/watcher/index.mjs`,
40
- name: 'Vovk.ts Schema Watcher',
40
+ command: `node ${import.meta.dirname}/dev/index.mjs`,
41
+ name: 'Vovk Dev Command',
41
42
  env: Object.assign({ PORT, __VOVK_START_WATCHER_IN_STANDALONE_MODE__: 'true' }, options.clientOut ? { VOVK_CLIENT_OUT_DIR: options.clientOut } : {}),
42
43
  },
43
44
  {
@@ -57,7 +58,7 @@ program
57
58
  }
58
59
  }
59
60
  else {
60
- void new VovkCLIWatcher().start({ clientOutDir: options.clientOut });
61
+ void new VovkDev().start({ clientOutDir: options.clientOut });
61
62
  }
62
63
  });
63
64
  program
@@ -72,46 +73,20 @@ program
72
73
  const schema = (await import(path.join(schemaOutAbsolutePath, 'index.js')));
73
74
  await generateClient(projectInfo, segments, schema.default);
74
75
  });
75
- // reused at vovk-init
76
- export function initProgram(p, command) {
77
- return p
78
- .command(command + '[prefix]')
79
- .description('Initialize Vovk project')
80
- .option('-Y, --yes', 'Skip all prompts and use default values')
81
- .option('--log-level <level>', 'Set log level', 'info')
82
- .option('--use-npm', 'Use npm as package manager')
83
- .option('--use-yarn', 'Use yarn as package manager')
84
- .option('--use-pnpm', 'Use pnpm as package manager')
85
- .option('--use-bun', 'Use bun as package manager')
86
- .option('--skip-install', 'Skip installing dependencies')
87
- .option('--update-ts-config', 'Update tsconfig.json')
88
- .option('--update-scripts <mode>', 'Update package.json scripts (implicit or explicit)')
89
- .option('--validation-library <library>', 'Validation library to use ("vovk-zod", "vovk-yup", "vovk-dto" or another). Set to "none" to skip validation')
90
- .option('--validate-on-client', 'Validate on client')
91
- .option('--dry-run', 'Do not write files to disk')
92
- .option('--channel <channel>', 'Channel to use for fetching packages', 'latest')
93
- .action((prefix = '.', options) => new Init().main(prefix, options));
94
- }
95
- initProgram(program, 'init ');
96
76
  program
97
77
  .command('new [components...]')
98
78
  .alias('n')
99
79
  .description('Create new components. "vovk new [...components] [segmentName/]moduleName" to create a new module or "vovk new segment [segmentName]" to create a new segment')
80
+ .option('-o, --overwrite', 'Overwrite existing files')
81
+ .option('--template, --templates <templates...>', 'Override config template. Accepts an array of strings that correspond the order of the components')
82
+ .option('--dir <dirname>', 'Override dirName in template file. Relative to the root of the project')
83
+ .option('--no-segment-update', 'Do not update segment files when creating a new module')
100
84
  .option('--dry-run', 'Do not write files to disk')
101
85
  .action((components, options) => newComponents(components, options));
102
86
  program
103
87
  .command('help')
104
88
  .description('Show help message')
105
89
  .action(() => program.help());
106
- /*
107
- TODO
108
- vovk new segment [segmentName]
109
- vovk new controller service [segmentName/]moduleName
110
- vovk new c s w [segmentName/]moduleName
111
-
112
- vovk c s w userApi/user
113
- vovk new c s w user
114
- */
115
90
  program.parse(process.argv);
116
91
  if (!process.argv.slice(2).length) {
117
92
  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;
@@ -22,7 +22,7 @@ export default async function createConfig({ root, log, dryRun, options: { valid
22
22
  config.validateOnClient = `${validationLibrary}/validateOnClient`;
23
23
  }
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) {
@@ -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;
@@ -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;