vovk-cli 0.0.1-draft.36 → 0.0.1-draft.360

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 (142) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +29 -1
  3. package/client-templates/cjs/index.cjs.ejs +19 -0
  4. package/client-templates/cjs/index.d.cts.ejs +22 -0
  5. package/client-templates/mixins/mixins.d.ts.ejs +64 -0
  6. package/client-templates/mixins/mixins.json.ejs +1 -0
  7. package/client-templates/mjs/index.d.mts.ejs +22 -0
  8. package/client-templates/mjs/index.mjs.ejs +18 -0
  9. package/client-templates/openapiCjs/openapi.cjs.ejs +4 -0
  10. package/client-templates/openapiCjs/openapi.d.cts.ejs +4 -0
  11. package/client-templates/openapiJson/openapi.json.ejs +1 -0
  12. package/client-templates/openapiTs/openapi.ts.ejs +4 -0
  13. package/client-templates/packageJson/package.json.ejs +1 -0
  14. package/client-templates/readme/README.md.ejs +39 -0
  15. package/client-templates/schemaCjs/schema.cjs.ejs +24 -0
  16. package/client-templates/schemaCjs/schema.d.cts.ejs +10 -0
  17. package/client-templates/schemaJson/schema.json.ejs +1 -0
  18. package/client-templates/schemaTs/schema.ts.ejs +28 -0
  19. package/client-templates/ts/index.ts.ejs +27 -0
  20. package/dist/bundle/index.d.mts +8 -0
  21. package/dist/bundle/index.mjs +103 -0
  22. package/dist/dev/diffSegmentSchema.d.mts +36 -0
  23. package/dist/dev/{diffSchema.mjs → diffSegmentSchema.mjs} +3 -11
  24. package/dist/dev/ensureSchemaFiles.d.mts +4 -1
  25. package/dist/dev/ensureSchemaFiles.mjs +15 -31
  26. package/dist/dev/index.d.mts +5 -1
  27. package/dist/dev/index.mjs +191 -80
  28. package/dist/dev/logDiffResult.d.mts +1 -1
  29. package/dist/dev/logDiffResult.mjs +6 -43
  30. package/dist/dev/writeMetaJson.d.mts +2 -0
  31. package/dist/dev/writeMetaJson.mjs +20 -0
  32. package/dist/dev/writeOneSegmentSchemaFile.d.mts +12 -0
  33. package/dist/dev/{writeOneSchemaFile.mjs → writeOneSegmentSchemaFile.mjs} +10 -6
  34. package/dist/generate/ensureClient.d.mts +3 -0
  35. package/dist/generate/ensureClient.mjs +28 -0
  36. package/dist/generate/generate.d.mts +13 -0
  37. package/dist/generate/generate.mjs +306 -0
  38. package/dist/generate/getClientTemplateFiles.d.mts +20 -0
  39. package/dist/generate/getClientTemplateFiles.mjs +85 -0
  40. package/dist/generate/getProjectFullSchema.d.mts +8 -0
  41. package/dist/generate/getProjectFullSchema.mjs +66 -0
  42. package/dist/generate/getTemplateClientImports.d.mts +19 -0
  43. package/dist/generate/getTemplateClientImports.mjs +49 -0
  44. package/dist/generate/index.d.mts +33 -0
  45. package/dist/generate/index.mjs +186 -0
  46. package/dist/generate/writeOneClientFile.d.mts +42 -0
  47. package/dist/generate/writeOneClientFile.mjs +139 -0
  48. package/dist/getProjectInfo/getConfig/getConfigAbsolutePaths.d.mts +5 -0
  49. package/dist/getProjectInfo/{getConfigAbsolutePaths.mjs → getConfig/getConfigAbsolutePaths.mjs} +4 -1
  50. package/dist/getProjectInfo/{getRelativeSrcRoot.d.mts → getConfig/getRelativeSrcRoot.d.mts} +1 -1
  51. package/dist/getProjectInfo/{getRelativeSrcRoot.mjs → getConfig/getRelativeSrcRoot.mjs} +2 -2
  52. package/dist/getProjectInfo/getConfig/getTemplateDefs.d.mts +24 -0
  53. package/dist/getProjectInfo/getConfig/getTemplateDefs.mjs +165 -0
  54. package/dist/getProjectInfo/{getUserConfig.d.mts → getConfig/getUserConfig.d.mts} +3 -2
  55. package/dist/getProjectInfo/{getUserConfig.mjs → getConfig/getUserConfig.mjs} +3 -3
  56. package/dist/getProjectInfo/{importUncachedModule.mjs → getConfig/importUncachedModule.mjs} +1 -4
  57. package/dist/getProjectInfo/getConfig/index.d.mts +78 -0
  58. package/dist/getProjectInfo/getConfig/index.mjs +91 -0
  59. package/dist/getProjectInfo/getMetaSchema.d.mts +8 -0
  60. package/dist/getProjectInfo/getMetaSchema.mjs +14 -0
  61. package/dist/getProjectInfo/index.d.mts +14 -9
  62. package/dist/getProjectInfo/index.mjs +24 -22
  63. package/dist/index.d.mts +2 -2
  64. package/dist/index.mjs +118 -36
  65. package/dist/init/createConfig.d.mts +2 -2
  66. package/dist/init/createConfig.mjs +39 -13
  67. package/dist/init/createStandardSchemaValidatorFile.d.mts +4 -0
  68. package/dist/init/createStandardSchemaValidatorFile.mjs +39 -0
  69. package/dist/init/getTemplateFilesFromPackage.mjs +10 -5
  70. package/dist/init/index.d.mts +2 -2
  71. package/dist/init/index.mjs +122 -72
  72. package/dist/init/installDependencies.mjs +4 -2
  73. package/dist/init/logUpdateDependenciesError.d.mts +3 -1
  74. package/dist/init/logUpdateDependenciesError.mjs +7 -1
  75. package/dist/init/updateDependenciesWithoutInstalling.mjs +39 -9
  76. package/dist/init/updateNPMScripts.d.mts +3 -1
  77. package/dist/init/updateNPMScripts.mjs +10 -7
  78. package/dist/init/updateTypeScriptConfig.d.mts +4 -1
  79. package/dist/init/updateTypeScriptConfig.mjs +11 -7
  80. package/dist/initProgram.d.mts +1 -1
  81. package/dist/initProgram.mjs +17 -17
  82. package/dist/locateSegments.d.mts +8 -1
  83. package/dist/locateSegments.mjs +13 -3
  84. package/dist/new/addClassToSegmentCode.d.mts +1 -2
  85. package/dist/new/addClassToSegmentCode.mjs +3 -3
  86. package/dist/new/index.d.mts +2 -1
  87. package/dist/new/index.mjs +4 -2
  88. package/dist/new/newModule.d.mts +4 -1
  89. package/dist/new/newModule.mjs +18 -17
  90. package/dist/new/newSegment.d.mts +4 -1
  91. package/dist/new/newSegment.mjs +19 -11
  92. package/dist/new/render.d.mts +7 -3
  93. package/dist/new/render.mjs +29 -8
  94. package/dist/types.d.mts +64 -42
  95. package/dist/utils/compileJSONSchemaToTypeScriptType.d.mts +5 -0
  96. package/dist/utils/compileJSONSchemaToTypeScriptType.mjs +9 -0
  97. package/dist/utils/compileTs.d.mts +12 -0
  98. package/dist/utils/compileTs.mjs +261 -0
  99. package/dist/utils/debounceWithArgs.d.mts +2 -2
  100. package/dist/utils/debounceWithArgs.mjs +24 -6
  101. package/dist/utils/deepExtend.d.mts +54 -0
  102. package/dist/utils/deepExtend.mjs +129 -0
  103. package/dist/utils/formatLoggedSegmentName.d.mts +3 -1
  104. package/dist/utils/formatLoggedSegmentName.mjs +3 -2
  105. package/dist/utils/generateFnName.d.mts +23 -0
  106. package/dist/utils/generateFnName.mjs +76 -0
  107. package/dist/utils/getPackageJson.d.mts +3 -0
  108. package/dist/utils/getPackageJson.mjs +22 -0
  109. package/dist/utils/getPublicModuleNameFromPath.d.mts +4 -0
  110. package/dist/utils/getPublicModuleNameFromPath.mjs +9 -0
  111. package/dist/utils/normalizeOpenAPIMixin.d.mts +14 -0
  112. package/dist/utils/normalizeOpenAPIMixin.mjs +114 -0
  113. package/dist/utils/pickSegmentFullSchema.d.mts +3 -0
  114. package/dist/utils/pickSegmentFullSchema.mjs +15 -0
  115. package/dist/utils/removeUnlistedDirectories.d.mts +10 -0
  116. package/dist/utils/removeUnlistedDirectories.mjs +61 -0
  117. package/dist/utils/resolveAbsoluteModulePath.d.mts +2 -0
  118. package/dist/utils/resolveAbsoluteModulePath.mjs +32 -0
  119. package/module-templates/arktype/controller.ts.ejs +68 -0
  120. package/module-templates/type/controller.ts.ejs +56 -0
  121. package/module-templates/type/service.ts.ejs +28 -0
  122. package/module-templates/valibot/controller.ts.ejs +68 -0
  123. package/package.json +40 -22
  124. package/dist/dev/diffSchema.d.mts +0 -43
  125. package/dist/dev/ensureClient.d.mts +0 -5
  126. package/dist/dev/ensureClient.mjs +0 -31
  127. package/dist/dev/isMetadataEmpty.d.mts +0 -2
  128. package/dist/dev/isMetadataEmpty.mjs +0 -4
  129. package/dist/dev/writeOneSchemaFile.d.mts +0 -11
  130. package/dist/generateClient.d.mts +0 -7
  131. package/dist/generateClient.mjs +0 -97
  132. package/dist/getProjectInfo/getConfig.d.mts +0 -11
  133. package/dist/getProjectInfo/getConfig.mjs +0 -29
  134. package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +0 -4
  135. package/dist/postinstall.d.mts +0 -1
  136. package/dist/postinstall.mjs +0 -24
  137. package/templates/controller.ejs +0 -52
  138. package/templates/service.ejs +0 -27
  139. package/templates/worker.ejs +0 -24
  140. /package/dist/getProjectInfo/{importUncachedModule.d.mts → getConfig/importUncachedModule.d.mts} +0 -0
  141. /package/dist/getProjectInfo/{importUncachedModuleWorker.d.mts → getConfig/importUncachedModuleWorker.d.mts} +0 -0
  142. /package/dist/getProjectInfo/{importUncachedModuleWorker.mjs → getConfig/importUncachedModuleWorker.mjs} +0 -0
