vovk-cli 0.0.1-draft.65 → 0.0.1-draft.67

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.
@@ -1,11 +1,11 @@
1
1
  <%- '// auto-generated\n/* eslint-disable */' %>
2
- const { default: fetcher } = require('<%= fetcherClientImportPath %>');
3
- const { default: createRPC } = require('<%= createRPCImportPath %>');
4
- const schema = require('<%= schemaOutImportPath %>');
5
- const { default: validateOnClient = null } = <%- validateOnClientImportPath ? `require('${validateOnClientImportPath}')` : '{}'%>;
6
- const apiRoot = '<%= apiRoot %>';
7
- <% segments.forEach((segment) => {
8
- Object.keys(segmentsSchema[segment.segmentName].controllers).forEach((key) => { %>
2
+ const { default: fetcher } = require('<%= template.imports.fetcher %>');
3
+ const { default: createRPC } = require('<%= template.imports.createRPC %>');
4
+ const schema = require('<%= template.imports.schema %>');
5
+ const { default: validateOnClient = null } = <%- template.imports.validateOnClient ? `require('${template.imports.validateOnClient}')` : '{}'%>;
6
+ const apiRoot = '<%= template.apiRoot %>';
7
+ <% template.segments.forEach((segment) => {
8
+ Object.keys(template.fullSchema[segment.segmentName].controllers).forEach((key) => { %>
9
9
  exports.<%= key %> = createRPC(
10
10
  schema['<%= segment.segmentName %>'].controllers.<%= key %>,
11
11
  '<%= segment.segmentName %>',
@@ -1,14 +1,14 @@
1
1
 
2
2
  <%- '// auto-generated\n/* eslint-disable */' %>
3
3
  import type { VovkClientFetcher } from 'vovk';
4
- import type fetcher from '<%= fetcherClientImportPath %>';
5
- import type createRPC from '<%= createRPCImportPath %>';
6
- <% segments.forEach((segment, i) => { if(Object.keys(segmentsSchema[segment.segmentName].controllers).length) { %>
4
+ import type fetcher from '<%= template.imports.fetcher %>';
5
+ import type createRPC from '<%= template.imports.createRPC %>';
6
+ <% template.segments.forEach((segment, i) => { if(Object.keys(template.fullSchema[segment.segmentName].controllers).length) { %>
7
7
  import type { Controllers as Controllers<%= i %> } from "<%= segment.segmentImportPath %>";
8
8
  <% }}) %>
9
9
  type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
10
- <% segments.forEach((segment, i) => {
11
- Object.keys(segmentsSchema[segment.segmentName].controllers).forEach((key) => { %>
10
+ <% template.segments.forEach((segment, i) => {
11
+ Object.keys(template.fullSchema[segment.segmentName].controllers).forEach((key) => { %>
12
12
  export const <%= key %>: ReturnType<typeof createRPC<Controllers<%= i %>["<%= key %>"], Options>>;
13
13
  <% })
14
14
  }) %>
@@ -1,14 +1,14 @@
1
1
 
2
2
  <%- '// auto-generated\n/* eslint-disable */' %>
3
3
  import type { VovkClientFetcher } from 'vovk';
4
- import type fetcher from '<%= fetcherClientImportPath %>';
5
- import type createRPC from '<%= createRPCImportPath %>';
6
- <% segments.forEach((segment, i) => { if(Object.keys(segmentsSchema[segment.segmentName].controllers).length) { %>
4
+ import type fetcher from '<%= template.imports.module.fetcher %>';
5
+ import type createRPC from '<%= template.imports.module.createRPC %>';
6
+ <% template.segments.forEach((segment, i) => { if(Object.keys(template.fullSchema[segment.segmentName].controllers).length) { %>
7
7
  import type { Controllers as Controllers<%= i %> } from "<%= segment.segmentImportPath %>";
8
8
  <% }}) %>
9
9
  type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
10
- <% segments.forEach((segment, i) => {
11
- Object.keys(segmentsSchema[segment.segmentName].controllers).forEach((key) => { %>
10
+ <% template.segments.forEach((segment, i) => {
11
+ Object.keys(template.fullSchema[segment.segmentName].controllers).forEach((key) => { %>
12
12
  export const <%= key %>: ReturnType<typeof createRPC<Controllers<%= i %>["<%= key %>"], Options>>;
13
13
  <% })
14
14
  }) %>
@@ -1,18 +1,18 @@
1
1
  <%- '// auto-generated\n/* eslint-disable */' %>
2
- import fetcherImport from '<%= fetcherClientImportPath %>';
3
- import createRPCImport from '<%= createRPCImportPath %>';
4
- import schema from '<%= schemaOutImportPath %>';
5
- <% if (validateOnClientImportPath) { %>
6
- import validateOnClientImport from '<%= validateOnClientImportPath %>';
2
+ import fetcherImport from '<%= template.imports.module.fetcher %>';
3
+ import createRPCImport from '<%= template.imports.module.createRPC %>';
4
+ import schema from '<%= template.imports.module.schema %>';
5
+ <% if (template.imports.module.validateOnClient) { %>
6
+ import validateOnClientImport from '<%= template.imports.module.validateOnClient %>';
7
7
  const validateOnClient = validateOnClientImport.default || validateOnClientImport;
8
8
  <% } else { %>
9
9
  const validateOnClient = undefined;
10
10
  <% } %>
11
- const apiRoot = '<%= apiRoot %>';
11
+ const apiRoot = '<%= template.apiRoot %>';
12
12
  const fetcher = fetcherImport.default || fetcherImport;
13
13
  const createRPC = createRPCImport.default || createRPCImport;
14
- <% segments.forEach((segment, i) => {
15
- Object.keys(segmentsSchema[segment.segmentName].controllers).forEach((key) => { %>
14
+ <% template.segments.forEach((segment, i) => {
15
+ Object.keys(template.fullSchema[segment.segmentName].controllers).forEach((key) => { %>
16
16
  export const <%= key %> = createRPC(
17
17
  schema['<%= segment.segmentName %>'].controllers.<%= key %>,
18
18
  '<%= segment.segmentName %>',
@@ -1,21 +1,21 @@
1
1
  <%- '// auto-generated\n/* eslint-disable */' %>
2
2
  import type { VovkClientFetcher } from 'vovk';
3
- import fetcher from '<%= fetcherClientImportPath %>';
4
- import createRPC from '<%= createRPCImportPath %>';
5
- import schema from '<%= schemaOutImportPath %>';
6
- <% segments.forEach((segment, i) => { if(Object.keys(segmentsSchema[segment.segmentName].controllers).length) { %>
3
+ import fetcher from '<%= template.imports.fetcher %>';
4
+ import createRPC from '<%= template.imports.createRPC %>';
5
+ import schema from '<%= template.imports.schema %>';
6
+ <% template.segments.forEach((segment, i) => { if(Object.keys(template.fullSchema[segment.segmentName].controllers).length) { %>
7
7
  import type { Controllers as Controllers<%= i %> } from "<%= segment.segmentImportPath %>";
8
8
  <% }}) %>
9
- <% if (validateOnClientImportPath) { %>
10
- import validateOnClient from '<%= validateOnClientImportPath %>';
9
+ <% if (template.imports.validateOnClient) { %>
10
+ import validateOnClient from '<%= template.imports.validateOnClient %>';
11
11
  <% } else { %>
12
12
  const validateOnClient = undefined;
13
13
  <% } %>
14
14
  type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
15
- const apiRoot = '<%= apiRoot %>';
15
+ const apiRoot = '<%= template.apiRoot %>';
16
16
 
17
- <% segments.forEach((segment, i) => { %>
18
- <% Object.keys(segmentsSchema[segment.segmentName].controllers).forEach((key) => { %>
17
+ <% template.segments.forEach((segment, i) => { %>
18
+ <% Object.keys(template.fullSchema[segment.segmentName].controllers).forEach((key) => { %>
19
19
  export const <%= key %> = createRPC<Controllers<%= i %>["<%= key %>"], Options>(
20
20
  schema['<%= segment.segmentName %>'].controllers.<%= key %>,
21
21
  '<%= segment.segmentName %>',
@@ -9,7 +9,6 @@ import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
9
9
  export default async function ensureSchemaFiles(projectInfo, schemaOutAbsolutePath, segmentNames) {
10
10
  const now = Date.now();
11
11
  let hasChanged = false;
12
- // Create index.js file
13
12
  const indexContent = `// auto-generated
14
13
  ${segmentNames
15
14
  .map((segmentName) => {
@@ -18,10 +17,10 @@ ${segmentNames
18
17
  .join('\n')}`;
19
18
  const dTsContent = `// auto-generated
20
19
  import type { VovkSchema } from 'vovk';
21
- declare const segmentSchema: {
22
- ${segmentNames.map((segmentName) => ` '${segmentName}': VovkSchema;`).join('\n')}
20
+ declare const fullSchema: {
21
+ ${segmentNames.map((segmentName) => ` '${segmentName}': import('${segmentName}.json');`).join('\n')}
23
22
  };
24
- export default segmentSchema;`;
23
+ export default fullSchema;`;
25
24
  const jsAbsolutePath = path.join(schemaOutAbsolutePath, 'index.cjs');
26
25
  const dTsAbsolutePath = path.join(schemaOutAbsolutePath, 'index.d.cts');
27
26
  const existingJs = await fs.readFile(jsAbsolutePath, 'utf-8').catch(() => null);
@@ -150,6 +150,7 @@ export class VovkDev {
150
150
  }
151
151
  else {
152
152
  log.info('Config file has been updated');
153
+ this.#generate();
153
154
  }
154
155
  isInitial = false;
155
156
  }, 1000);
@@ -347,7 +348,7 @@ export class VovkDev {
347
348
  });
348
349
  }
349
350
  else {
350
- log.info(`Ready in ${Date.now() - now}ms. Making initial requests for schemas in a moment...`);
351
+ log.info(`Ready in ${Date.now() - now}ms. Making requests for schemas in a moment...`);
351
352
  }
352
353
  }
353
354
  }
@@ -29,23 +29,23 @@ export default function logDiffResult(segmentName, diffResult, projectInfo) {
29
29
  case 'controller':
30
30
  switch (diffNormalizedItem.type) {
31
31
  case 'added':
32
- projectInfo.log.info(`Schema forn RPC ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
32
+ projectInfo.log.info(`Schema for RPC ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
33
33
  break;
34
34
  case 'removed':
35
- projectInfo.log.info(`Schema forn RPC ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
35
+ projectInfo.log.info(`Schema for RPC ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
36
36
  break;
37
37
  }
38
38
  break;
39
39
  case 'controllerHandler':
40
40
  switch (diffNormalizedItem.type) {
41
41
  case 'added':
42
- projectInfo.log.info(`Schema forn RPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
42
+ projectInfo.log.info(`Schema for RPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${addedText} at ${formatLoggedSegmentName(segmentName)}`);
43
43
  break;
44
44
  case 'removed':
45
- projectInfo.log.info(`Schema forn RPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
45
+ projectInfo.log.info(`Schema for RPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${removedText} from ${formatLoggedSegmentName(segmentName)}`);
46
46
  break;
47
47
  case 'changed':
48
- projectInfo.log.info(`Schema forn RPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${changedText} at ${formatLoggedSegmentName(segmentName)}`);
48
+ projectInfo.log.info(`Schema for RPC method ${chalkHighlightThing(diffNormalizedItem.name)} has been ${changedText} at ${formatLoggedSegmentName(segmentName)}`);
49
49
  break;
50
50
  }
51
51
  break;
@@ -7,7 +7,7 @@ import getClientTemplates from './getClientTemplates.mjs';
7
7
  export default async function generate({ projectInfo, segments, segmentsSchema, forceNothingWrittenLog, templates, prettify: prettifyClient, fullSchema, }) {
8
8
  templates = templates ?? projectInfo.config.experimental_clientGenerateTemplateNames;
9
9
  const noClient = templates?.[0] === 'none';
10
- const { config, cwd, log, validateOnClientImportPath, apiRoot, fetcherClientImportPath, createRPCImportPath, schemaOutImportPath, } = projectInfo;
10
+ const { config, cwd, log, clientImports, apiRoot } = projectInfo;
11
11
  const { clientOutDirAbsolutePath, templateFiles } = getClientTemplates({ config, cwd, templateNames: templates });
12
12
  // Ensure that each segment has a matching schema if it needs to be emitted:
13
13
  for (let i = 0; i < segments.length; i++) {
@@ -21,14 +21,11 @@ export default async function generate({ projectInfo, segments, segmentsSchema,
21
21
  }
22
22
  const now = Date.now();
23
23
  // Data for the EJS templates:
24
- const ejsData = {
24
+ const template = {
25
25
  apiRoot,
26
- fetcherClientImportPath,
27
- schemaOutImportPath,
28
- validateOnClientImportPath,
29
- createRPCImportPath,
26
+ imports: clientImports,
30
27
  segments,
31
- segmentsSchema,
28
+ fullSchema: segmentsSchema,
32
29
  };
33
30
  // Process each template in parallel
34
31
  const processedTemplates = noClient
@@ -37,7 +34,11 @@ export default async function generate({ projectInfo, segments, segmentsSchema,
37
34
  // Read the EJS template
38
35
  const templateContent = await fs.readFile(templatePath, 'utf-8');
39
36
  // Render the template
40
- let rendered = templatePath.endsWith('.ejs') ? ejs.render(templateContent, ejsData) : templateContent;
37
+ let rendered = templatePath.endsWith('.ejs')
38
+ ? ejs.render(templateContent, { template }, {
39
+ filename: templatePath,
40
+ })
41
+ : templateContent;
41
42
  // Optionally prettify
42
43
  if (prettifyClient || config.prettifyClient) {
43
44
  rendered = await prettify(rendered, outPath);
@@ -1,11 +1,11 @@
1
- import type { VovkConfig } from '../types.mjs';
1
+ import type { VovkStrictConfig } from '../types.mjs';
2
2
  export default function getConfig({ clientOutDir, cwd }: {
3
3
  clientOutDir?: string;
4
4
  cwd: string;
5
5
  }): Promise<{
6
- config: Required<VovkConfig>;
6
+ config: VovkStrictConfig;
7
7
  srcRoot: string;
8
8
  configAbsolutePaths: string[];
9
- userConfig: VovkConfig | null;
9
+ userConfig: import("../types.mjs").VovkConfig | null;
10
10
  error: Error | undefined;
11
11
  }>;
@@ -5,11 +5,14 @@ export default async function getConfig({ clientOutDir, cwd }) {
5
5
  const { configAbsolutePaths, error, userConfig } = await getUserConfig({ cwd });
6
6
  const conf = userConfig ?? {};
7
7
  const srcRoot = await getRelativeSrcRoot({ cwd });
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';
8
11
  const config = {
9
12
  modulesDir: env.VOVK_MODULES_DIR ?? conf.modulesDir ?? './' + [srcRoot, 'modules'].filter(Boolean).join('/'),
10
- validateOnClientPath: env.VOVK_VALIDATE_ON_CLIENT_PATH ?? conf.validateOnClientPath ?? null,
11
- fetcherPath: env.VOVK_FETCHER_PATH ?? conf.fetcherPath ?? 'vovk/dist/client/defaultFetcher.js',
12
- createRPCPath: env.VOVK_CREATE_RPC_PATH ?? conf.createRPCPath ?? 'vovk/dist/client/createRPC.js',
13
+ validateOnClientImport: typeof validateOnClientImport === 'string' ? [validateOnClientImport] : validateOnClientImport,
14
+ fetcherImport: typeof fetcherImport === 'string' ? [fetcherImport] : fetcherImport,
15
+ createRPCImport: typeof createRPCImport === 'string' ? [createRPCImport] : createRPCImport,
13
16
  schemaOutDir: env.VOVK_SCHEMA_OUT_DIR ?? conf.schemaOutDir ?? './.vovk-schema',
14
17
  clientOutDir: clientOutDir ?? env.VOVK_CLIENT_OUT_DIR ?? conf.clientOutDir ?? './node_modules/.vovk-client',
15
18
  origin: (env.VOVK_ORIGIN ?? conf.origin ?? '').replace(/\/$/, ''), // Remove trailing slash
@@ -9,11 +9,19 @@ export default function getProjectInfo({ port: givenPort, clientOutDir, cwd, }?:
9
9
  apiRoot: string;
10
10
  apiDir: string;
11
11
  srcRoot: string;
12
- schemaOutImportPath: string;
13
- fetcherClientImportPath: string;
14
- createRPCImportPath: string;
15
- validateOnClientImportPath: string | null;
16
- config: Required<import("../types.mjs").VovkConfig>;
12
+ config: import("../types.mjs").VovkStrictConfig;
13
+ clientImports: {
14
+ schema: string;
15
+ fetcher: string;
16
+ createRPC: string;
17
+ validateOnClient: string | null;
18
+ module: {
19
+ schema: string;
20
+ fetcher: string;
21
+ createRPC: string;
22
+ validateOnClient: string | null;
23
+ };
24
+ };
17
25
  log: {
18
26
  info: (msg: string) => void;
19
27
  warn: (msg: string) => void;
@@ -10,15 +10,6 @@ export default async function getProjectInfo({ port: givenPort, clientOutDir, cw
10
10
  const apiDir = path.join(srcRoot, 'app', config.rootEntry);
11
11
  const schemaOutImportPath = path.relative(config.clientOutDir, config.schemaOutDir).replace(/\\/g, '/') + // windows fix
12
12
  '/index.cjs';
13
- const fetcherClientImportPath = config.fetcherPath.startsWith('.')
14
- ? path.relative(config.clientOutDir, config.fetcherPath)
15
- : config.fetcherPath;
16
- const createRPCImportPath = config.createRPCPath.startsWith('.')
17
- ? path.relative(config.clientOutDir, config.createRPCPath)
18
- : config.createRPCPath;
19
- const validateOnClientImportPath = config.validateOnClientPath?.startsWith('.')
20
- ? path.relative(config.clientOutDir, config.validateOnClientPath)
21
- : config.validateOnClientPath;
22
13
  const log = getLogger(config.logLevel);
23
14
  if (configAbsolutePaths.length > 1) {
24
15
  log.warn(`Multiple config files found. Using the first one: ${configAbsolutePaths[0]}`);
@@ -26,17 +17,29 @@ export default async function getProjectInfo({ port: givenPort, clientOutDir, cw
26
17
  if (!userConfig && configAbsolutePaths.length > 0) {
27
18
  log.error(`Error reading config file at ${configAbsolutePaths[0]}: ${error?.message ?? 'Unknown Error'}`);
28
19
  }
20
+ const getImportPath = (p) => (p.startsWith('.') ? path.relative(config.clientOutDir, p) : p);
21
+ const clientImports = {
22
+ schema: schemaOutImportPath,
23
+ fetcher: getImportPath(config.fetcherImport[0]),
24
+ createRPC: getImportPath(config.createRPCImport[0]),
25
+ validateOnClient: config.validateOnClientImport ? getImportPath(config.validateOnClientImport[0]) : null,
26
+ module: {
27
+ schema: schemaOutImportPath,
28
+ fetcher: getImportPath(config.fetcherImport[1] ?? config.fetcherImport[0]),
29
+ createRPC: getImportPath(config.createRPCImport[1] ?? config.createRPCImport[0]),
30
+ validateOnClient: config.validateOnClientImport
31
+ ? getImportPath(config.validateOnClientImport[1] ?? config.validateOnClientImport[0])
32
+ : null,
33
+ },
34
+ };
29
35
  return {
30
36
  cwd,
31
37
  port,
32
38
  apiRoot,
33
39
  apiDir,
34
40
  srcRoot,
35
- schemaOutImportPath,
36
- fetcherClientImportPath,
37
- createRPCImportPath,
38
- validateOnClientImportPath,
39
41
  config,
42
+ clientImports,
40
43
  log,
41
44
  };
42
45
  }
package/dist/index.mjs CHANGED
@@ -20,10 +20,10 @@ program
20
20
  .command('dev')
21
21
  .alias('d')
22
22
  .description('start schema watcher (optional flag --next-dev to start it with Next.js)')
23
+ .argument('[nextArgs...]', 'extra arguments for the dev command')
23
24
  .option('--next-dev', 'start schema watcher and Next.js with automatic port allocation')
24
25
  .option('--exit', 'kill the processe when schema and client is generated')
25
- .allowUnknownOption(true)
26
- .action(async (options, command) => {
26
+ .action(async (nextArgs, options) => {
27
27
  const { nextDev, exit = false } = options;
28
28
  const portAttempts = 30;
29
29
  const PORT = !nextDev
@@ -40,7 +40,7 @@ program
40
40
  if (nextDev) {
41
41
  const { result } = concurrently([
42
42
  {
43
- command: `npx next dev ${command.args.join(' ')}`,
43
+ command: `npx next dev ${nextArgs.join(' ')}`,
44
44
  name: 'Next.js Development Server',
45
45
  env: { PORT },
46
46
  },
@@ -72,7 +72,7 @@ program
72
72
  program
73
73
  .command('generate')
74
74
  .alias('g')
75
- .description('Generate client')
75
+ .description('Generate RPC client from schema')
76
76
  .option('--out, --client-out-dir <path>', 'path to output directory')
77
77
  .option('--template, --templates <templates...>', 'client code templates ("ts", "compiled", "python", "none", a custom path)')
78
78
  .option('--full-schema [fileName]', 'generate client with full schema')
@@ -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, channel, dryRun }, }: {
3
+ export default function createConfig({ root, log, options: { validationLibrary, validateOnClient, reactQuery, channel, dryRun }, }: {
4
4
  root: string;
5
5
  log: ReturnType<typeof getLogger>;
6
- options: Pick<InitOptions, 'validationLibrary' | 'validateOnClient' | 'channel' | 'dryRun'>;
6
+ options: Pick<InitOptions, 'validationLibrary' | 'validateOnClient' | '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, channel, dryRun }, }) {
6
+ export default async function createConfig({ root, log, options: { validationLibrary, validateOnClient, 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;
@@ -17,7 +17,7 @@ export default async function createConfig({ root, log, options: { validationLib
17
17
  };
18
18
  if (validationLibrary) {
19
19
  if (validateOnClient) {
20
- config.validateOnClientPath = `${validationLibrary}/validateOnClient/index.js`;
20
+ config.validateOnClientImport = `${validationLibrary}/validateOnClient.js`;
21
21
  }
22
22
  try {
23
23
  const validationTemplates = await getTemplateFilesFromPackage(validationLibrary, channel);
@@ -27,6 +27,9 @@ export default async function createConfig({ root, log, options: { validationLib
27
27
  log.warn(`Failed to fetch validation library templates: ${error.message}`);
28
28
  }
29
29
  }
30
+ if (reactQuery) {
31
+ config.createRPCImport = 'vovk-react-query';
32
+ }
30
33
  config.templates = templates;
31
34
  const configStr = await prettify(`/** @type {import('vovk-cli').VovkConfig} */
32
35
  const config = ${JSON.stringify(config, null, 2)};
@@ -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, dryRun, channel, }: InitOptions): Promise<void>;
7
+ main(prefix: string, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, reactQuery, dryRun, channel, }: InitOptions): Promise<void>;
8
8
  }
@@ -17,10 +17,10 @@ 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, dryRun, channel, }) {
20
+ async #init({ configPaths, pkgJson, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, reactQuery, dryRun, channel, }) {
21
21
  const { log, root } = this;
22
22
  const dependencies = ['vovk', 'vovk-client'];
23
- const devDependencies = ['vovk-cli'];
23
+ const devDependencies = ['vovk-cli', 'openapi3-ts'];
24
24
  // delete older config files
25
25
  if (configPaths.length) {
26
26
  await Promise.all(configPaths.map((configPath) => fs.rm(configPath)));
@@ -34,6 +34,10 @@ export class Init {
34
34
  'vovk-dto': ['class-validator', 'class-transformer', 'vovk-mapped-types', 'reflect-metadata'],
35
35
  }[validationLibrary] ?? []));
36
36
  }
37
+ if (reactQuery) {
38
+ dependencies.push('vovk-react-query');
39
+ dependencies.push('@tanstack/react-query');
40
+ }
37
41
  if (updateScripts) {
38
42
  try {
39
43
  if (!dryRun)
@@ -102,7 +106,7 @@ export class Init {
102
106
  const { configAbsolutePath } = await createConfig({
103
107
  root,
104
108
  log,
105
- options: { validationLibrary, validateOnClient, channel, dryRun },
109
+ options: { validationLibrary, validateOnClient, reactQuery, channel, dryRun },
106
110
  });
107
111
  log.info('Config created successfully at ' + configAbsolutePath);
108
112
  }
@@ -110,7 +114,7 @@ export class Init {
110
114
  log.error(`Failed to create config: ${error.message}`);
111
115
  }
112
116
  }
113
- async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, dryRun, channel, }) {
117
+ async main(prefix, { yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, validateOnClient, reactQuery, dryRun, channel, }) {
114
118
  const cwd = process.cwd();
115
119
  const root = path.resolve(cwd, prefix);
116
120
  const log = getLogger(logLevel);
@@ -129,6 +133,7 @@ export class Init {
129
133
  updateScripts: updateScripts ?? 'implicit',
130
134
  validationLibrary: validationLibrary?.toLocaleLowerCase() === 'none' ? null : (validationLibrary ?? 'vovk-zod'),
131
135
  validateOnClient: validateOnClient ?? true,
136
+ reactQuery: reactQuery ?? true,
132
137
  dryRun: dryRun ?? false,
133
138
  channel: channel ?? 'latest',
134
139
  });
@@ -181,7 +186,7 @@ export class Init {
181
186
  updateScripts =
182
187
  updateScripts ??
183
188
  (await select({
184
- message: 'Do you want to update package.json by adding "generate" and "dev" NPM scripts?',
189
+ message: 'Do you want to update package.json by adding "generate" and updating "dev" NPM scripts?',
185
190
  default: 'implicit',
186
191
  choices: [
187
192
  {
@@ -201,17 +206,22 @@ export class Init {
201
206
  },
202
207
  ],
203
208
  }));
209
+ reactQuery =
210
+ reactQuery ??
211
+ (await confirm({
212
+ message: 'Do you want to use @tanstack/react-query for data fetching at React components?',
213
+ }));
204
214
  if (typeof updateTsConfig === 'undefined') {
205
215
  let shouldAsk = false;
206
216
  try {
207
217
  shouldAsk = !(await checkTSConfigForExperimentalDecorators(root));
208
218
  }
209
219
  catch (error) {
210
- log.error(`Failed to check tsconfig.json for experimentalDecorators: ${error.message}`);
220
+ log.error(`Failed to check tsconfig.json for "experimentalDecorators": ${error.message}`);
211
221
  }
212
222
  if (shouldAsk) {
213
223
  updateTsConfig = await confirm({
214
- message: 'Do you want to add experimentalDecorators to tsconfig.json?',
224
+ message: 'Do you want to add "experimentalDecorators" option to tsconfig.json?',
215
225
  });
216
226
  }
217
227
  }
@@ -225,6 +235,7 @@ export class Init {
225
235
  updateScripts,
226
236
  validationLibrary,
227
237
  validateOnClient,
238
+ reactQuery,
228
239
  dryRun,
229
240
  channel,
230
241
  });
@@ -2,20 +2,21 @@ import { Init } from './init/index.mjs';
2
2
  // reused at vovk-init
3
3
  export default function initProgram(program) {
4
4
  return program
5
- .argument('[prefix]', 'Directory to initialize project in', '.')
6
- .description('Initialize Vovk project')
7
- .option('-y, --yes', 'Skip all prompts and use default values')
8
- .option('--log-level <level>', 'Set log level', 'info')
9
- .option('--use-npm', 'Use npm as package manager')
10
- .option('--use-yarn', 'Use yarn as package manager')
11
- .option('--use-pnpm', 'Use pnpm as package manager')
12
- .option('--use-bun', 'Use bun as package manager')
13
- .option('--skip-install', 'Skip installing dependencies')
14
- .option('--update-ts-config', 'Update tsconfig.json')
15
- .option('--update-scripts <mode>', 'Update package.json scripts ("implicit" or "explicit")')
16
- .option('--validation-library <library>', 'Validation library to use ("vovk-zod", "vovk-yup", "vovk-dto" or another). Set to "none" to skip validation')
17
- .option('--validate-on-client', 'Path to validateOnClient file')
18
- .option('--channel <channel>', 'Channel to use for fetching packages', 'latest')
19
- .option('--dry-run', 'Do not write files to disk')
5
+ .argument('[prefix]', 'directory to initialize project in', '.')
6
+ .description('Initialize Vovk.ts project')
7
+ .option('-y, --yes', 'skip all prompts and use default values')
8
+ .option('--log-level <level>', 'set log level', 'info')
9
+ .option('--use-npm', 'use npm as package manager')
10
+ .option('--use-yarn', 'use yarn as package manager')
11
+ .option('--use-pnpm', 'use pnpm as package manager')
12
+ .option('--use-bun', 'use bun as package manager')
13
+ .option('--skip-install', 'skip installing dependencies')
14
+ .option('--update-ts-config', 'update tsconfig.json')
15
+ .option('--update-scripts <mode>', 'update package.json scripts ("implicit" or "explicit")')
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
+ .option('--react-query', 'use @tanstack/react-query for data fetching inside components')
19
+ .option('--channel <channel>', 'channel to use for fetching packages', 'latest')
20
+ .option('--dry-run', 'do not write files to disk')
20
21
  .action((prefix = '.', options) => new Init().main(prefix, options));
21
22
  }
@@ -5,6 +5,7 @@ import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
5
5
  import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
6
6
  import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
7
7
  import prettify from '../utils/prettify.mjs';
8
+ import chalk from 'chalk';
8
9
  export default async function newSegment({ segmentName, overwrite, dryRun, }) {
9
10
  const { apiDir, cwd, log } = await getProjectInfo();
10
11
  const absoluteSegmentRoutePath = path.join(cwd, apiDir, segmentName, '[[...vovk]]/route.ts');
@@ -29,5 +30,6 @@ ${segmentName ? ` segmentName: '${segmentName}',\n` : ''} emitSchema: true,
29
30
  await fs.writeFile(absoluteSegmentRoutePath, code);
30
31
  }
31
32
  log.info(`${formatLoggedSegmentName(segmentName, { upperFirst: true })} created at ${absoluteSegmentRoutePath}.`);
32
- log.info(`Run ${chalkHighlightThing(`npx vovk new service controller ${[segmentName, 'thing'].filter(Boolean).join('/')}`)} to create a new controller with a service at /modules/thing/ folder.`);
33
+ const dir = chalk.cyanBright([segmentName, 'thing'].filter(Boolean).join('/'));
34
+ log.info(`Run ${chalkHighlightThing(`npx vovk new service controller ${dir}`)} to create a new controller with a service at modules/${dir} folder for this segment`);
33
35
  }
package/dist/types.d.mts CHANGED
@@ -21,9 +21,9 @@ export type VovkEnv = {
21
21
  export type VovkConfig = {
22
22
  clientOutDir?: string;
23
23
  schemaOutDir?: string;
24
- fetcherPath?: string;
25
- validateOnClientPath?: string | null;
26
- createRPCPath?: string;
24
+ fetcherImport?: string | string[];
25
+ validateOnClientImport?: string | string[] | null;
26
+ createRPCImport?: string | string[];
27
27
  modulesDir?: string;
28
28
  rootEntry?: string;
29
29
  origin?: string;
@@ -38,6 +38,11 @@ export type VovkConfig = {
38
38
  [key: string]: string | undefined;
39
39
  };
40
40
  };
41
+ export type VovkStrictConfig = Required<Omit<VovkConfig, 'validateOnClientImport' | 'fetcherImport' | 'createRPCImport'>> & {
42
+ validateOnClientImport: string[] | null;
43
+ fetcherImport: string[];
44
+ createRPCImport: string[];
45
+ };
41
46
  export type VovkModuleRenderResult = {
42
47
  fileName: string;
43
48
  dir: string;
@@ -66,6 +71,7 @@ export interface InitOptions {
66
71
  updateTsConfig?: boolean;
67
72
  updateScripts?: 'implicit' | 'explicit';
68
73
  validationLibrary?: string | null;
74
+ reactQuery?: boolean;
69
75
  validateOnClient?: boolean;
70
76
  dryRun?: boolean;
71
77
  channel?: 'latest' | 'beta' | 'draft';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vovk-cli",
3
- "version": "0.0.1-draft.65",
3
+ "version": "0.0.1-draft.67",
4
4
  "bin": {
5
5
  "vovk": "./dist/index.mjs"
6
6
  },
@@ -36,14 +36,14 @@
36
36
  },
37
37
  "homepage": "https://vovk.dev",
38
38
  "peerDependencies": {
39
- "vovk": "^3.0.0-draft.65"
39
+ "vovk": "^3.0.0-draft.66"
40
40
  },
41
41
  "dependencies": {
42
42
  "@inquirer/prompts": "^7.3.1",
43
43
  "@npmcli/package-json": "^6.1.1",
44
44
  "chalk": "^5.4.1",
45
45
  "chokidar": "^4.0.3",
46
- "commander": "^12.1.0",
46
+ "commander": "^13.1.0",
47
47
  "concurrently": "^9.1.2",
48
48
  "dotenv": "^16.4.7",
49
49
  "ejs": "^3.1.10",