vovk-cli 0.0.1-draft.143 → 0.0.1-draft.145

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 (44) hide show
  1. package/client-templates/{fullSchema → fullSchemaCjs}/fullSchema.d.cts.ejs +1 -1
  2. package/client-templates/fullSchemaJson/full-schema.json.ejs +1 -0
  3. package/client-templates/fullSchemaTs/fullSchema.ts.ejs +18 -0
  4. package/client-templates/main/main.cjs.ejs +1 -2
  5. package/client-templates/module/module.mjs.ejs +1 -2
  6. package/client-templates/package/package.json.ejs +1 -0
  7. package/client-templates/ts/index.ts.ejs +2 -3
  8. package/dist/dev/index.mjs +0 -1
  9. package/dist/generate/ensureClient.d.mts +1 -3
  10. package/dist/generate/ensureClient.mjs +19 -66
  11. package/dist/generate/getClientTemplateFiles.d.mts +17 -0
  12. package/dist/generate/getClientTemplateFiles.mjs +61 -0
  13. package/dist/generate/getTemplateClientImports.d.mts +19 -0
  14. package/dist/generate/getTemplateClientImports.mjs +38 -0
  15. package/dist/generate/index.d.mts +4 -7
  16. package/dist/generate/index.mjs +157 -157
  17. package/dist/generate/mergePackages.d.mts +7 -0
  18. package/dist/generate/mergePackages.mjs +74 -0
  19. package/dist/generate/writeOneClientFile.d.mts +28 -0
  20. package/dist/generate/writeOneClientFile.mjs +78 -0
  21. package/dist/getProjectInfo/getConfig/getTemplateDefs.d.mts +11 -0
  22. package/dist/getProjectInfo/getConfig/getTemplateDefs.mjs +66 -0
  23. package/dist/getProjectInfo/{getConfig.d.mts → getConfig/index.d.mts} +3 -3
  24. package/dist/getProjectInfo/{getConfig.mjs → getConfig/index.mjs} +32 -21
  25. package/dist/getProjectInfo/index.d.mts +3 -12
  26. package/dist/getProjectInfo/index.mjs +3 -39
  27. package/dist/index.mjs +7 -9
  28. package/dist/init/createConfig.mjs +7 -6
  29. package/dist/init/getTemplateFilesFromPackage.mjs +4 -4
  30. package/dist/init/index.mjs +16 -4
  31. package/dist/init/updateTypeScriptConfig.d.mts +4 -1
  32. package/dist/init/updateTypeScriptConfig.mjs +11 -7
  33. package/dist/locateSegments.d.mts +0 -1
  34. package/dist/locateSegments.mjs +1 -2
  35. package/dist/new/newModule.mjs +1 -1
  36. package/dist/types.d.mts +4 -3
  37. package/dist/utils/pickSegmentFullSchema.d.mts +2 -1
  38. package/dist/utils/pickSegmentFullSchema.mjs +8 -2
  39. package/package.json +10 -10
  40. package/dist/generate/getClientTemplates.d.mts +0 -25
  41. package/dist/generate/getClientTemplates.mjs +0 -113
  42. /package/client-templates/{fullSchema → fullSchemaCjs}/fullSchema.cjs.ejs +0 -0
  43. /package/{templates → module-templates}/controller.ts.ejs +0 -0
  44. /package/{templates → module-templates}/service.ts.ejs +0 -0
@@ -1,5 +1,5 @@
1
1
  <%- `// auto-generated ${new Date().toISOString()}\n/* eslint-disable */` %>
2
- import { VovkStrictConfig, VovkSegmentSchema } from 'vovk';
2
+ import type { VovkStrictConfig, VovkSegmentSchema } from 'vovk';
3
3
 
