vovk-cli 0.0.1-draft.107 → 0.0.1-draft.109

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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023-present Andrii Gubanov
3
+ Copyright (c) 2023-present Andrey Gubanov
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,8 +1,8 @@
1
1
  <%- `// auto-generated ${new Date().toISOString()}\n/* eslint-disable */` %>
2
- const { default: fetcher } = require('<%= t.imports.fetcher %>');
3
- const { default: createRPC } = require('<%= t.imports.createRPC %>');
2
+ const { fetcher } = require('<%= t.imports.fetcher %>');
3
+ const { createRPC } = require('<%= t.imports.createRPC %>');
4
4
  const fullSchema = require('<%= t.imports.fullSchema %>');
5
- const { default: validateOnClient = null } = <%- t.imports.validateOnClient ? `require('${t.imports.validateOnClient}')` : '{}'%>;
5
+ const { validateOnClient = null } = <%- t.imports.validateOnClient ? `require('${t.imports.validateOnClient}')` : '{}'%>;
6
6
  const apiRoot = '<%= t.apiRoot %>';
7
7
  <% Object.values(t.fullSchema.segments).forEach((segment) => {
8
8
  Object.keys(segment.controllers).forEach((controllerName) => { %>
@@ -1,10 +1,10 @@
1
1
 
2
2
  <%- `// auto-generated ${new Date().toISOString()}\n/* eslint-disable */` %>
3
3
  import type { VovkClientFetcher } from 'vovk';
4
- import type fetcher from '<%= t.imports.fetcher %>';
5
- import type createRPC from '<%= t.imports.createRPC %>';
4
+ import type { fetcher } from '<%= t.imports.fetcher %>';
5
+ import type { createRPC } from '<%= t.imports.createRPC %>';
6
6
  <% Object.values(t.fullSchema.segments).forEach((segment, i) => { if(Object.keys(segment.controllers).length) { %>
7
- import type { Controllers as Controllers<%= i %> } from "<%= segment.segmentImportPath %>";
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
10
  <% Object.values(t.fullSchema.segments).forEach((segment, i) => {
@@ -1,10 +1,10 @@
1
1
 
2
2
  <%- `// auto-generated ${new Date().toISOString()}\n/* eslint-disable */` %>
3
3
  import type { VovkClientFetcher } from 'vovk';
4
- import type fetcher from '<%= t.imports.module.fetcher %>';
5
- import type createRPC from '<%= t.imports.module.createRPC %>';
4
+ import type { fetcher } from '<%= t.imports.module.fetcher %>';
5
+ import type { createRPC } from '<%= t.imports.module.createRPC %>';
6
6
  <% Object.values(t.fullSchema.segments).forEach((segment, i) => { if(Object.keys(segment.controllers).length) { %>
7
- import type { Controllers as Controllers<%= i %> } from "<%= segment.segmentImportPath %>";
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
10
  <% Object.values(t.fullSchema.segments).forEach((segment, i) => {
@@ -1,16 +1,13 @@
1
1
  <%- `// auto-generated ${new Date().toISOString()}\n/* eslint-disable */` %>
2
- import fetcherImport from '<%= t.imports.module.fetcher %>';
3
- import createRPCImport from '<%= t.imports.module.createRPC %>';
2
+ import { fetcher } from '<%= t.imports.module.fetcher %>';
3
+ import { createRPC } from '<%= t.imports.module.createRPC %>';
4
4
  import fullSchema from '<%= t.imports.module.fullSchema %>';
5
5
  <% if (t.imports.module.validateOnClient) { %>
6
- import validateOnClientImport from '<%= t.imports.module.validateOnClient %>';
7
- const validateOnClient = validateOnClientImport.default || validateOnClientImport;
6
+ import { validateOnClient } from '<%= t.imports.module.validateOnClient %>';
8
7
  <% } else { %>
9
8
  const validateOnClient = undefined;
10
9
  <% } %>
11
10
  const apiRoot = '<%= t.apiRoot %>';
12
- const fetcher = fetcherImport.default || fetcherImport;
13
- const createRPC = createRPCImport.default || createRPCImport;
14
11
  <% Object.values(t.fullSchema.segments).forEach((segment, i) => {
15
12
  Object.keys(segment.controllers).forEach((controllerName) => { %>
16
13
  export const <%= controllerName %> = createRPC(
@@ -1,13 +1,13 @@
1
1
  <%- `// auto-generated ${new Date().toISOString()}\n/* eslint-disable */` %>
2
2
  import type { VovkClientFetcher } from 'vovk';
3
- import fetcher from '<%= t.imports.fetcher %>';
4
- import createRPC from '<%= t.imports.createRPC %>';
3
+ import { fetcher } from '<%= t.imports.fetcher %>';
4
+ import { createRPC } from '<%= t.imports.createRPC %>';
5
5
  import fullSchema from '<%= t.imports.fullSchema %>';
6
6
  <% Object.values(t.fullSchema.segments).forEach((segment, i) => { if(Object.keys(segment.controllers).length) { %>
7
- import type { Controllers as Controllers<%= i %> } from "<%= segment.segmentImportPath %>";
7
+ import type { Controllers as Controllers<%= i %> } from "<%= t.segmentMeta[segment.segmentName].segmentImportPath %>";
8
8
  <% }}) %>
9
9
  <% if (t.imports.validateOnClient) { %>
10
- import validateOnClient from '<%= t.imports.validateOnClient %>';
10
+ import { validateOnClient } from '<%= t.imports.validateOnClient %>';
11
11
  <% } else { %>
12
12
  const validateOnClient = undefined;
13
13
  <% } %>
@@ -7,9 +7,9 @@ export default async function writeConfigJson(schemaOutAbsolutePath, projectInfo
7
7
  const existingStr = await fs.readFile(configJsonPath, 'utf-8').catch(() => null);
8
8
  if (existingStr !== configStr) {
9
9
  await fs.writeFile(configJsonPath, configStr);
10
- projectInfo?.log.info(`config.json written to ${configJsonPath}`);
10
+ projectInfo?.log.info(`Config JSON written to ${configJsonPath}`);
11
11
  }
12
12
  else {
13
- projectInfo?.log.debug(`config.json is up to date at ${configJsonPath}`);
13
+ projectInfo?.log.debug(`Config JSON is up to date at ${configJsonPath}`);
14
14
  }
15
15
  }
@@ -1,5 +1,4 @@
1
1
  import type { ProjectInfo } from '../getProjectInfo/index.mjs';
2
2
  export default function ensureClient({ config, cwd, log }: ProjectInfo): Promise<{
3
3
  written: boolean;
4
- path: string;
5
4
  }>;
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
2
2
  import getClientTemplates from './getClientTemplates.mjs';
3
3
  import uniq from 'lodash/uniq.js';
4
4
  import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
5
+ import path from 'node:path';
5
6
  export default async function ensureClient({ config, cwd, log }) {
6
7
  const now = Date.now();
7
8
  const { clientOutDirAbsolutePath, templateFiles } = getClientTemplates({
@@ -17,7 +18,7 @@ export default async function ensureClient({ config, cwd, log }) {
17
18
  for (const { outPath, templateName } of templateFiles) {
18
19
  const existing = await fs.readFile(outPath, 'utf-8').catch(() => null);
19
20
  if (!existing) {
20
- await fs.mkdir(clientOutDirAbsolutePath, { recursive: true });
21
+ await fs.mkdir(path.dirname(outPath), { recursive: true });
21
22
  await fs.writeFile(outPath, outPath.endsWith('.py') ? text.replace(/\/\//g, '#') : text);
22
23
  usedTemplateNames.push(templateName);
23
24
  }
@@ -26,5 +27,5 @@ export default async function ensureClient({ config, cwd, log }) {
26
27
  if (usedTemplateNames.length) {
27
28
  log.info(`Placeholder client files from template${usedTemplateNames.length !== 1 ? 's' : ''} ${chalkHighlightThing(usedTemplateNames.map((s) => `"${s}"`).join(', '))} are generated at ${clientOutDirAbsolutePath} in ${Date.now() - now}ms`);
28
29
  }
29
- return { written: !!usedTemplateNames.length, path: clientOutDirAbsolutePath };
30
+ return { written: !!usedTemplateNames.length };
30
31
  }
@@ -1,7 +1,7 @@
1
1
  import type { VovkFullSchema } from 'vovk';
2
2
  import type { ProjectInfo } from '../getProjectInfo/index.mjs';
3
3
  import type { Segment } from '../locateSegments.mjs';
4
- import { GenerateOptions } from '../types.mjs';
4
+ import type { GenerateOptions } from '../types.mjs';
5
5
  export default function generate({ projectInfo, segments, forceNothingWrittenLog, templates, prettify: prettifyClient, fullSchema, emitFullSchema, }: {
6
6
  projectInfo: ProjectInfo;
7
7
  segments: Segment[];
@@ -1,11 +1,12 @@
1
1
  import path from 'node:path';
2
2
  import fs from 'node:fs/promises';
3
3
  import ejs from 'ejs';
4
+ import matter from 'gray-matter';
5
+ import uniq from 'lodash/uniq.js';
4
6
  import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
5
7
  import prettify from '../utils/prettify.mjs';
6
8
  import getClientTemplates from './getClientTemplates.mjs';
7
9
  import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
8
- import uniq from 'lodash/uniq.js';
9
10
  export default async function generate({ projectInfo, segments, forceNothingWrittenLog, templates, prettify: prettifyClient, fullSchema, emitFullSchema, }) {
10
11
  const segmentsSchema = fullSchema.segments;
11
12
  const generateFrom = templates ?? projectInfo.config.generateFrom;
@@ -28,16 +29,29 @@ export default async function generate({ projectInfo, segments, forceNothingWrit
28
29
  apiRoot,
29
30
  imports: clientImports,
30
31
  fullSchema,
32
+ segmentMeta: Object.fromEntries(segments.map(({ segmentName, ...s }) => [segmentName, s])),
31
33
  };
32
34
  // Process each template in parallel
33
35
  const processedTemplates = noClient
34
36
  ? []
35
37
  : await Promise.all(templateFiles.map(async ({ templatePath, outPath, templateName }) => {
38
+ /*const parsed = matter((await ejs.render(codeTemplate, { t }, { async: true, filename: templateFileName })).trim());
39
+ const { dir, fileName, sourceName, compiledName } = parsed.data as VovkModuleRenderResult;
40
+ const code = empty ? (sourceName ? `export default class ${sourceName} {}` : '') : parsed.content;*/
36
41
  // Read the EJS template
37
42
  const templateContent = await fs.readFile(templatePath, 'utf-8');
43
+ const { data, content } = matter(templateContent);
44
+ if (data.imports instanceof Array) {
45
+ for (const imp of data.imports) {
46
+ t.imports = {
47
+ ...t.imports,
48
+ [imp]: await import(imp),
49
+ };
50
+ }
51
+ }
38
52
  // Render the template
39
53
  let rendered = templatePath.endsWith('.ejs')
40
- ? ejs.render(templateContent, { t }, {
54
+ ? ejs.render(content, { t }, {
41
55
  filename: templatePath,
42
56
  })
43
57
  : templateContent;
@@ -66,10 +80,6 @@ export default async function generate({ projectInfo, segments, forceNothingWrit
66
80
  .map(({ emitFullSchema }) => (typeof emitFullSchema === 'string' ? emitFullSchema : DEFAULT_NAME)));
67
81
  }
68
82
  fullSchemaNames = uniq(fullSchemaNames);
69
- if (fullSchemaNames.length || usedTemplateNames.length > 0) {
70
- // Make sure the output directory exists
71
- await fs.mkdir(clientOutDirAbsolutePath, { recursive: true });
72
- }
73
83
  if (fullSchemaNames.length) {
74
84
  await Promise.all(fullSchemaNames.map(async (name) => {
75
85
  const fullSchemaOutAbsolutePath = path.resolve(clientOutDirAbsolutePath, name);
@@ -83,8 +93,9 @@ export default async function generate({ projectInfo, segments, forceNothingWrit
83
93
  return { written: false, path: clientOutDirAbsolutePath };
84
94
  }
85
95
  // Write updated files where needed
86
- await Promise.all(processedTemplates.map(({ outPath, rendered, needsWriting }) => {
96
+ await Promise.all(processedTemplates.map(async ({ outPath, rendered, needsWriting }) => {
87
97
  if (needsWriting) {
98
+ await fs.mkdir(clientOutDirAbsolutePath, { recursive: true });
88
99
  return fs.writeFile(outPath, rendered);
89
100
  }
90
101
  return null;
@@ -6,8 +6,8 @@ export default async function getConfig({ clientOutDir, cwd }) {
6
6
  const conf = userConfig ?? {};
7
7
  const srcRoot = await getRelativeSrcRoot({ cwd });
8
8
  const validateOnClientImport = env.VOVK_VALIDATE_ON_CLIENT_PATH ?? conf.validateOnClientImport ?? null;
9
- const fetcherImport = env.VOVK_FETCHER_PATH ?? conf.fetcherImport ?? 'vovk/dist/client/defaultFetcher.js';
10
- const createRPCImport = env.VOVK_CREATE_RPC_PATH ?? conf.createRPCImport ?? 'vovk/dist/client/createRPC.js';
9
+ const fetcherImport = env.VOVK_FETCHER_PATH ?? conf.fetcherImport ?? 'vovk';
10
+ const createRPCImport = env.VOVK_CREATE_RPC_PATH ?? conf.createRPCImport ?? 'vovk';
11
11
  const defaultClientTemplates = ['ts', 'module', 'main'];
12
12
  const config = {
13
13
  emitConfig: [],
@@ -1,9 +1,9 @@
1
1
  import type getLogger from '../utils/getLogger.mjs';
2
2
  import type { InitOptions } from '../types.mjs';
3
- export default function createConfig({ root, log, options: { validationLibrary, validateOnClient, reactQuery, channel, dryRun }, }: {
3
+ export default function createConfig({ root, log, options: { validationLibrary, reactQuery, channel, dryRun }, }: {
4
4
  root: string;
5
5
  log: ReturnType<typeof getLogger>;
6
- options: Pick<InitOptions, 'validationLibrary' | 'validateOnClient' | 'reactQuery' | 'channel' | 'dryRun'>;
6
+ options: Pick<InitOptions, 'validationLibrary' | 'reactQuery' | 'channel' | 'dryRun'>;
7
7
  }): Promise<{
8
8
  configAbsolutePath: string;
9
9
  }>;
@@ -3,7 +3,7 @@ import fs from 'node:fs/promises';
3
3
  import getTemplateFilesFromPackage from './getTemplateFilesFromPackage.mjs';
4
4
  import prettify from '../utils/prettify.mjs';
5
5
  import getFileSystemEntryType, { FileSystemEntryType } from '../utils/getFileSystemEntryType.mjs';
6
- export default async function createConfig({ root, log, options: { validationLibrary, validateOnClient, reactQuery, channel, dryRun }, }) {
6
+ export default async function createConfig({ root, log, options: { validationLibrary, reactQuery, channel, dryRun }, }) {
7
7
  const config = {};
8
8
  const dotConfigPath = path.join(root, '.config');
9
9
  const dir = (await getFileSystemEntryType(dotConfigPath)) === FileSystemEntryType.DIRECTORY ? dotConfigPath : root;
@@ -16,9 +16,10 @@ export default async function createConfig({ root, log, options: { validationLib
16
16
  service: 'vovk-cli/templates/service.ejs',
17
17
  };
18
18
  if (validationLibrary) {
19
- if (validateOnClient) {
20
- config.validateOnClientImport = `${validationLibrary}/validateOnClient.js`;
21
- }
19
+ config.validateOnClientImport =
20
+ {
21
+ 'vovk-dto': `vovk-dto/validateOnClient.js`,
22
+ }[validationLibrary] ?? 'vovk-ajv';
22
23
  try {
23
24
  const validationTemplates = await getTemplateFilesFromPackage(validationLibrary, channel);
24
25
  Object.assign(templates, validationTemplates);
@@ -4,5 +4,5 @@ export declare class Init {
4
4
  #private;
5
5
  root: string;
6
6
  log: ReturnType<typeof getLogger>;
7
- main(prefix: string, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, reactQuery, dryRun, channel, }: InitOptions): Promise<void>;
7
+ main(prefix: string, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, reactQuery, dryRun, channel, }: InitOptions): Promise<void>;
8
8
  }
@@ -17,26 +17,24 @@ import NPMCliPackageJson from '@npmcli/package-json';
17
17
  export class Init {
18
18
  root;
19
19
  log;
20
- async #init({ configPaths, pkgJson, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, reactQuery, dryRun, channel, }) {
20
+ async #init({ configPaths, pkgJson, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, reactQuery, dryRun, channel, }) {
21
21
  const { log, root } = this;
22
- const dependencies = ['vovk', 'vovk-client'];
23
- const devDependencies = ['vovk-cli', 'openapi3-ts'];
22
+ const dependencies = ['vovk', 'vovk-client', 'vovk-openapi'];
23
+ const devDependencies = ['vovk-cli'];
24
24
  // delete older config files
25
25
  if (configPaths.length) {
26
26
  await Promise.all(configPaths.map((configPath) => fs.rm(configPath)));
27
27
  log.debug(`Deleted existing config file${configPaths.length > 1 ? 's' : ''} at ${configPaths.join(', ')}`);
28
28
  }
29
29
  if (validationLibrary) {
30
- dependencies.push(validationLibrary);
31
- dependencies.push(...({
30
+ dependencies.push(validationLibrary, 'vovk-ajv', ...({
32
31
  'vovk-zod': ['zod'],
33
32
  'vovk-yup': ['yup'],
34
33
  'vovk-dto': ['class-validator', 'class-transformer', 'vovk-mapped-types', 'reflect-metadata'],
35
34
  }[validationLibrary] ?? []));
36
35
  }
37
36
  if (reactQuery) {
38
- dependencies.push('vovk-react-query');
39
- dependencies.push('@tanstack/react-query');
37
+ dependencies.push('vovk-react-query', '@tanstack/react-query');
40
38
  }
41
39
  if (updateScripts) {
42
40
  try {
@@ -106,7 +104,7 @@ export class Init {
106
104
  const { configAbsolutePath } = await createConfig({
107
105
  root,
108
106
  log,
109
- options: { validationLibrary, validateOnClient, reactQuery, channel, dryRun },
107
+ options: { validationLibrary, reactQuery, channel, dryRun },
110
108
  });
111
109
  log.info('Config created successfully at ' + configAbsolutePath);
112
110
  }
@@ -114,7 +112,7 @@ export class Init {
114
112
  log.error(`Failed to create config: ${error.message}`);
115
113
  }
116
114
  }
117
- async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, reactQuery, dryRun, channel, }) {
115
+ async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, reactQuery, dryRun, channel, }) {
118
116
  const cwd = process.cwd();
119
117
  const root = path.resolve(cwd, prefix);
120
118
  const log = getLogger(logLevel);
@@ -132,7 +130,6 @@ export class Init {
132
130
  updateTsConfig: updateTsConfig ?? true,
133
131
  updateScripts: updateScripts ?? 'implicit',
134
132
  validationLibrary: validationLibrary?.toLocaleLowerCase() === 'none' ? null : (validationLibrary ?? 'vovk-zod'),
135
- validateOnClient: validateOnClient ?? true,
136
133
  reactQuery: reactQuery ?? true,
137
134
  dryRun: dryRun ?? false,
138
135
  channel: channel ?? 'latest',
@@ -176,13 +173,6 @@ export class Init {
176
173
  { name: 'None', value: null, description: 'Install validation library later' },
177
174
  ],
178
175
  })));
179
- if (validationLibrary) {
180
- validateOnClient =
181
- validateOnClient ??
182
- (await confirm({
183
- message: 'Do you want to enable client validation?',
184
- }));
185
- }
186
176
  updateScripts =
187
177
  updateScripts ??
188
178
  (await select({
@@ -234,7 +224,6 @@ export class Init {
234
224
  updateTsConfig,
235
225
  updateScripts,
236
226
  validationLibrary,
237
- validateOnClient,
238
227
  reactQuery,
239
228
  dryRun,
240
229
  channel,
@@ -9,7 +9,9 @@ export default async function updateNPMScripts(pkgJson, root, updateScriptsMode)
9
9
  pkgJson.update({
10
10
  scripts: {
11
11
  ...pkgJson.content.scripts,
12
- generate: 'vovk generate',
12
+ generate: pkgJson.content.scripts?.generate
13
+ ? `${pkgJson.content.scripts.generate} && vovk generate`
14
+ : 'vovk generate',
13
15
  dev: getDevScript(pkgJson, updateScriptsMode),
14
16
  },
15
17
  });
@@ -14,7 +14,6 @@ export default function initProgram(program) {
14
14
  .option('--update-ts-config', 'update tsconfig.json')
15
15
  .option('--update-scripts <mode>', 'update package.json scripts ("implicit" or "explicit")')
16
16
  .option('--validation-library <library>', 'validation library to use ("vovk-zod", "vovk-yup", "vovk-dto" or another); set to "none" to skip')
17
- .option('--validate-on-client', 'path to validateOnClient file')
18
17
  .option('--react-query', 'use @tanstack/react-query for data fetching inside components')
19
18
  .option('--channel <channel>', 'channel to use for fetching packages', 'latest')
20
19
  .option('--dry-run', 'do not write files to disk')
package/dist/types.d.mts CHANGED
@@ -28,7 +28,6 @@ export interface InitOptions {
28
28
  updateScripts?: 'implicit' | 'explicit';
29
29
  validationLibrary?: string | null;
30
30
  reactQuery?: boolean;
31
- validateOnClient?: boolean;
32
31
  dryRun?: boolean;
33
32
  channel?: 'latest' | 'beta' | 'draft';
34
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vovk-cli",
3
- "version": "0.0.1-draft.107",
3
+ "version": "0.0.1-draft.109",
4
4
  "bin": {
5
5
  "vovk": "./dist/index.mjs"
6
6
  },
@@ -29,14 +29,17 @@
29
29
  "cli",
30
30
  "vovk"
31
31
  ],
32
- "author": "Andrii Gubanov",
32
+ "author": "Andrey Gubanov",
33
33
  "license": "MIT",
34
34
  "bugs": {
35
35
  "url": "https://github.com/finom/vovk/issues"
36
36
  },
37
37
  "homepage": "https://vovk.dev",
38
38
  "peerDependencies": {
39
- "vovk": "^3.0.0-draft.90"
39
+ "vovk": "^3.0.0-draft.92"
40
+ },
41
+ "optionalDependencies": {
42
+ "vovk-python-client": "*"
40
43
  },
41
44
  "dependencies": {
42
45
  "@inquirer/prompts": "^7.3.1",