@@ -0,0 +1,78 @@
1
+ import { VovkSchemaIdEnum, type VovkStrictConfig } from 'vovk';
2
+ import type { LogLevelNames } from 'loglevel';
3
+ export default function getConfig({ configPath, cwd, logLevel, }: {
4
+ configPath?: string;
5
+ cwd: string;
6
+ logLevel?: LogLevelNames;
7
+ }): Promise<{
8
+ config: VovkStrictConfig;
9
+ srcRoot: string | null;
10
+ configAbsolutePaths: string[];
11
+ userConfig: {
12
+ $schema?: typeof VovkSchemaIdEnum.CONFIG | (string & {});
13
+ emitConfig?: boolean | (keyof VovkStrictConfig | (string & {}))[];
14
+ schemaOutDir?: string;
15
+ modulesDir?: string;
16
+ rootEntry?: string;
17
+ logLevel?: "error" | "trace" | "debug" | "info" | "warn" | (string & {});
18
+ libs?: {
19
+ ajv: import("vovk").KnownAny;
20
+ [key: string]: import("vovk").KnownAny;
21
+ };
22
+ devHttps?: boolean;
23
+ composedClient?: {
24
+ enabled?: boolean;
25
+ outDir?: string;
26
+ fromTemplates?: string[];
27
+ prettifyClient?: boolean;
28
+ } & ({
29
+ excludeSegments?: never;
30
+ includeSegments?: string[];
31
+ } | {
32
+ excludeSegments?: string[];
33
+ includeSegments?: never;
34
+ });
35
+ segmentedClient?: {
36
+ enabled?: boolean;
37
+ outDir?: string;
38
+ fromTemplates?: string[];
39
+ prettifyClient?: boolean;
40
+ } & ({
41
+ excludeSegments?: never;
42
+ includeSegments?: string[];
43
+ } | {
44
+ excludeSegments?: string[];
45
+ includeSegments?: never;
46
+ });
47
+ bundle?: {
48
+ requires?: Record<string, string>;
49
+ prebundleOutDir?: string;
50
+ keepPrebundleDir?: boolean;
51
+ tsdownBuildOptions?: Parameters<typeof import("tsdown/index.mjs").build>[0];
52
+ generatorConfig?: import("vovk").VovkGeneratorConfig;
53
+ } & ({
54
+ excludeSegments?: never;
55
+ includeSegments?: string[];
56
+ } | {
57
+ excludeSegments?: string[];
58
+ includeSegments?: never;
59
+ });
60
+ clientTemplateDefs?: Record<string, import("vovk/types").ClientTemplateDef>;
61
+ rootSegmentModulesDirName?: string;
62
+ moduleTemplates?: {
63
+ service?: string;
64
+ controller?: string;
65
+ [key: string]: string | undefined;
66
+ };
67
+ generatorConfig?: import("vovk").VovkGeneratorConfig & {
68
+ segments?: Record<string, import("vovk/types").VovkSegmentConfig>;
69
+ };
70
+ } | null;
71
+ log: {
72
+ info: (msg: string) => void;
73
+ warn: (msg: string) => void;
74
+ error: (msg: string) => void;
75
+ debug: (msg: string) => void;
76
+ raw: import("loglevel").RootLogger;
77
+ };
78
+ }>;
@@ -0,0 +1,91 @@
1
+ import path from 'node:path';
2
+ import { VovkSchemaIdEnum } from 'vovk';
3
+ import getLogger from '../../utils/getLogger.mjs';
4
+ import getUserConfig from './getUserConfig.mjs';
5
+ import getRelativeSrcRoot from './getRelativeSrcRoot.mjs';
6
+ import getTemplateDefs, { BuiltInTemplateName } from './getTemplateDefs.mjs';
7
+ import { normalizeOpenAPIMixin } from '../../utils/normalizeOpenAPIMixin.mjs';
8
+ import chalkHighlightThing from '../../utils/chalkHighlightThing.mjs';
9
+ export default async function getConfig({ configPath, cwd, logLevel, }) {
10
+ const { configAbsolutePaths, error, userConfig } = await getUserConfig({
11
+ configPath,
12
+ cwd,
13
+ });
14
+ const conf = userConfig ?? {};
15
+ logLevel = logLevel ?? conf.logLevel ?? 'info';
16
+ const log = getLogger(logLevel);
17
+ const env = process.env;
18
+ const clientTemplateDefs = getTemplateDefs(conf.clientTemplateDefs);
19
+ const srcRoot = await getRelativeSrcRoot({ cwd });
20
+ const config = {
21
+ $schema: VovkSchemaIdEnum.CONFIG,
22
+ clientTemplateDefs,
23
+ emitConfig: [],
24
+ composedClient: {
25
+ ...conf.composedClient,
26
+ enabled: conf.composedClient?.enabled ?? true,
27
+ fromTemplates: conf.composedClient?.fromTemplates ?? ['mjs', 'cjs'],
28
+ outDir: conf.composedClient?.outDir ?? './node_modules/.vovk-client',
29
+ prettifyClient: conf.composedClient?.prettifyClient ?? false,
30
+ },
31
+ segmentedClient: {
32
+ ...conf.segmentedClient,
33
+ enabled: conf.segmentedClient?.enabled ?? false,
34
+ fromTemplates: conf.segmentedClient?.fromTemplates ?? ['ts'],
35
+ outDir: conf.segmentedClient?.outDir ?? path.join(srcRoot ?? '.', 'client'),
36
+ prettifyClient: conf.segmentedClient?.prettifyClient ?? true,
37
+ },
38
+ bundle: {
39
+ prebundleOutDir: conf.bundle?.prebundleOutDir ?? 'tmp_prebundle',
40
+ keepPrebundleDir: conf.bundle?.keepPrebundleDir ?? false,
41
+ requires: {
42
+ [BuiltInTemplateName.readme]: '.',
43
+ [BuiltInTemplateName.packageJson]: '.',
44
+ },
45
+ generatorConfig: {},
46
+ ...conf.bundle,
47
+ tsdownBuildOptions: {
48
+ outDir: conf.bundle?.tsdownBuildOptions?.outDir ?? 'dist',
49
+ ...conf.bundle?.tsdownBuildOptions,
50
+ },
51
+ },
52
+ modulesDir: conf.modulesDir ?? path.join(srcRoot ?? '.', 'modules'),
53
+ schemaOutDir: env.VOVK_SCHEMA_OUT_DIR ?? conf.schemaOutDir ?? './.vovk-schema',
54
+ rootEntry: env.VOVK_ROOT_ENTRY ?? conf.rootEntry ?? 'api',
55
+ rootSegmentModulesDirName: conf.rootSegmentModulesDirName ?? '',
56
+ logLevel,
57
+ devHttps: conf.devHttps ?? false,
58
+ moduleTemplates: {
59
+ service: 'vovk-cli/module-templates/service.ts.ejs',
60
+ controller: 'vovk-cli/module-templates/controller.ts.ejs',
61
+ ...conf.moduleTemplates,
62
+ },
63
+ libs: conf.libs ?? {},
64
+ generatorConfig: {
65
+ ...conf.generatorConfig,
66
+ origin: (env.VOVK_ORIGIN ?? conf?.generatorConfig?.origin ?? '').replace(/\/$/, ''), // Remove trailing slash
67
+ segments: Object.fromEntries(await Promise.all(Object.entries(conf.generatorConfig?.segments ?? {}).map(async ([key, value]) => [
68
+ key,
69
+ {
70
+ ...value,
71
+ openAPIMixin: value.openAPIMixin
72
+ ? await normalizeOpenAPIMixin({ mixinModule: value.openAPIMixin, log, cwd })
73
+ : undefined,
74
+ },
75
+ ]))),
76
+ },
77
+ };
78
+ if (typeof conf.emitConfig === 'undefined') {
79
+ config.emitConfig = ['libs', 'generatorConfig'];
80
+ }
81
+ else if (conf.emitConfig === true) {
82
+ config.emitConfig = Object.keys(config);
83
+ }
84
+ else if (Array.isArray(conf.emitConfig)) {
85
+ config.emitConfig = conf.emitConfig;
86
+ } // else it's false and emitConfig already is []
87
+ if (!userConfig) {
88
+ log.warn(`Unable to load config at ${chalkHighlightThing(cwd)}. Using default values. ${error ?? ''}`);
89
+ }
90
+ return { config, srcRoot, configAbsolutePaths, userConfig, log };
91
+ }
@@ -0,0 +1,8 @@
1
+ import { VovkSchemaIdEnum, type VovkStrictConfig } from 'vovk';
2
+ export default function getMetaSchema({ config, useEmitConfig }: {
3
+ config: VovkStrictConfig;
4
+ useEmitConfig: boolean;
5
+ }): {
6
+ config: VovkStrictConfig;
7
+ $schema: VovkSchemaIdEnum;
8
+ };
@@ -0,0 +1,14 @@
1
+ import { VovkSchemaIdEnum } from 'vovk';
2
+ import pick from 'lodash/pick.js';
3
+ export default function getMetaSchema({ config, useEmitConfig }) {
4
+ return {
5
+ $schema: VovkSchemaIdEnum.META,
6
+ ...{
7
+ config: useEmitConfig
8
+ ? (config
9
+ ? pick(config, [...config.emitConfig, '$schema'])
10
+ : {})
11
+ : config,
12
+ },
13
+ };
14
+ }
@@ -1,18 +1,23 @@
1
+ import { LogLevelNames } from 'loglevel';
1
2
  export type ProjectInfo = Awaited<ReturnType<typeof getProjectInfo>>;
