vovk-cli 0.0.1-draft.165 → 0.0.1-draft.166

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 (63) hide show
  1. package/client-templates/cjs/index.cjs.ejs +4 -4
  2. package/client-templates/cjs/index.d.cts.ejs +3 -3
  3. package/client-templates/mjs/index.d.mts.ejs +3 -3
  4. package/client-templates/mjs/index.mjs.ejs +4 -4
  5. package/client-templates/readme/README.md.ejs +1 -1
  6. package/client-templates/{fullSchemaCjs/fullSchema.cjs.ejs → schemaCjs/schema.cjs.ejs} +4 -4
  7. package/client-templates/schemaCjs/schema.d.cts.ejs +10 -0
  8. package/client-templates/schemaJson/schema.json.ejs +1 -0
  9. package/client-templates/{fullSchemaTs/fullSchema.ts.ejs → schemaTs/schema.ts.ejs} +4 -4
  10. package/client-templates/ts/index.ts.ejs +5 -5
  11. package/dist/bundle/index.d.mts +5 -3
  12. package/dist/bundle/index.mjs +28 -16
  13. package/dist/dev/ensureSchemaFiles.mjs +2 -2
  14. package/dist/dev/index.mjs +32 -27
  15. package/dist/generate/ensureClient.d.mts +2 -1
  16. package/dist/generate/ensureClient.mjs +8 -7
  17. package/dist/generate/getClientTemplateFiles.d.mts +1 -1
  18. package/dist/generate/getClientTemplateFiles.mjs +13 -16
  19. package/dist/generate/getFullSchemaFromJSON.d.mts +2 -2
  20. package/dist/generate/getFullSchemaFromJSON.mjs +3 -4
  21. package/dist/generate/getTemplateClientImports.d.mts +4 -5
  22. package/dist/generate/getTemplateClientImports.mjs +3 -3
  23. package/dist/generate/index.d.mts +5 -3
  24. package/dist/generate/index.mjs +41 -40
  25. package/dist/generate/mergePackages.mjs +4 -4
  26. package/dist/generate/writeOneClientFile.d.mts +5 -3
  27. package/dist/generate/writeOneClientFile.mjs +12 -8
  28. package/dist/getProjectInfo/{getRelativeSrcRoot.d.mts → getConfig/getRelativeSrcRoot.d.mts} +1 -1
  29. package/dist/getProjectInfo/{getRelativeSrcRoot.mjs → getConfig/getRelativeSrcRoot.mjs} +2 -2
  30. package/dist/getProjectInfo/getConfig/getTemplateDefs.d.mts +3 -3
  31. package/dist/getProjectInfo/getConfig/getTemplateDefs.mjs +17 -19
  32. package/dist/getProjectInfo/{getUserConfig.mjs → getConfig/getUserConfig.mjs} +1 -1
  33. package/dist/getProjectInfo/getConfig/index.d.mts +4 -7
  34. package/dist/getProjectInfo/getConfig/index.mjs +28 -65
  35. package/dist/getProjectInfo/index.d.mts +4 -6
  36. package/dist/getProjectInfo/index.mjs +7 -12
  37. package/dist/index.mjs +20 -13
  38. package/dist/init/index.mjs +1 -1
  39. package/dist/new/newModule.mjs +3 -1
  40. package/dist/new/newSegment.mjs +7 -5
  41. package/dist/types.d.mts +7 -8
  42. package/dist/utils/formatLoggedSegmentName.d.mts +2 -1
  43. package/dist/utils/formatLoggedSegmentName.mjs +2 -2
  44. package/dist/utils/getPublicModuleNameFromPath.d.mts +4 -0
  45. package/dist/utils/getPublicModuleNameFromPath.mjs +9 -0
  46. package/dist/utils/pickSegmentFullSchema.d.mts +3 -3
  47. package/dist/utils/pickSegmentFullSchema.mjs +9 -11
  48. package/dist/utils/resolveAbsoluteModulePath.d.mts +1 -0
  49. package/dist/utils/resolveAbsoluteModulePath.mjs +17 -5
  50. package/package.json +1 -1
  51. package/client-templates/fullSchemaCjs/fullSchema.d.cts.ejs +0 -10
  52. package/client-templates/fullSchemaJson/full-schema.json.ejs +0 -1
  53. package/dist/dev/isSegmentSchemaEmpty.d.mts +0 -2
  54. package/dist/dev/isSegmentSchemaEmpty.mjs +0 -4
  55. package/dist/enums.d.mts +0 -5
  56. package/dist/enums.mjs +0 -6
  57. /package/dist/getProjectInfo/{getConfigAbsolutePaths.d.mts → getConfig/getConfigAbsolutePaths.d.mts} +0 -0
  58. /package/dist/getProjectInfo/{getConfigAbsolutePaths.mjs → getConfig/getConfigAbsolutePaths.mjs} +0 -0
  59. /package/dist/getProjectInfo/{getUserConfig.d.mts → getConfig/getUserConfig.d.mts} +0 -0
  60. /package/dist/getProjectInfo/{importUncachedModule.d.mts → getConfig/importUncachedModule.d.mts} +0 -0
  61. /package/dist/getProjectInfo/{importUncachedModule.mjs → getConfig/importUncachedModule.mjs} +0 -0
  62. /package/dist/getProjectInfo/{importUncachedModuleWorker.d.mts → getConfig/importUncachedModuleWorker.d.mts} +0 -0
  63. /package/dist/getProjectInfo/{importUncachedModuleWorker.mjs → getConfig/importUncachedModuleWorker.mjs} +0 -0