4
4
  export const fullSchema: {
5
5
  config: Partial<VovkStrictConfig>;
@@ -0,0 +1 @@
1
+ <%- JSON.stringify(t.fullSchema, null, 2) %>
@@ -0,0 +1,18 @@
1
+ <%- `// auto-generated ${new Date().toISOString()}\n/* eslint-disable */` %>
2
+ import type { VovkStrictConfig, VovkSegmentSchema } from 'vovk';
3
+ import config from './<%= t.schemaOutDir %>/config.json' with { type: "json" };
4
+
5
+ <% Object.values(t.fullSchema.segments).forEach((segment, i) => { if(Object.keys(segment.controllers).length) { %>
6
+ import segment<%= i %> from './<%= t.schemaOutDir %>/<%= t.SEGMENTS_SCHEMA_DIR_NAME %>/<%= segment.segmentName || t.ROOT_SEGMENT_SCHEMA_NAME %>.json' with { type: "json" };
7
+ <% }}) %>
8
+
9
+ const segments = {
10
+ <% Object.values(t.fullSchema.segments).forEach((segment, i) => { %>
11
+ '<%= segment.segmentName %>': segment<%= i %>,
12
+ <% }) %>
13
+ };
14
+
15
+ export const fullSchema = {
16
+ config,
17
+ segments,
18
+ };
@@ -3,12 +3,11 @@ const { fetcher } = require('<%= t.imports.fetcher %>');
3
3
  const { createRPC } = require('<%= t.imports.createRPC %>');
4
4
  const { fullSchema } = require('./fullSchema.cjs');
5
5
  const { validateOnClient = null } = <%- t.imports.validateOnClient ? `require('${t.imports.validateOnClient}')` : '{}'%>;
6
- const apiRoot = '<%= t.apiRoot %>';
7
6
  <% Object.values(t.fullSchema.segments).forEach((segment) => {
8
7
  Object.keys(segment.controllers).forEach((controllerName) => { %>
9
8
  exports.<%= controllerName %> = createRPC(
10
9
  fullSchema, '<%= segment.segmentName %>', '<%= controllerName %>',
11
- { fetcher, validateOnClient, defaultOptions: { apiRoot } }
10
+ { fetcher, validateOnClient, defaultOptions: { apiRoot: '<%= segment.segmentApiRoot ?? t.apiRoot %>' } }
12
11
  );
13
12
  <% })
14
13
  }) %>
@@ -7,12 +7,11 @@ import { validateOnClient } from '<%= t.imports.module.validateOnClient %>';
7
7
  <% } else { %>
8
8
  const validateOnClient = undefined;
9
9
  <% } %>