2
- export default function getProjectInfo({ port: givenPort, clientOutDir, cwd, }?: {
3
+ export default function getProjectInfo({ port: givenPort, cwd, configPath, srcRootRequired, logLevel, }?: {
3
4
  port?: number;
4
- clientOutDir?: string;
5
5
  cwd?: string;
6
+ configPath?: string;
7
+ srcRootRequired?: boolean;
8
+ logLevel?: LogLevelNames;
6
9
  }): Promise<{
7
10
  cwd: string;
8
11
  port: string;
9
- apiEntryPoint: string;
10
- apiDir: string;
11
- srcRoot: string;
12
- schemaOutImportPath: string;
13
- fetcherClientImportPath: string;
14
- validateOnClientImportPath: string | null;
15
- config: Required<import("../types.mjs").VovkConfig>;
12
+ apiRoot: string;
13
+ apiDirAbsolutePath: string | null;
14
+ srcRoot: string | null;
15
+ vovkCliPackage: {
16
+ version: string;
17
+ };
18
+ config: import("vovk/types").VovkStrictConfig;
19
+ packageJson: import("type-fest").PackageJson;
20
+ isNextInstalled: boolean;
16
21
  log: {
17
22
  info: (msg: string) => void;
18
23
  warn: (msg: string) => void;
@@ -1,37 +1,39 @@
1
1
  import path from 'node:path';
2
- import getConfig from './getConfig.mjs';
3
- import getLogger from '../utils/getLogger.mjs';
4
- export default async function getProjectInfo({ port: givenPort, clientOutDir, cwd = process.cwd(), } = {}) {
2
+ import getConfig from './getConfig/index.mjs';
3
+ import { getPackageJson } from '../utils/getPackageJson.mjs';
4
+ import { readFile } from 'node:fs/promises';
5
+ export default async function getProjectInfo({ port: givenPort, cwd = process.cwd(), configPath, srcRootRequired = true, logLevel, } = {
6
+ logLevel: 'info',
7
+ }) {
5
8
  const port = givenPort?.toString() ?? process.env.PORT ?? '3000';
6
9
  // Make PORT available to the config file at getConfig
7
10
  process.env.PORT = port;
8
- const { config, srcRoot, configAbsolutePaths, userConfig, error } = await getConfig({ clientOutDir, cwd });
9
- const apiEntryPoint = `${config.origin ?? ''}/${config.rootEntry}`;
10
- const apiDir = path.join(srcRoot, 'app', config.rootEntry);
11
- const schemaOutImportPath = path.relative(config.clientOutDir, config.schemaOutDir).replace(/\\/g, '/'); // windows fix
12
- const fetcherClientImportPath = config.fetcher.startsWith('.')
13
- ? path.relative(config.clientOutDir, config.fetcher)
14
- : config.fetcher;
15
- const validateOnClientImportPath = config.validateOnClient?.startsWith('.')
16
- ? path.relative(config.clientOutDir, config.validateOnClient)
17
- : config.validateOnClient;
18
- const log = getLogger(config.logLevel);
11
+ const { config, srcRoot, configAbsolutePaths, log } = await getConfig({
12
+ configPath,
13
+ cwd,
14
+ logLevel,
15
+ });
16
+ const packageJson = await getPackageJson(cwd, log);
17
+ const isNextInstalled = !!packageJson?.dependencies?.next || !!packageJson?.devDependencies?.next;
18
+ if (srcRootRequired && !srcRoot) {
19
+ throw new Error(`Could not find app router directory at ${cwd}. Check Next.js docs for more info.`);
20
+ }
21
+ const apiRoot = `${config.generatorConfig.origin ?? ''}/${config.rootEntry}`;
22
+ const apiDirAbsolutePath = srcRoot ? path.resolve(cwd, srcRoot, 'app', config.rootEntry) : null;
19
23
  if (configAbsolutePaths.length > 1) {
20
24
  log.warn(`Multiple config files found. Using the first one: ${configAbsolutePaths[0]}`);
21
25
  }
22
- if (!userConfig && configAbsolutePaths.length > 0) {
23
- log.error(`Error reading config file at ${configAbsolutePaths[0]}: ${error?.message ?? 'Unknown Error'}`);
24
- }
26
+ const vovkCliPackage = JSON.parse(await readFile(path.join(import.meta.dirname, '../../package.json'), 'utf-8'));
25
27
  return {
26
28
  cwd,
27
29
  port,
28
- apiEntryPoint,
29
- apiDir,
30
+ apiRoot,
31
+ apiDirAbsolutePath,
30
32
  srcRoot,
31
- schemaOutImportPath,
32
- fetcherClientImportPath,
33
- validateOnClientImportPath,
33
+ vovkCliPackage,
34
34
  config,
35
+ packageJson,
36
+ isNextInstalled,
35
37
  log,
36
38
  };
37
39
  }
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import type { VovkConfig, VovkDevEnv } from './types.mjs';
3
2
  import 'dotenv/config';
4
- export type { VovkConfig, VovkDevEnv };
3
+ import type { VovkEnv } from './types.mjs';
4
+ export type { VovkEnv };
package/dist/index.mjs CHANGED
@@ -1,54 +1,69 @@
1
1
  #!/usr/bin/env node
2
2
  import path from 'node:path';
3
3
  import { readFileSync } from 'node:fs';
4
+ import 'dotenv/config';
4
5
  import { Command } from 'commander';
5
6
  import concurrently from 'concurrently';
6
7
  import getAvailablePort from './utils/getAvailablePort.mjs';
7
8
  import getProjectInfo from './getProjectInfo/index.mjs';
8
- import generateClient from './generateClient.mjs';
9
- import locateSegments from './locateSegments.mjs';
9
+ import { VovkGenerate } from './generate/index.mjs';
10
+ import { bundle } from './bundle/index.mjs';
10
11
  import { VovkDev } from './dev/index.mjs';
11
- import newComponents from './new/index.mjs';
12
- import 'dotenv/config';
13
- import initProgram from './initProgram.mjs';
14
- import { pathToFileURL } from 'node:url';
12
+ import { newComponents } from './new/index.mjs';
13
+ import { initProgram } from './initProgram.mjs';
14
+ import { getProjectFullSchema } from './generate/getProjectFullSchema.mjs';
15
15
  const program = new Command();
16
- const packageJSON = JSON.parse(readFileSync(path.join(import.meta.dirname, '../package.json'), 'utf-8'));
17
- program.name('vovk').description('Vovk CLI').version(packageJSON.version);
16
+ const vovkCliPackage = JSON.parse(readFileSync(path.join(import.meta.dirname, '../package.json'), 'utf-8'));
17
+ program.name('vovk').description('Vovk CLI').version(vovkCliPackage.version);
18
18
  initProgram(program.command('init'));
19
19
  program
20
20
  .command('dev')
21
+ .alias('d')
21
22
  .description('Start schema watcher (optional flag --next-dev to start it with Next.js)')
22
- .option('--next-dev', 'Start schema watcher and Next.js with automatic port allocation', false)
23
- .allowUnknownOption(true)
24
- .action(async (options, command) => {
23
+ .argument('[nextArgs...]', 'extra arguments for the dev command')
24
+ .option('--next-dev', 'start schema watcher and Next.js with automatic port allocation')
25
+ .option('--exit', 'kill the processes when schema and client is generated')
26
+ .option('--schema-out <path>', 'path to schema output directory (default: .vovk-schema)')
27
+ .option('--https, --dev-https', 'use HTTPS for the dev server (default: false)')
28
+ .option('--log-level <level>', 'set the log level')
29
+ .action(async (nextArgs, options) => {
30
+ const { nextDev, exit = false, schemaOut, devHttps, logLevel } = options;
25
31
  const portAttempts = 30;
26
- const PORT = !options.nextDev
32
+ const PORT = !nextDev
27
33
  ? process.env.PORT
28
34
  : process.env.PORT ||
29
35
  (await getAvailablePort(3000, portAttempts, 0, (failedPort, tryingPort) =>
30
36
  // eslint-disable-next-line no-console
31
- console.warn(`🐺 Next.js Port ${failedPort} is in use, trying ${tryingPort} instead.`)).catch(() => {
32
- throw new Error(`🐺 ❌ Failed to find available Next port after ${portAttempts} attempts`);
37
+ console.warn(`🐺 Port ${failedPort} is in use, trying ${tryingPort} instead.`)).catch(() => {
38
+ throw new Error(`🐺 ❌ Failed to find an available port after ${portAttempts} attempts`);
33
39
  }));
34
40
  if (!PORT) {
35
41
  throw new Error('🐺 ❌ PORT env variable is required');
36
42
  }
37
- if (options.nextDev) {
43
+ if (nextDev) {
38
44
  const { result } = concurrently([
39
45
  {
40
- command: `npx next dev ${command.args.join(' ')}`,
46
+ command: `npx next dev ${nextArgs.join(' ')}`,
41
47
  name: 'Next.js Development Server',
42
48
  env: { PORT },
43
49
  },
44
50
  {
45
51
  command: `node ${import.meta.dirname}/dev/index.mjs`,
46
- name: 'Vovk Dev Command',
47
- env: { PORT, __VOVK_START_WATCHER_IN_STANDALONE_MODE__: 'true' },
52
+ name: 'Vovk Dev Watcher',
53
+ env: {
54
+ PORT,
55
+ __VOVK_START_WATCHER_IN_STANDALONE_MODE__: 'true',
56
+ // TODO: Pass these as flags
57
+ __VOVK_SCHEMA_OUT_FLAG__: schemaOut ?? '',
58
+ __VOVK_DEV_HTTPS_FLAG__: devHttps ? 'true' : 'false',
59
+ __VOVK_EXIT__: exit ? 'true' : 'false',
60
+ __VOVK_LOG_LEVEL__: logLevel ?? undefined,
61
+ },
48
62
  },
49
63
  ], {
50
- killOthers: ['failure', 'success'],
64
+ killOthersOn: ['failure', 'success'],
51
65
  prefix: 'none',
66
+ successCondition: 'first',
52
67
  });
53
68
  try {
54
69
  await result;
@@ -58,32 +73,99 @@ program
58
73
  }
59
74
  }
60
75
  else {
61
- void new VovkDev().start();
76
+ void new VovkDev({ schemaOut, devHttps, logLevel }).start({ exit });
62
77
  }
63
78
  });
64
79
  program
65
80
  .command('generate')
66
- .description('Generate client')
67
- .option('--client-out <path>', 'Path to output directory')
68
- .action(async (options) => {
69
- const projectInfo = await getProjectInfo({ clientOutDir: options.clientOut });
70
- const { cwd, config, apiDir } = projectInfo;
71
- const segments = await locateSegments(apiDir);
72
- const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
73
- const schemaImportUrl = pathToFileURL(path.join(schemaOutAbsolutePath, 'index.js')).href;
74
- const schema = await import(schemaImportUrl);
75
- await generateClient(projectInfo, segments, schema.default);
81
+ .alias('g')
82
+ .description('Generate RPC client from schema')
83
+ .option('--composed-only', 'generate only composed client even if segmented client is enabled')
84
+ .option('--out, --composed-out <path>', 'path to output directory for composed client')
85
+ .option('--from, --composed-from <templates...>', 'client template names for composed client')
86
+ .option('--include, --composed-include-segments <segments...>', 'include segments in composed client')
87
+ .option('--exclude, --composed-exclude-segments <segments...>', 'exclude segments in composed client')
88
+ .option('--segmented-only', 'generate only segmented client even if composed client is enabled')
89
+ .option('--segmented-out <path>', 'path to output directory for segmented client')
90
+ .option('--segmented-from <templates...>', 'client template names for segmented client')
91
+ .option('--segmented-include-segments <segments...>', 'include segments in segmented client')
92
+ .option('--segmented-exclude-segments <segments...>', 'exclude segments in segmented client')
93
+ .option('--prettify', 'prettify output files')
94
+ .option('--schema, --schema-path <path>', 'path to schema folder (default: ./.vovk-schema)')
95
+ .option('--config, --config-path <config>', 'path to config file')
96
+ .option('--origin <url>', 'set the origin URL for the generated client')
97
+ .option('--watch [s]', 'watch for changes in schema or openapi spec and regenerate client; accepts a number in seconds to throttle the watcher or make an HTTP request to the OpenAPI spec URL')
98
+ .option('--openapi, --openapi-spec <openapi_path_or_urls...>', 'use OpenAPI schema for client generation')
99
+ .option('--openapi-module-name, --openapi-get-module-name <names...>', 'module names corresponding to the index of --openapi option')
100
+ .option('--openapi-method-name, --openapi-get-method-name <names...>', 'method names corresponding to the index of --openapi option')
101
+ .option('--openapi-root-url <urls...>', 'root URLs corresponding to the index of --openapi option')
102
+ .option('--openapi-mixin-name <names...>', 'mixin names corresponding to the index of --openapi option')
103
+ .option('--openapi-fallback <paths...>', 'save OpenAPI spec and use it as a fallback if URL is not available')
104
+ .option('--log-level <level>', 'set the log level')
105
+ .action(async (cliGenerateOptions) => {
106
+ const projectInfo = await getProjectInfo({
107
+ configPath: cliGenerateOptions.configPath,
108
+ srcRootRequired: false,
109
+ logLevel: cliGenerateOptions.logLevel,
110
+ });
111
+ await new VovkGenerate({
112
+ projectInfo,
113
+ forceNothingWrittenLog: true,
114
+ cliGenerateOptions,
115
+ }).start();
116
+ });
117
+ program
118
+ .command('bundle')
119
+ .alias('b')
120
+ .description('Generate TypeScript RPC and bundle it')
121
+ .option('--out, --out-dir <path>', 'path to output directory for bundle')
122
+ .option('--include, --include-segments <segments...>', 'include segments')
123
+ .option('--exclude, --exclude-segments <segments...>', 'exclude segments')
124
+ .option('--prebundle-out-dir, --prebundle-out <path>', 'path to output directory for prebundle')
125
+ .option('--keep-prebundle-dir', 'do not delete prebundle directory after bundling')
126
+ .option('--schema, --schema-path <path>', 'path to schema folder (default: .vovk-schema)')
127
+ .option('--config, --config-path <config>', 'path to config file')
128
+ .option('--origin <url>', 'set the origin URL for the generated client')
129
+ .option('--tsconfig <path>', 'path to tsconfig.json for bundling by tsdown')
130
+ .option('--openapi, --openapi-spec <openapi_path_or_urls...>', 'use OpenAPI schema instead of Vovk schema')
131
+ .option('--openapi-get-module-name <names...>', 'module names corresponding to the index of --openapi option')
132
+ .option('--openapi-get-method-name <names...>', 'method names corresponding to the index of --openapi option')
133
+ .option('--openapi-root-url <urls...>', 'root URLs corresponding to the index of --openapi option')
134
+ .option('--log-level <level>', 'set the log level')
135
+ .action(async (cliBundleOptions) => {
136
+ const projectInfo = await getProjectInfo({
137
+ configPath: cliBundleOptions.configPath,
138
+ srcRootRequired: false,
139
+ logLevel: cliBundleOptions.logLevel,
140
+ });
141
+ const { cwd, config, log, isNextInstalled } = projectInfo;
142
+ const fullSchema = await getProjectFullSchema({
143
+ schemaOutAbsolutePath: path.resolve(cwd, cliBundleOptions?.schema ?? config.schemaOutDir),
144
+ log,
145
+ isNextInstalled,
146
+ config,
147
+ });
148
+ await bundle({
149
+ projectInfo,
150
+ fullSchema,
151
+ cliBundleOptions,
152
+ });
76
153
  });
77
154
  program
78
155
  .command('new [components...]')
79
156
  .alias('n')
80
157
  .description('Create new components. "vovk new [...components] [segmentName/]moduleName" to create a new module or "vovk new segment [segmentName]" to create a new segment')
81
- .option('-o, --overwrite', 'Overwrite existing files')
82
- .option('--template, --templates <templates...>', 'Override config template. Accepts an array of strings that correspond the order of the components')
83
- .option('--dir <dirname>', 'Override dirName in template file. Relative to the root of the project')
84
- .option('--no-segment-update', 'Do not update segment files when creating a new module')
85
- .option('--dry-run', 'Do not write files to disk')
86
- .action((components, options) => newComponents(components, options));
158
+ .option('-o, --overwrite', 'overwrite existing files')
159
+ .option('--template, --templates <templates...>', 'override config template; accepts an array of strings that correspond the order of the components')
160
+ .option('--dir <dirname>', 'override dirName in template file; relative to the root of the project')
161
+ .option('--empty', 'create an empty module')
162
+ .option('--no-segment-update', 'do not update segment files when creating a new module')
163
+ .option('--dry-run', 'do not write files to disk')
164
+ .option('--static', 'if the segment is static')
165
+ .option('--log-level <level>', 'set the log level')
166
+ .action(async (components, newOptions) => newComponents(components, await getProjectInfo({
167
+ logLevel: newOptions.logLevel,
168
+ }), newOptions));
87
169
  program
88
170
  .command('help')
89
171
  .description('Show help message')
@@ -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, lang, 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' | 'lang' | '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, lang, 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;
@@ -11,26 +11,52 @@ export default async function createConfig({ root, log, options: { validationLib
11
11
  .readFile(path.join(root, 'package.json'), 'utf-8')
12
12
  .then((content) => JSON.parse(content).type === 'module');
13
13
  const configAbsolutePath = path.join(dir, isModule ? 'vovk.config.mjs' : 'vovk.config.js');
14
- const templates = {
15
- controller: 'vovk-cli/templates/controller.ejs',
16
- service: 'vovk-cli/templates/service.ejs',
17
- worker: 'vovk-cli/templates/worker.ejs',
14
+ const typeTemplates = {
15
+ controller: 'vovk-cli/module-templates/type/controller.ts.ejs',
16
+ service: 'vovk-cli/module-templates/type/service.ts.ejs',
18
17
  };
19
- if (validationLibrary) {
20
- config.validationLibrary = validationLibrary;
21
- if (validateOnClient) {
22
- config.validateOnClient = `${validationLibrary}/validateOnClient`;
23
- }
18
+ const moduleTemplates = {
19
+ ...typeTemplates,
20
+ ...{
21
+ type: typeTemplates,
22
+ zod: {
23
+ controller: 'vovk-zod/module-templates/controller.ts.ejs',
24
+ },
25
+ yup: {
26
+ controller: 'vovk-yup/module-templates/controller.ts.ejs',
27
+ },
28
+ 'class-validator': {
29
+ controller: 'vovk-dto/module-templates/controller.ts.ejs',
30
+ },
31
+ valibot: {
32
+ controller: 'vovk-cli/module-templates/valibot/controller.ts.ejs',
33
+ },
34
+ arktype: {
35
+ controller: 'vovk-cli/module-templates/arktype/controller.ts.ejs',
36
+ },
37
+ }[validationLibrary ?? 'type'],
38
+ };
39
+ config.generatorConfig ??= {};
40
+ config.generatorConfig.imports ??= {};
41
+ config.generatorConfig.imports.validateOnClient =
42
+ validationLibrary === 'class-validator' ? 'vovk-dto/validateOnClient' : 'vovk-ajv';
43
+ if (validationLibrary && !moduleTemplates) {
24
44
  try {
45
+ // TODO: Legacy, is it useful to keep it?
25
46
  const validationTemplates = await getTemplateFilesFromPackage(validationLibrary, channel);
26
- Object.assign(templates, validationTemplates);
47
+ Object.assign(moduleTemplates, validationTemplates);
27
48
  }
28
49
  catch (error) {
29
50
  log.warn(`Failed to fetch validation library templates: ${error.message}`);
30
51
  }
31
52
  }
32
- config.templates = templates;
33
- const configStr = await prettify(`/** @type {import('vovk-cli').VovkConfig} */
53
+ if (lang?.length) {
54
+ config.composedClient ??= {};
55
+ config.composedClient.fromTemplates = ['mjs', 'cjs', ...lang];
56
+ }
57
+ config.moduleTemplates = moduleTemplates;
58
+ const configStr = await prettify(`// @ts-check
59
+ /** @type {import('vovk').VovkConfig} */
34
60
  const config = ${JSON.stringify(config, null, 2)};
35
61
  ${isModule ? '\nexport default config;' : 'module.exports = config;'}`, configAbsolutePath);
36
62
  if (!dryRun)
@@ -0,0 +1,4 @@
1
+ export declare function createStandardSchemaValidatorFile({ root, validationLibrary, }: {
2
+ root: string;
3
+ validationLibrary: 'arktype' | 'valibot';
4
+ }): Promise<void>;
@@ -0,0 +1,39 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import getRelativeSrcRoot from '../getProjectInfo/getConfig/getRelativeSrcRoot.mjs';
4
+ function getCode(validationLibrary) {
5
+ if (validationLibrary === 'valibot') {
6
+ return `
7
+ import { createStandardValidation, KnownAny } from 'vovk';
8
+ import { toJsonSchema } from '@valibot/to-json-schema';
9
+ import * as v from 'valibot';
10
+
11
+ const withValibot = createStandardValidation({
12
+ toJSONSchema: (model: v.BaseSchema<KnownAny, KnownAny, KnownAny>) => toJsonSchema(model),
13
+ });
14
+
15
+ export default withValibot;
16
+ `.trimStart();
17
+ }
18
+ if (validationLibrary === 'arktype') {
19
+ return `
20
+ import { createStandardValidation } from 'vovk';
21
+ import { type } from 'arktype';
22
+
23
+ const withArk = createStandardValidation({
24
+ toJSONSchema: (model: type) => model.toJsonSchema(),
25
+ });
26
+
27
+ export default withArk;
28
+ `.trimStart();
29
+ }
30
+ throw new Error(`Unknown validation library: ${validationLibrary}`);
31
+ }
32
+ export async function createStandardSchemaValidatorFile({ root, validationLibrary, }) {
33
+ const code = getCode(validationLibrary);
34
+ const srcRoot = (await getRelativeSrcRoot({ cwd: root })) ?? '.';
35
+ const libDir = path.resolve(root, srcRoot, 'lib');
36
+ const filePath = path.join(libDir, `${validationLibrary === 'arktype' ? 'withArk' : 'withValibot'}.ts`);
37
+ await fs.mkdir(libDir, { recursive: true });
38
+ await fs.writeFile(filePath, code);
39
+ }