@@ -1,14 +1,14 @@
1
1
  <%- `// auto-generated ${new Date().toISOString()}\n/* eslint-disable */` %>
2
2
  const { fetcher } = require('<%= t.imports.fetcher %>');
3
3
  const { createRPC } = require('<%= t.imports.createRPC %>');
4
- const { fullSchema } = require('./fullSchema.cjs');
4
+ const { schema } = require('./schema.cjs');
5
5
  const { validateOnClient = null } = <%- t.imports.validateOnClient ? `require('${t.imports.validateOnClient}')` : '{}'%>;
6
- <% Object.values(t.fullSchema.segments).filter((segment) => segment.emitSchema).forEach((segment) => {
6
+ <% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment) => {
7
7
  Object.keys(segment.controllers).forEach((rpcModuleName) => { %>
8
8
  exports.<%= rpcModuleName %> = createRPC(
9
- fullSchema, '<%= segment.segmentName %>', '<%= rpcModuleName %>',
9
+ schema, '<%= segment.segmentName %>', '<%= rpcModuleName %>',
10
10
  { fetcher, validateOnClient, defaultOptions: { apiRoot: '<%= segment.segmentApiRoot ?? t.apiRoot %>' } }
11
11
  );
12
12
  <% })
13
13
  }) %>
14
- exports.fullSchema = fullSchema;
14
+ exports.schema = schema;
@@ -3,13 +3,13 @@
3
3
  import type { VovkClientFetcher } from 'vovk';
4
4
  import type { fetcher } from '<%= t.imports.module.fetcher %>';
5
5
  import type { createRPC } from '<%= t.imports.module.createRPC %>';
6
- <% Object.values(t.fullSchema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
6
+ <% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
7
7
  import type { Controllers as Controllers<%= i %> } from "<%= t.segmentMeta[segment.segmentName].segmentImportPath %>";
8
8
  <% }) %>
9
9
  type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
10
- <% Object.values(t.fullSchema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => {
10
+ <% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => {
11
11
  Object.keys(segment.controllers).forEach((rpcModuleName) => { %>
12
12
  export const <%= rpcModuleName %>: ReturnType<typeof createRPC<Controllers<%= i %>["<%= rpcModuleName %>"], Options>>;
13
13
  <% })
14
14
  }) %>
15
- export { fullSchema } from './fullSchema.cjs';
15
+ export { schema } from './schema.cjs';
@@ -3,13 +3,13 @@
3
3
  import type { VovkClientFetcher } from 'vovk';
4
4
  import type { fetcher } from '<%= t.imports.module.fetcher %>';
5
5
  import type { createRPC } from '<%= t.imports.module.createRPC %>';
6
- <% Object.values(t.fullSchema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
6
+ <% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
7
7
  import type { Controllers as Controllers<%= i %> } from "<%= t.segmentMeta[segment.segmentName].segmentImportPath %>";
8
8
  <% }) %>
9
9
  type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