10
- const apiRoot = '<%= t.apiRoot %>';
11
10
  <% Object.values(t.fullSchema.segments).forEach((segment, i) => {
12
11
  Object.keys(segment.controllers).forEach((controllerName) => { %>
13
12
  export const <%= controllerName %> = createRPC(
14
13
  fullSchema, '<%= segment.segmentName %>', '<%= controllerName %>',
15
- { fetcher, validateOnClient, defaultOptions: { apiRoot } }
14
+ { fetcher, validateOnClient, defaultOptions: { apiRoot: '<%= segment.segmentApiRoot ?? t.apiRoot %>' } }
16
15
  );
17
16
  <%
18
17
  });
@@ -0,0 +1 @@
1
+ <%- JSON.stringify(t.package, null, 2) %>
@@ -2,7 +2,7 @@
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.cjs';
5
+ import { fullSchema } from './fullSchema.ts';
6
6
  <% Object.values(t.fullSchema.segments).forEach((segment, i) => { if(Object.keys(segment.controllers).length) { %>
7
7
  import type { Controllers as Controllers<%= i %> } from "<%= t.segmentMeta[segment.segmentName].segmentImportPath %>";
8
8
  <% }}) %>
@@ -12,12 +12,11 @@ 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
- const apiRoot = '<%= t.apiRoot %>';
16
15
  <% Object.values(t.fullSchema.segments).forEach((segment, i) => { %>
17
16
  <% Object.keys(segment.controllers).forEach((controllerName) => { %>
18
17
  export const <%= controllerName %> = createRPC<Controllers<%= i %>["<%= controllerName %>"], Options>(
19
18
  fullSchema, '<%= segment.segmentName %>', '<%= controllerName %>',
20
- { fetcher, validateOnClient, defaultOptions: { apiRoot } }
19
+ { fetcher, validateOnClient, defaultOptions: { apiRoot: '<%= segment.segmentApiRoot ?? t.apiRoot %>' } }
21
20
  );
22
21
  <% }) %>
23
22
  <% }) %>
@@ -50,7 +50,6 @@ export class VovkDev {
50
50
  {
51
51
  routeFilePath: filePath,
52
52
  segmentName,
53
- segmentImportPath: path.relative(config.clientOutDir, filePath), // TODO DRY locateSegments
54
53
  },
55
54
  ];
56
55
  log.info(`${capitalize(formatLoggedSegmentName(segmentName))} has been added`);
@@ -1,4 +1,2 @@
1
1
  import type { ProjectInfo } from '../getProjectInfo/index.mjs';
2
- export default function ensureClient({ config, cwd, log, segments }: ProjectInfo): Promise<{
3
- written: boolean;
4
- }>;
2
+ export default function ensureClient(projectInfo: ProjectInfo): Promise<void>;
@@ -1,69 +1,22 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import getClientTemplates, { BuiltInTemplateName } from './getClientTemplates.mjs';
4
- import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
5
- import { ROOT_SEGMENT_SCHEMA_NAME } from '../dev/writeOneSegmentSchemaFile.mjs';
6
- async function writeOnePlaceholder({ outPath, defaultText, templateName, usedTemplateNames, }) {
7
- const existing = await fs.readFile(outPath, 'utf-8').catch(() => null);
8
- if (!existing) {
9
- let text = defaultText;
10
- await fs.mkdir(path.dirname(outPath), { recursive: true });
11
- // a workaround that prevents compilation error when client is not yet generated but back-end imports fullSchema
12
- if (Object.keys(BuiltInTemplateName).includes(templateName)) {
13
- if (outPath.endsWith('.cjs')) {
14
- text += '\nmodule.exports.fullSchema = {};';
15
- }
16
- else {
17
- text += '\nexport const fullSchema = {};';
18
- }
19
- }
20
- await fs.writeFile(outPath, outPath.endsWith('.py') ? text.replace(/\/\//g, '#') : text);
21
- usedTemplateNames.add(templateName);
1
+ import generate from './index.mjs';
2
+ const getEmptySegmentRecordSchema = (segments) => {
3
+ const result = {};
4
+ for (const { segmentName } of segments) {
5
+ result[segmentName] = {
6
+ segmentName,
7
+ emitSchema: false,
8
+ controllers: {},
9
+ };
22
10
  }
23
- }
24
- export default async function ensureClient({ config, cwd, log, segments }) {
25
- const now = Date.now();
26
- const { clientOutDirAbsolutePath, templateFiles } = await getClientTemplates({
27
- config,
28
- cwd,
29
- generateFrom: config.generateFrom,
30
- log,
11
+ return result;
12
+ };
13
+ export default async function ensureClient(projectInfo) {
14
+ return generate({
15
+ isEnsuringClient: true,
16
+ projectInfo,
17
+ fullSchema: {
18
+ config: {},
19
+ segments: getEmptySegmentRecordSchema(projectInfo.segments),
20
+ },
31
21
  });
32
- const usedTemplateNames = new Set();
33
- const defaultText = `// auto-generated ${new Date().toISOString()}
34
- // This is a temporary placeholder to avoid compilation errors if client is imported before it's generated.
35
- // If you still see this text, the client is not generated yet because of an unknown problem.
36
- // Feel free to report an issue at https://github.com/finom/vovk/issues`;
37
- await Promise.all(templateFiles.map(async (clientTemplate) => {
38
- const { templatePath, templateName, outDir } = clientTemplate;
39
- if (!templatePath)
40
- return;
41
- const outPath = path.join(outDir, path.basename(templatePath).replace('.ejs', ''));
42
- if (config.generateFullClient) {
43
- await writeOnePlaceholder({
44
- outPath,
45
- defaultText,
46
- templateName,
47
- usedTemplateNames,
48
- });
49
- }
50
- if (config.generateSegmentClient) {
51
- // Generate client files for each segment
52
- await Promise.all(segments.map(async ({ segmentName }) => {
53
- const outPath = path.join(outDir, segmentName || ROOT_SEGMENT_SCHEMA_NAME, path.basename(templatePath).replace('.ejs', ''));
54
- return writeOnePlaceholder({
55
- outPath,
56
- defaultText,
57
- templateName,
58
- usedTemplateNames,
59
- });
60
- }));
61
- }
62
- }));
63
- if (usedTemplateNames.size) {
64
- log.info(`Placeholder client files from template${usedTemplateNames.size !== 1 ? 's' : ''} ${chalkHighlightThing(Array.from(usedTemplateNames)
65
- .map((s) => `"${s}"`)
66
- .join(', '))} are generated at ${clientOutDirAbsolutePath} in ${Date.now() - now}ms`);
67
- }
68
- return { written: !!usedTemplateNames.size };
69
22
  }
@@ -0,0 +1,17 @@
1
+ import type { VovkStrictConfig } from 'vovk';
2
+ import type { ProjectInfo } from '../getProjectInfo/index.mjs';
3
+ import { type GenerateOptions } from '../types.mjs';
4
+ export interface ClientTemplateFile {
5
+ templateName: string;
6
+ templateFilePath: string;
7
+ relativeDir: string;
8
+ outCwdRelativeDir: string;
9
+ templateDef: VovkStrictConfig['clientTemplateDefs'][string];
10
+ }
11
+ export default function getClientTemplateFiles({ config, cwd, log, configKey, cliOptions, }: {
12
+ config: VovkStrictConfig;
13
+ cwd: string;
14
+ log: ProjectInfo['log'];
15
+ configKey: 'fullClient' | 'segmentedClient';
16
+ cliOptions?: GenerateOptions;
17
+ }): Promise<ClientTemplateFile[]>;
@@ -0,0 +1,61 @@
1
+ import path from 'node:path';
2
+ import { glob } from 'glob';
3
+ import resolveAbsoluteModulePath from '../utils/resolveAbsoluteModulePath.mjs';
4
+ import getFileSystemEntryType, { FileSystemEntryType } from '../utils/getFileSystemEntryType.mjs';
5
+ export default async function getClientTemplateFiles({ config, cwd, log, configKey, cliOptions, }) {
6
+ const usedTemplateDefs = {};
7
+ const fromTemplates = configKey === 'fullClient'
8
+ ? (cliOptions?.fullClientFrom ?? config.fullClient.fromTemplates)
9
+ : (cliOptions?.segmentedClientFrom ?? config.segmentedClient.fromTemplates);
10
+ const outDir = configKey === 'fullClient'
11
+ ? (cliOptions?.fullClientOut ?? config.fullClient.outDir)
12
+ : (cliOptions?.segmentedClientOut ?? config.segmentedClient.outDir);
13
+ for (const templateName of fromTemplates) {
14
+ if (!(templateName in config.clientTemplateDefs)) {
15
+ throw new Error(`Unknown template name: ${templateName}`);
16
+ }
17
+ usedTemplateDefs[templateName] = config.clientTemplateDefs[templateName];
18
+ }
19
+ const templateFiles = [];
20
+ const entries = Object.entries(usedTemplateDefs);
21
+ for (let i = 0; i < entries.length; i++) {
22
+ const [templateName, templateDef, forceOutCwdRelativeDir] = entries[i];
23
+ const templateAbsolutePath = resolveAbsoluteModulePath(templateDef.templatePath, cwd);
24
+ const entryType = await getFileSystemEntryType(templateDef.templatePath);
25
+ if (!entryType)
26
+ throw new Error(`Unable to locate template path ${templateDef.templatePath}`);
27
+ const defOutDir = configKey === 'fullClient' ? templateDef.fullClient?.outDir : templateDef.segmentedClient?.outDir;
28
+ let files;
29
+ if (entryType === FileSystemEntryType.FILE) {
30
+ files = [templateAbsolutePath];
31
+ }
32
+ else {
33
+ const globPath = path.join(templateAbsolutePath, '**/*.*');
34
+ files = await glob(globPath);
35
+ }
36
+ if (files.length === 0) {
37
+ log.error(`Template "${templateAbsolutePath}" not found`);
38
+ continue;
39
+ }
40
+ const outCwdRelativeDir = forceOutCwdRelativeDir ?? defOutDir ?? outDir;
41
+ for (const filePath of files) {
42
+ templateFiles.push({
43
+ templateName,
44
+ templateFilePath: filePath,
45
+ relativeDir: path.relative(templateAbsolutePath, path.dirname(filePath)),
46
+ outCwdRelativeDir,
47
+ templateDef,
48
+ });
49
+ }
50
+ if (templateDef.requires) {
51
+ for (const [tName, reqRelativeDir] of Object.entries(templateDef.requires)) {
52
+ const def = config.clientTemplateDefs[tName];
53
+ if (!def) {
54
+ throw new Error(`Template "${tName}" required by "${templateName}" not found`);
55
+ }
56
+ entries.push([tName, def, path.join(outCwdRelativeDir, reqRelativeDir)]);
57
+ }
58
+ }
59
+ }
60
+ return templateFiles;
61
+ }
@@ -0,0 +1,19 @@
1
+ import type { VovkStrictConfig } from 'vovk';
2
+ import type { Segment } from '../locateSegments.mjs';
3
+ export type ClientImports = {
4
+ fetcher: string;
5
+ validateOnClient: string | null;
6
+ createRPC: string;
7
+ };
8
+ export default function getTemplateClientImports({ config, segments, outCwdRelativeDir, }: {
9
+ config: VovkStrictConfig;
10
+ segments: Segment[];
11
+ outCwdRelativeDir: string;
12
+ }): {
13
+ fullClient: ClientImports & {
14
+ module: ClientImports;
15
+ };
16
+ segmentedClient: Record<string, ClientImports & {
17
+ module: ClientImports;
18
+ }>;
19
+ };
@@ -0,0 +1,38 @@
1
+ import path from 'node:path';
2
+ import { ROOT_SEGMENT_SCHEMA_NAME } from '../dev/writeOneSegmentSchemaFile.mjs';
3
+ export default function getTemplateClientImports({ config, segments, outCwdRelativeDir, }) {
4
+ const { imports } = config;
5
+ const getImportPath = (p, s = '') => p.startsWith('.') ? path.relative(path.join(outCwdRelativeDir, s), p) : p;
6
+ const clientImports = {
7
+ fullClient: {
8
+ fetcher: getImportPath(imports.fetcher[0]),
9
+ createRPC: getImportPath(imports.createRPC[0]),
10
+ validateOnClient: imports.validateOnClient ? getImportPath(imports.validateOnClient[0]) : null,
11
+ module: {
12
+ fetcher: getImportPath(imports.fetcher[1] ?? imports.fetcher[0]),
13
+ createRPC: getImportPath(imports.createRPC[1] ?? imports.createRPC[0]),
14
+ validateOnClient: imports.validateOnClient
15
+ ? getImportPath(imports.validateOnClient[1] ?? imports.validateOnClient[0])
16
+ : null,
17
+ },
18
+ },
19
+ segmentedClient: Object.fromEntries(segments.map((segment) => [
20
+ segment.segmentName,
21
+ {
22
+ fetcher: getImportPath(imports.fetcher[0], segment.segmentName || ROOT_SEGMENT_SCHEMA_NAME),
23
+ createRPC: getImportPath(imports.createRPC[0], segment.segmentName || ROOT_SEGMENT_SCHEMA_NAME),
24
+ validateOnClient: imports.validateOnClient
25
+ ? getImportPath(imports.validateOnClient[0], segment.segmentName || ROOT_SEGMENT_SCHEMA_NAME)
26
+ : null,
27
+ module: {
28
+ fetcher: getImportPath(imports.fetcher[1] ?? imports.fetcher[0], segment.segmentName || ROOT_SEGMENT_SCHEMA_NAME),
29
+ createRPC: getImportPath(imports.createRPC[1] ?? imports.createRPC[0], segment.segmentName || ROOT_SEGMENT_SCHEMA_NAME),
30
+ validateOnClient: imports.validateOnClient
31
+ ? getImportPath(imports.validateOnClient[1] ?? imports.validateOnClient[0], segment.segmentName || ROOT_SEGMENT_SCHEMA_NAME)
32
+ : null,
33
+ },
34
+ },
35
+ ])),
36
+ };
37
+ return clientImports;
38
+ }
@@ -1,13 +1,10 @@
1
1
  import type { VovkFullSchema } from 'vovk';
2
2
  import type { ProjectInfo } from '../getProjectInfo/index.mjs';
3
3
  import type { GenerateOptions } from '../types.mjs';
4
- export type ClientImports = {
5
- fetcher: string;
6
- validateOnClient: string | null;
7
- createRPC: string;
8
- };
9
- export default function generate({ projectInfo, forceNothingWrittenLog, templates, prettify: prettifyClient, fullSchema, fullSchemaJson, }: {
4
+ export default function generate({ isEnsuringClient, projectInfo, forceNothingWrittenLog, fullSchema, cliOptions, }: {
5
+ isEnsuringClient?: boolean;
10
6
  projectInfo: ProjectInfo;
11
7
  forceNothingWrittenLog?: boolean;
12
8
  fullSchema: VovkFullSchema;
13
- } & Pick<GenerateOptions, 'templates' | 'prettify' | 'fullSchemaJson'>): Promise<void>;
9
+ cliOptions?: GenerateOptions;
10
+ }): Promise<void>;