10
- <% Object.values(t.fullSchema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => {
10
+ <% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => {
11
11
  Object.keys(segment.controllers).forEach((rpcModuleName) => { %>
12
12
  export const <%= rpcModuleName %>: ReturnType<typeof createRPC<Controllers<%= i %>["<%= rpcModuleName %>"], Options>>;
13
13
  <% })
14
14
  }) %>
15
- export { fullSchema } from './fullSchema.cjs';
15
+ export { schema } from './schema.cjs';
@@ -1,20 +1,20 @@
1
1
  <%- `// auto-generated ${new Date().toISOString()}\n/* eslint-disable */` %>
2
2
  import { fetcher } from '<%= t.imports.module.fetcher %>';
3
3
  import { createRPC } from '<%= t.imports.module.createRPC %>';
4
- import { fullSchema } from './fullSchema.cjs';
4
+ import { schema } from './schema.cjs';
5
5
  <% if (t.imports.module.validateOnClient) { %>
6
6
  import { validateOnClient } from '<%= t.imports.module.validateOnClient %>';
7
7
  <% } else { %>
8
8
  const validateOnClient = undefined;
9
9
  <% } %>
10
- <% Object.values(t.fullSchema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => {
10
+ <% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => {
11
11
  Object.keys(segment.controllers).forEach((rpcModuleName) => { %>
12
12
  export const <%= rpcModuleName %> = createRPC(
13
- fullSchema, '<%= segment.segmentName %>', '<%= rpcModuleName %>',
13
+ schema, '<%= segment.segmentName %>', '<%= rpcModuleName %>',
14
14
  { fetcher, validateOnClient, defaultOptions: { apiRoot: '<%= segment.segmentApiRoot ?? t.apiRoot %>' } }
15
15
  );
16
16
  <%
17
17
  });
18
18
  });
19
19
  %>
20
- export { fullSchema };
20
+ export { schema };
@@ -16,7 +16,7 @@ imports: ['vovk-openapi']
16
16
  npm install <%= t.package.name.replace(/-/g, '_') %>
17
17
  ```
18
18
 
19
- <% Object.entries(t.fullSchema.segments).forEach(([segmentName, segment]) => {
19
+ <% Object.entries(t.schema.segments).forEach(([segmentName, segment]) => {
20
20
  Object.values(segment.controllers).forEach((controllerSchema) => { %>
21
21
 
22
22
  ## <%= controllerSchema.rpcModuleName %>
@@ -1,12 +1,12 @@
1
1
  <%- `// auto-generated ${new Date().toISOString()}\n/* eslint-disable */` %>
2
2
  const config = require('./<%= t.schemaOutDir %>/config.json');
3
- const segments = {<% Object.values(t.fullSchema.segments).filter((segment) => segment.emitSchema).forEach((segment) => { %>
3
+ const segments = {<% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment) => { %>
4
4
  '<%= segment.segmentName %>': require('./<%= t.schemaOutDir %>/<%= t.SEGMENTS_SCHEMA_DIR_NAME %>/<%= segment.segmentName || t.ROOT_SEGMENT_SCHEMA_NAME %>.json'),<% }) %>
5
5
  };
6
- const fullSchema = {
7
- $schema: '<%- t.SchemaIdEnum.FULL %>',
6
+ const schema = {
7
+ $schema: '<%- t.VovkSchemaIdEnum.FULL %>',
8
8
  config,
9
9
  segments,
10
10
  };
11
11
 
12
- module.exports.fullSchema = fullSchema;
12
+ module.exports.schema = schema;
@@ -0,0 +1,10 @@
1
+ <%- `// auto-generated ${new Date().toISOString()}\n/* eslint-disable */` %>
2
+ import type { VovkStrictConfig, VovkSegmentSchema, VovkSchemaIdEnum } from 'vovk';
3
+
4
+ export const schema: {
5
+ $schema: '<%- t.VovkSchemaIdEnum.FULL %>';
6
+ config: Partial<VovkStrictConfig>;
7
+ segments: {<% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment) => { %>
8
+ '<%= segment.segmentName %>': VovkSegmentSchema;<% }) %>
9
+ };
10
+ };
@@ -0,0 +1 @@
1
+ <%- JSON.stringify(t.schema, null, 2) %>
@@ -1,14 +1,14 @@
1
1
  <%- `// auto-generated ${new Date().toISOString()}\n/* eslint-disable */` %>
2
2
  import config from './<%= t.schemaOutDir %>/config.json' with { type: "json" };
3
- <% Object.values(t.fullSchema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
3
+ <% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
4
4
  import segment<%= i %> from './<%= t.schemaOutDir %>/<%= t.SEGMENTS_SCHEMA_DIR_NAME %>/<%= segment.segmentName || t.ROOT_SEGMENT_SCHEMA_NAME %>.json' with { type: "json" };
5
5
  <% }) %>
6
- const segments = {<% Object.values(t.fullSchema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
6
+ const segments = {<% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
7
7
  '<%= segment.segmentName %>': segment<%= i %>,<% }) %>
8
8
  };
9
9
 
10
- export const fullSchema = {
11
- $schema: '<%- t.SchemaIdEnum.FULL %>',
10
+ export const schema = {
11
+ $schema: '<%- t.VovkSchemaIdEnum.FULL %>',
12
12
  config,
13
13
  segments,
14
14
  };
@@ -2,8 +2,8 @@
2
2
  import type { VovkClientFetcher } from 'vovk';
3
3
  import { fetcher } from '<%= t.imports.fetcher %>';
4
4
  import { createRPC } from '<%= t.imports.createRPC %>';
5
- import { fullSchema } from './fullSchema.ts';
6
- <% Object.values(t.fullSchema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
5
+ import { schema } from './schema.ts';
6
+ <% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
7
7
  import type { Controllers as Controllers<%= i %> } from "<%= t.segmentMeta[segment.segmentName].segmentImportPath %>";
8
8
  <% }) %>
9
9
  <% if (t.imports.validateOnClient) { %>
@@ -12,12 +12,12 @@ import { validateOnClient } from '<%= t.imports.validateOnClient %>';
12
12
  const validateOnClient = undefined;
13
13
  <% } %>
14
14
  type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
15
- <% Object.values(t.fullSchema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
15
+ <% Object.values(t.schema.segments).filter((segment) => segment.emitSchema).forEach((segment, i) => { %>
16
16
  <% Object.keys(segment.controllers).forEach((rpcModuleName) => { %>
17
17
  export const <%= rpcModuleName %> = createRPC<Controllers<%= i %>["<%= rpcModuleName %>"], Options>(
18
- fullSchema, '<%= segment.segmentName %>', '<%= rpcModuleName %>',
18
+ schema, '<%= segment.segmentName %>', '<%= rpcModuleName %>',
19
19
  { fetcher, validateOnClient, defaultOptions: { apiRoot: '<%= segment.segmentApiRoot ?? t.apiRoot %>' } }
20
20
  );
21
21
  <% }) %>
22
22
  <% }) %>
23
- export { fullSchema };
23
+ export { schema };
@@ -1,6 +1,8 @@
1
- import type { VovkFullSchema } from 'vovk';
1
+ import type { VovkSchema } from 'vovk';
2
2
  import type { ProjectInfo } from '../getProjectInfo/index.mjs';
3
- export default function bundle({ projectInfo, fullSchema, }: {
3
+ import type { BundleOptions } from '../types.mjs';
4
+ export default function bundle({ projectInfo, fullSchema, cliBundleOptions, }: {
4
5
  projectInfo: ProjectInfo;
5
- fullSchema: VovkFullSchema;
6
+ fullSchema: VovkSchema;
7
+ cliBundleOptions: BundleOptions;
6
8
  }): Promise<void>;
@@ -5,25 +5,36 @@ import groupBy from 'lodash/groupBy.js';
5
5
  import generate from '../generate/index.mjs';
6
6
  import { BuiltInTemplateName } from '../getProjectInfo/getConfig/getTemplateDefs.mjs';
7
7
  import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
8
- export default async function bundle({ projectInfo, fullSchema, }) {
9
- const { config, log } = projectInfo;
8
+ import locateSegments from '../locateSegments.mjs';
9
+ export default async function bundle({ projectInfo, fullSchema, cliBundleOptions, }) {
10
+ const { config, log, cwd, apiDir } = projectInfo;
11
+ const locatedSegments = await locateSegments({ dir: path.join(cwd, apiDir), config, log });
10
12
  const { bundle: bundleConfig } = config;
11
- const cwd = process.cwd();
12
13
  const tsFullClientOutAbsoluteDirInput = path.join(cwd, bundleConfig.tsClientOutDir);
14
+ const tsClientOutDir = cliBundleOptions?.tsClientOutDir ?? bundleConfig.tsClientOutDir;
15
+ const dontDeleteTsClientOutDirAfter = cliBundleOptions?.dontDeleteTsClientOutDirAfter ?? bundleConfig?.dontDeleteTsClientOutDirAfter ?? false;
16
+ const sourcemap = cliBundleOptions?.sourcemap ?? bundleConfig?.sourcemap;
17
+ if (!tsClientOutDir) {
18
+ throw new Error('No output directory specified for composed client');
19
+ }
20
+ const outDir = cliBundleOptions?.outDir ?? bundleConfig.outDir;
21
+ if (!outDir) {
22
+ throw new Error('No output directory specified for bundling');
23
+ }
13
24
  await generate({
14
25
  isEnsuringClient: false,
15
26
  projectInfo,
16
27
  forceNothingWrittenLog: true,
17
28
  fullSchema,
29
+ locatedSegments,
18
30
  cliGenerateOptions: {
19
- fullFrom: [BuiltInTemplateName.ts],
20
- fullOut: bundleConfig.tsClientOutDir,
31
+ composedFrom: [BuiltInTemplateName.ts],
32
+ composedOut: tsClientOutDir,
33
+ composedOnly: true,
34
+ composedIncludeSegments: cliBundleOptions.includeSegments ?? bundleConfig.includeSegments,
35
+ composedExcludeSegments: cliBundleOptions.excludeSegments ?? bundleConfig.excludeSegments,
21
36
  },
22
37
  });
23
- const outDir = bundleConfig.outDir;
24
- if (!outDir) {
25
- throw new Error('No output directory specified for bundling');
26
- }
27
38
  await build({
28
39
  entry: path.join(tsFullClientOutAbsoluteDirInput, './index.ts'),
29
40
  dts: true,
@@ -35,29 +46,30 @@ export default async function bundle({ projectInfo, fullSchema, }) {
35
46
  const outDirAbsolute = path.resolve(cwd, outDir);
36
47
  log.info(`Bundled index.ts to ${chalkHighlightThing(outDirAbsolute)}`);
37
48
  await build({
38
- entry: path.join(tsFullClientOutAbsoluteDirInput, './fullSchema.ts'),
49
+ entry: path.join(tsFullClientOutAbsoluteDirInput, './schema.ts'),
39
50
  dts: true,
40
51
  format: ['cjs'],
41
52
  fixedExtension: true,
42
53
  outDir,
43
54
  clean: false,
44
- sourcemap: bundleConfig.sourcemap,
55
+ sourcemap,
45
56
  });
46
- log.info(`Bundled fullSchema.ts to ${chalkHighlightThing(outDirAbsolute)}`);
47
- const requiresGroup = groupBy(Object.entries(bundleConfig.requires), ([, relativeOutDir]) => relativeOutDir);
57
+ log.info(`Bundled schema.ts to ${chalkHighlightThing(outDirAbsolute)}`);
58
+ const requiresGroup = groupBy(Object.entries(bundleConfig.requires), ([, relativePath]) => relativePath);
48
59
  for (const [relativePath, group] of Object.entries(requiresGroup)) {
49
60
  await generate({
50
61
  isEnsuringClient: false,
51
62
  projectInfo,
52
63
  forceNothingWrittenLog: true,
53
64
  fullSchema,
65
+ locatedSegments,
54
66
  cliGenerateOptions: {
55
- fullFrom: group.map(([templateName]) => templateName),
56
- fullOut: path.resolve(outDir, relativePath),
67
+ composedFrom: group.map(([templateName]) => templateName),
68
+ composedOut: path.resolve(outDir, relativePath),
57
69
  },
58
70
  });
59
71
  }
60
- if (!bundleConfig.dontDeleteTsClientOutDirAfter) {
72
+ if (!dontDeleteTsClientOutDirAfter) {
61
73
  await fs.rm(tsFullClientOutAbsoluteDirInput, { recursive: true, force: true });
62
74
  log.debug(`Deleted temporary TypeScript client output directory: ${chalkHighlightThing(tsFullClientOutAbsoluteDirInput)}`);
63
75
  }
@@ -1,10 +1,10 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import debounce from 'lodash/debounce.js';
4
+ import { VovkSchemaIdEnum } from 'vovk';
4
5
  import writeOneSchemaFile, { SEGMENTS_SCHEMA_DIR_NAME, ROOT_SEGMENT_SCHEMA_NAME, } from './writeOneSegmentSchemaFile.mjs';
5
6
  import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
6
7
  import writeConfigJson from './writeConfigJson.mjs';
7
- import { SchemaIdEnum } from '../enums.mjs';
8
8
  /**
9
9
  * Ensure that the schema files are created to avoid any import errors.
10
10
  */
@@ -19,7 +19,7 @@ export default async function ensureSchemaFiles(projectInfo, schemaOutAbsolutePa
19
19
  const { isCreated } = await writeOneSchemaFile({
20
20
  schemaOutAbsolutePath,
21
21
  segmentSchema: {
22
- $schema: SchemaIdEnum.SEGMENT,
22
+ $schema: VovkSchemaIdEnum.SEGMENT,
23
23
  emitSchema: false,
24
24
  segmentName,
25
25
  controllers: {},
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
+ import { VovkSchemaIdEnum } from 'vovk';
3
4
  import * as chokidar from 'chokidar';
4
5
  import { Agent, setGlobalDispatcher } from 'undici';
5
6
  import keyBy from 'lodash/keyBy.js';
@@ -15,13 +16,13 @@ import generate from '../generate/index.mjs';
15
16
  import locateSegments from '../locateSegments.mjs';
16
17
  import debounceWithArgs from '../utils/debounceWithArgs.mjs';
17
18
  import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
18
- import isSegmentSchemaEmpty from './isSegmentSchemaEmpty.mjs';
19
19
  import writeConfigJson from './writeConfigJson.mjs';
20
- import { SchemaIdEnum } from '../enums.mjs';
20
+ import { isEmpty } from 'lodash';
21
21
  export class VovkDev {
22
22
  #projectInfo;
23
+ #segments = [];
23
24
  #fullSchema = {
24
- $schema: SchemaIdEnum.FULL,
25
+ $schema: VovkSchemaIdEnum.SCHEMA,
25
26
  segments: {},
26
27
  config: {},
27
28
  };
@@ -45,18 +46,18 @@ export class VovkDev {
45
46
  log.debug(`File ${filePath} has been added to segments folder`);
46
47
  if (segmentReg.test(filePath)) {
47
48
  const segmentName = getSegmentName(filePath);
48
- this.#projectInfo.segments = this.#projectInfo.segments.find((s) => s.segmentName === segmentName)
49
- ? this.#projectInfo.segments
49
+ this.#segments = this.#segments.find((s) => s.segmentName === segmentName)
50
+ ? this.#segments
50
51
  : [
51
- ...this.#projectInfo.segments,
52
+ ...this.#segments,
52
53
  {
53
54
  routeFilePath: filePath,
54
55
  segmentName,
55
56
  },
56
57
  ];
57
58
  log.info(`${capitalize(formatLoggedSegmentName(segmentName))} has been added`);
58
- log.debug(`Full list of segments: ${this.#projectInfo.segments.map((s) => s.segmentName).join(', ')}`);
59
- void debouncedEnsureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, this.#projectInfo.segments.map((s) => s.segmentName));
59
+ log.debug(`Full list of segments: ${this.#segments.map((s) => s.segmentName).join(', ')}`);
60
+ void debouncedEnsureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, this.#segments.map((s) => s.segmentName));
60
61
  }
61
62
  })
62
63
  .on('change', (filePath) => {
@@ -67,23 +68,23 @@ export class VovkDev {
67
68
  })
68
69
  .on('addDir', async (dirPath) => {
69
70
  log.debug(`Directory ${dirPath} has been added to segments folder`);
70
- this.#projectInfo.segments = await locateSegments({
71
+ this.#segments = await locateSegments({
71
72
  dir: apiDirAbsolutePath,
72
73
  config,
73
74
  log: this.#projectInfo.log,
74
75
  });
75
- for (const { segmentName } of this.#projectInfo.segments) {
76
+ for (const { segmentName } of this.#segments) {
76
77
  void this.#requestSchema(segmentName);
77
78
  }
78
79
  })
79
80
  .on('unlinkDir', async (dirPath) => {
80
81
  log.debug(`Directory ${dirPath} has been removed from segments folder`);
81
- this.#projectInfo.segments = await locateSegments({
82
+ this.#segments = await locateSegments({
82
83
  dir: apiDirAbsolutePath,
83
84
  config,
84
85
  log: this.#projectInfo.log,
85
86
  });
86
- for (const { segmentName } of this.#projectInfo.segments) {
87
+ for (const { segmentName } of this.#segments) {
87
88
  void this.#requestSchema(segmentName);
88
89
  }
89
90
  })
@@ -91,10 +92,10 @@ export class VovkDev {
91
92
  log.debug(`File ${filePath} has been removed from segments folder`);
92
93
  if (segmentReg.test(filePath)) {
93
94
  const segmentName = getSegmentName(filePath);
94
- this.#projectInfo.segments = this.#projectInfo.segments.filter((s) => s.segmentName !== segmentName);
95
+ this.#segments = this.#segments.filter((s) => s.segmentName !== segmentName);
95
96
  log.info(`${formatLoggedSegmentName(segmentName, { upperFirst: true })} has been removed`);
96
- log.debug(`Full list of segments: ${this.#projectInfo.segments.map((s) => s.segmentName).join(', ')}`);
97
- void debouncedEnsureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, this.#projectInfo.segments.map((s) => s.segmentName));
97
+ log.debug(`Full list of segments: ${this.#segments.map((s) => s.segmentName).join(', ')}`);
98
+ void debouncedEnsureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, this.#segments.map((s) => s.segmentName));
98
99
  }
99
100
  })
100
101
  .on('ready', () => {
@@ -127,12 +128,12 @@ export class VovkDev {
127
128
  log.debug(`File ${filePath} has been removed from modules folder`);
128
129
  })
129
130
  .on('addDir', () => {
130
- for (const { segmentName } of this.#projectInfo.segments) {
131
+ for (const { segmentName } of this.#segments) {
131
132
  void this.#requestSchema(segmentName);
132
133
  }
133
134
  })
134
135
  .on('unlinkDir', () => {
135
- for (const { segmentName } of this.#projectInfo.segments) {
136
+ for (const { segmentName } of this.#segments) {
136
137
  void this.#requestSchema(segmentName);
137
138
  }
138
139
  })
@@ -151,6 +152,8 @@ export class VovkDev {
151
152
  let isReady = false;
152
153
  const handle = debounce(async () => {
153
154
  this.#projectInfo = await getProjectInfo();
155
+ const { config, apiDir } = this.#projectInfo;
156
+ this.#segments = await locateSegments({ dir: path.join(cwd, apiDir), config, log });
154
157
  await this.#modulesWatcher?.close();
155
158
  await this.#segmentWatcher?.close();
156
159
  await Promise.all([
@@ -200,7 +203,7 @@ export class VovkDev {
200
203
  if (this.#isWatching)
201
204
  throw new Error('Already watching');
202
205
  const { log } = this.#projectInfo;
203
- log.debug(`Starting segments and modules watcher. Detected initial segments: ${JSON.stringify(this.#projectInfo.segments.map((s) => s.segmentName))}.`);
206
+ log.debug(`Starting segments and modules watcher. Detected initial segments: ${JSON.stringify(this.#segments.map((s) => s.segmentName))}.`);
204
207
  // automatically watches segments and modules
205
208
  this.#watchConfig(callback);
206
209
  }
@@ -215,7 +218,7 @@ export class VovkDev {
215
218
  const namesOfClasses = [...code.matchAll(nameOfClasReg)].map((match) => match[1]);
216
219
  const importRegex = /import\s*{[^}]*\b(get|post|put|del|head|options)\b[^}]*}\s*from\s*['"]vovk['"]/;
217
220
  if (importRegex.test(code) && namesOfClasses.length) {
218
- const affectedSegments = this.#projectInfo.segments.filter((s) => {
221
+ const affectedSegments = this.#segments.filter((s) => {
219
222
  const segmentSchema = this.#fullSchema.segments[s.segmentName];
220
223
  if (!segmentSchema)
221
224
  return false;
@@ -265,7 +268,7 @@ export class VovkDev {
265
268
  }
266
269
  return { isError: false };
267
270
  }, 500);
268
- #generate = debounce(() => generate({ projectInfo: this.#projectInfo, fullSchema: this.#fullSchema }).then(this.#onFirstTimeGenerate), 1000);
271
+ #generate = debounce(() => generate({ projectInfo: this.#projectInfo, fullSchema: this.#fullSchema, locatedSegments: this.#segments }).then(this.#onFirstTimeGenerate), 1000);
269
272
  async #handleSegmentSchema(segmentName, segmentSchema) {
270
273
  const { log, config, cwd } = this.#projectInfo;
271
274
  if (!segmentSchema) {
@@ -274,7 +277,7 @@ export class VovkDev {
274
277
  }
275
278
  log.debug(`Handling received schema from ${formatLoggedSegmentName(segmentName)}`);
276
279
  const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
277
- const segment = this.#projectInfo.segments.find((s) => s.segmentName === segmentName);
280
+ const segment = this.#segments.find((s) => s.segmentName === segmentName);
278
281
  if (!segment) {
279
282
  log.warn(`${formatLoggedSegmentName(segmentName)} not found`);
280
283
  return;
@@ -293,10 +296,10 @@ export class VovkDev {
293
296
  log.info(`Schema for ${formatLoggedSegmentName(segment.segmentName)} has been updated in ${timeTook}ms`);
294
297
  }
295
298
  }
296
- else if (segmentSchema && !isSegmentSchemaEmpty(segmentSchema)) {
299
+ else if (segmentSchema && !isEmpty(segmentSchema.controllers)) {
297
300
  log.error(`Non-empty schema provided for ${formatLoggedSegmentName(segment.segmentName)} but "emitSchema" is false`);
298
301
  }
299
- if (this.#projectInfo.segments.every((s) => this.#fullSchema.segments[s.segmentName])) {
302
+ if (this.#segments.every((s) => this.#fullSchema.segments[s.segmentName])) {
300
303
  log.debug(`All segments with "emitSchema" have schema.`);
301
304
  this.#generate();
302
305
  }
@@ -304,7 +307,8 @@ export class VovkDev {
304
307
  async start({ exit }) {
305
308
  const now = Date.now();
306
309
  this.#projectInfo = await getProjectInfo();
307
- const { log, config, cwd } = this.#projectInfo;
310
+ const { log, config, cwd, apiDir } = this.#projectInfo;
311
+ this.#segments = await locateSegments({ dir: path.join(cwd, apiDir), config, log });
308
312
  log.info('Starting...');
309
313
  if (exit) {
310
314
  this.#onFirstTimeGenerate = once(() => {
@@ -326,13 +330,14 @@ export class VovkDev {
326
330
  log.error(`Unhandled Rejection: ${String(reason)}`);
327
331
  });
328
332
  const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
329
- await ensureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, this.#projectInfo.segments.map((s) => s.segmentName));
330
- await ensureClient(this.#projectInfo);
333
+ const segmentNames = this.#segments.map((s) => s.segmentName);
334
+ await ensureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, segmentNames);
335
+ await ensureClient(this.#projectInfo, this.#segments);
331
336
  const MAX_ATTEMPTS = 5;
332
337
  const DELAY = 5000;
333
338
  // Request schema every segment in 5 seconds in order to update schema on start
334
339
  setTimeout(() => {
335
- for (const { segmentName } of this.#projectInfo.segments) {
340
+ for (const { segmentName } of this.#segments) {
336
341
  let attempts = 0;
337
342
  void this.#requestSchema(segmentName).then(({ isError }) => {
338
343
  if (isError) {
@@ -1,2 +1,3 @@
1
1
  import type { ProjectInfo } from '../getProjectInfo/index.mjs';
2
- export default function ensureClient(projectInfo: ProjectInfo): Promise<void>;
2
+ import type { Segment } from '../locateSegments.mjs';
3
+ export default function ensureClient(projectInfo: ProjectInfo, locatedSegments: Segment[]): Promise<void>;
@@ -1,10 +1,10 @@
1
- import { SchemaIdEnum } from '../enums.mjs';
1
+ import { VovkSchemaIdEnum } from 'vovk';
2
2
  import generate from './index.mjs';
3
- const getEmptySegmentRecordSchema = (segments) => {
3
+ const getEmptySegmentRecordSchema = (segmentNames) => {
4
4
  const result = {};
5
- for (const { segmentName } of segments) {
5
+ for (const segmentName of segmentNames) {
6
6
  result[segmentName] = {
7
- $schema: SchemaIdEnum.SEGMENT,
7
+ $schema: VovkSchemaIdEnum.SEGMENT,
8
8
  segmentName,
9
9
  emitSchema: false,
10
10
  controllers: {},
@@ -12,14 +12,15 @@ const getEmptySegmentRecordSchema = (segments) => {
12
12
  }
13
13
  return result;
14
14
  };
15
- export default async function ensureClient(projectInfo) {
15
+ export default async function ensureClient(projectInfo, locatedSegments) {
16
16
  return generate({
17
17
  isEnsuringClient: true,
18
18
  projectInfo,
19
19
  fullSchema: {
20
- $schema: SchemaIdEnum.FULL,
20
+ $schema: VovkSchemaIdEnum.SCHEMA,
21
21
  config: {},
22
- segments: getEmptySegmentRecordSchema(projectInfo.segments),
22
+ segments: getEmptySegmentRecordSchema(locatedSegments.map(({ segmentName }) => segmentName)),
23
23
  },
24
+ locatedSegments,
24
25
  });
25
26
  }
@@ -12,7 +12,7 @@ export default function getClientTemplateFiles({ config, cwd, log, configKey, cl
12
12
  config: VovkStrictConfig;
13
13
  cwd: string;
14
14
  log: ProjectInfo['log'];
15
- configKey: 'fullClient' | 'segmentedClient';
15
+ configKey: 'composedClient' | 'segmentedClient';
16
16
  cliGenerateOptions?: GenerateOptions;
17
17
  }): Promise<{
18
18
  fromTemplates: string[];
@@ -2,18 +2,18 @@ import path from 'node:path';
2
2
  import { glob } from 'glob';
3
3
  import resolveAbsoluteModulePath from '../utils/resolveAbsoluteModulePath.mjs';
4
4
  import getFileSystemEntryType, { FileSystemEntryType } from '../utils/getFileSystemEntryType.mjs';
5
+ import getPublicModuleNameFromPath from '../utils/getPublicModuleNameFromPath.mjs';
5
6
  export default async function getClientTemplateFiles({ config, cwd, log, configKey, cliGenerateOptions, }) {
6
7
  const usedTemplateDefs = {};
7
- const fromTemplates = configKey === 'fullClient'
8
- ? cliGenerateOptions?.fullFrom || cliGenerateOptions?.segmentedFrom
9
- ? (cliGenerateOptions?.fullFrom ?? [])
10
- : config.fullClient.fromTemplates
11
- : cliGenerateOptions?.fullFrom || cliGenerateOptions?.segmentedFrom
8
+ const fromTemplates = configKey === 'composedClient'
9
+ ? cliGenerateOptions?.composedFrom || cliGenerateOptions?.segmentedFrom
10
+ ? (cliGenerateOptions?.composedFrom ?? [])
11
+ : config.composedClient.fromTemplates
12
+ : cliGenerateOptions?.composedFrom || cliGenerateOptions?.segmentedFrom
12
13
  ? (cliGenerateOptions?.segmentedFrom ?? [])
13
14
  : config.segmentedClient.fromTemplates;
14
- const outDir = configKey === 'fullClient'
15
- ? (cliGenerateOptions?.fullOut ?? config.fullClient.outDir)
16
- : (cliGenerateOptions?.segmentedOut ?? config.segmentedClient.outDir);
15
+ const cliOutDir = configKey === 'composedClient' ? cliGenerateOptions?.composedOut : cliGenerateOptions?.segmentedOut;
16
+ const configOutDir = configKey === 'composedClient' ? config.composedClient.outDir : config.segmentedClient.outDir;
17
17
  for (const templateName of fromTemplates) {
18
18
  if (!(templateName in config.clientTemplateDefs)) {
19
19
  throw new Error(`Unknown template name: ${templateName}`);
@@ -29,16 +29,13 @@ export default async function getClientTemplateFiles({ config, cwd, log, configK
29
29
  : null;
30
30
  const entryType = templateAbsolutePath ? await getFileSystemEntryType(templateAbsolutePath) : null;
31
31
  if (templateAbsolutePath && !entryType) {
32
- if (templateDef.templatePath &&
33
- !templateDef.templatePath.startsWith('.') &&
34
- !templateDef.templatePath.startsWith('/')) {
35
- const pathParts = templateDef.templatePath.split('/');
36
- const npmModuleName = pathParts[0].startsWith('@') ? `${pathParts[0]}/${pathParts[1]}` : pathParts[0];
37
- throw new Error(`Unable to locate template path "${templateDef.templatePath}" resolved as "${templateAbsolutePath}". You may need to install the package "${npmModuleName}" first.`);
32
+ const { moduleName } = templateDef.templatePath ? getPublicModuleNameFromPath(templateDef.templatePath) : {};
33
+ if (moduleName) {
34
+ throw new Error(`Unable to locate template path "${templateDef.templatePath}" resolved as "${templateAbsolutePath}". You may need to install the package "${moduleName}" first.`);
38
35
  }
39
36
  throw new Error(`Unable to locate template path "${templateDef.templatePath}" resolved as "${templateAbsolutePath}"`);
40
37
  }
41
- const defOutDir = configKey === 'fullClient' ? templateDef.fullClient?.outDir : templateDef.segmentedClient?.outDir;
38
+ const defOutDir = configKey === 'composedClient' ? templateDef.composedClient?.outDir : templateDef.segmentedClient?.outDir;
42
39
  let files = [];
43
40
  if (templateAbsolutePath) {
44
41
  if (entryType === FileSystemEntryType.FILE) {
@@ -55,7 +52,7 @@ export default async function getClientTemplateFiles({ config, cwd, log, configK
55
52
  log.error(`Template "${templateAbsolutePath}" not found`);
56
53
  continue;
57
54
  }
58
- const outCwdRelativeDir = forceOutCwdRelativeDir ?? defOutDir ?? outDir;
55
+ const outCwdRelativeDir = forceOutCwdRelativeDir ?? cliOutDir ?? defOutDir ?? configOutDir;
59
56
  for (const { filePath, isSingleFileTemplate } of files) {
60
57
  templateFiles.push({
61
58
  templateName,
@@ -1,3 +1,3 @@
1
- import type { VovkFullSchema } from 'vovk';
1
+ import { type VovkSchema } from 'vovk';
2
2
  import type { ProjectInfo } from '../getProjectInfo/index.mjs';
3
- export declare function getFullSchemaFromJSON(schemaOutAbsolutePath: string, projectInfo: ProjectInfo): Promise<VovkFullSchema>;
3
+ export declare function getFullSchemaFromJSON(schemaOutAbsolutePath: string, log: ProjectInfo['log']): Promise<VovkSchema>;
@@ -1,15 +1,14 @@
1
1
  import { readFile, access } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { glob } from 'glob';
4
+ import { VovkSchemaIdEnum } from 'vovk';
4
5
  import { ROOT_SEGMENT_SCHEMA_NAME } from '../dev/writeOneSegmentSchemaFile.mjs';
5
- import { SchemaIdEnum } from '../enums.mjs';
6
- export async function getFullSchemaFromJSON(schemaOutAbsolutePath, projectInfo) {
6
+ export async function getFullSchemaFromJSON(schemaOutAbsolutePath, log) {
7
7
  const result = {
8
- $schema: SchemaIdEnum.FULL,
8
+ $schema: VovkSchemaIdEnum.SCHEMA,
9
9
  config: {},
10
10
  segments: {},
11
11
  };
12
- const { log } = projectInfo;
13
12
  // Handle config.json
14
13
  const configPath = path.join(schemaOutAbsolutePath, 'config.json');
15
14
  try {