vovk-cli 0.0.1-draft.39 → 0.0.1-draft.392

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 (140) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +31 -1
  3. package/client-templates/cjs/index.cjs.ejs +20 -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 +19 -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 +33 -0
  20. package/dist/bundle/index.d.mts +8 -0
  21. package/dist/bundle/index.mjs +74 -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 +141 -66
  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 +300 -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 +21 -0
  43. package/dist/generate/getTemplateClientImports.mjs +56 -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 +151 -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 +91 -0
  58. package/dist/getProjectInfo/getConfig/index.mjs +92 -0
  59. package/dist/getProjectInfo/getMetaSchema.d.mts +8 -0
  60. package/dist/getProjectInfo/getMetaSchema.mjs +27 -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 +116 -35
  65. package/dist/init/createConfig.d.mts +2 -2
  66. package/dist/init/createConfig.mjs +40 -13
  67. package/dist/init/createStandardSchemaValidatorFile.d.mts +4 -0
  68. package/dist/init/createStandardSchemaValidatorFile.mjs +52 -0
  69. package/dist/init/getTemplateFilesFromPackage.mjs +10 -5
  70. package/dist/init/index.d.mts +2 -2
  71. package/dist/init/index.mjs +121 -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.mjs +1 -1
  77. package/dist/init/updateTypeScriptConfig.d.mts +4 -1
  78. package/dist/init/updateTypeScriptConfig.mjs +11 -7
  79. package/dist/initProgram.d.mts +1 -1
  80. package/dist/initProgram.mjs +17 -17
  81. package/dist/locateSegments.d.mts +8 -1
  82. package/dist/locateSegments.mjs +13 -3
  83. package/dist/new/addClassToSegmentCode.d.mts +1 -2
  84. package/dist/new/addClassToSegmentCode.mjs +3 -3
  85. package/dist/new/index.d.mts +2 -1
  86. package/dist/new/index.mjs +5 -3
  87. package/dist/new/newModule.d.mts +5 -2
  88. package/dist/new/newModule.mjs +26 -24
  89. package/dist/new/newSegment.d.mts +4 -1
  90. package/dist/new/newSegment.mjs +20 -12
  91. package/dist/new/render.d.mts +7 -3
  92. package/dist/new/render.mjs +31 -10
  93. package/dist/types.d.mts +66 -44
  94. package/dist/utils/compileJSONSchemaToTypeScriptType.d.mts +5 -0
  95. package/dist/utils/compileJSONSchemaToTypeScriptType.mjs +9 -0
  96. package/dist/utils/compileTs.d.mts +12 -0
  97. package/dist/utils/compileTs.mjs +261 -0
  98. package/dist/utils/debounceWithArgs.d.mts +1 -1
  99. package/dist/utils/deepExtend.d.mts +54 -0
  100. package/dist/utils/deepExtend.mjs +129 -0
  101. package/dist/utils/formatLoggedSegmentName.d.mts +3 -1
  102. package/dist/utils/formatLoggedSegmentName.mjs +3 -2
  103. package/dist/utils/generateFnName.d.mts +23 -0
  104. package/dist/utils/generateFnName.mjs +76 -0
  105. package/dist/utils/getPackageJson.d.mts +3 -0
  106. package/dist/utils/getPackageJson.mjs +22 -0
  107. package/dist/utils/getPublicModuleNameFromPath.d.mts +4 -0
  108. package/dist/utils/getPublicModuleNameFromPath.mjs +9 -0
  109. package/dist/utils/normalizeOpenAPIMixin.d.mts +14 -0
  110. package/dist/utils/normalizeOpenAPIMixin.mjs +114 -0
  111. package/dist/utils/pickSegmentFullSchema.d.mts +3 -0
  112. package/dist/utils/pickSegmentFullSchema.mjs +15 -0
  113. package/dist/utils/removeUnlistedDirectories.d.mts +10 -0
  114. package/dist/utils/removeUnlistedDirectories.mjs +61 -0
  115. package/dist/utils/resolveAbsoluteModulePath.d.mts +2 -0
  116. package/dist/utils/resolveAbsoluteModulePath.mjs +32 -0
  117. package/module-templates/arktype/controller.ts.ejs +78 -0
  118. package/module-templates/type/controller.ts.ejs +64 -0
  119. package/module-templates/type/service.ts.ejs +37 -0
  120. package/module-templates/valibot/controller.ts.ejs +78 -0
  121. package/package.json +39 -22
  122. package/dist/dev/diffSchema.d.mts +0 -43
  123. package/dist/dev/ensureClient.d.mts +0 -5
  124. package/dist/dev/ensureClient.mjs +0 -31
  125. package/dist/dev/isMetadataEmpty.d.mts +0 -2
  126. package/dist/dev/isMetadataEmpty.mjs +0 -4
  127. package/dist/dev/writeOneSchemaFile.d.mts +0 -11
  128. package/dist/generateClient.d.mts +0 -7
  129. package/dist/generateClient.mjs +0 -97
  130. package/dist/getProjectInfo/getConfig.d.mts +0 -11
  131. package/dist/getProjectInfo/getConfig.mjs +0 -29
  132. package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +0 -4
  133. package/dist/postinstall.d.mts +0 -1
  134. package/dist/postinstall.mjs +0 -24
  135. package/templates/controller.ejs +0 -52
  136. package/templates/service.ejs +0 -27
  137. package/templates/worker.ejs +0 -24
  138. /package/dist/getProjectInfo/{importUncachedModule.d.mts → getConfig/importUncachedModule.d.mts} +0 -0
  139. /package/dist/getProjectInfo/{importUncachedModuleWorker.d.mts → getConfig/importUncachedModuleWorker.d.mts} +0 -0
  140. /package/dist/getProjectInfo/{importUncachedModuleWorker.mjs → getConfig/importUncachedModuleWorker.mjs} +0 -0
@@ -0,0 +1,186 @@
1
+ import path from 'node:path';
2
+ import * as chokidar from 'chokidar';
3
+ import { getProjectFullSchema } from './getProjectFullSchema.mjs';
4
+ import { generate } from './generate.mjs';
5
+ import { locateSegments } from '../locateSegments.mjs';
6
+ import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
7
+ const THROTTLE_DELAY = 5000;
8
+ export class VovkGenerate {
9
+ #cliGenerateOptions;
10
+ #projectInfo;
11
+ #forceNothingWrittenLog;
12
+ constructor({ cliGenerateOptions, projectInfo, forceNothingWrittenLog, }) {
13
+ this.#cliGenerateOptions = cliGenerateOptions;
14
+ this.#projectInfo = projectInfo;
15
+ this.#forceNothingWrittenLog = forceNothingWrittenLog ?? true;
16
+ }
17
+ start() {
18
+ const { watch } = this.#cliGenerateOptions;
19
+ if (watch) {
20
+ const throttleDelay = typeof watch === 'boolean' ? THROTTLE_DELAY : parseFloat(watch) * 1e3 || THROTTLE_DELAY;
21
+ this.watch({ throttleDelay });
22
+ }
23
+ else {
24
+ this.generate();
25
+ }
26
+ }
27
+ async generate() {
28
+ const fullSchema = await this.getFullSchema();
29
+ const { log, config, apiDirAbsolutePath } = this.#projectInfo;
30
+ const locatedSegments = await locateSegments({ dir: apiDirAbsolutePath, config, log });
31
+ await generate({
32
+ projectInfo: this.#projectInfo,
33
+ fullSchema,
34
+ forceNothingWrittenLog: this.#forceNothingWrittenLog,
35
+ cliGenerateOptions: this.#cliGenerateOptions,
36
+ locatedSegments,
37
+ });
38
+ }
39
+ async getFullSchema() {
40
+ const { log, config, cwd, isNextInstalled } = this.#projectInfo;
41
+ const { schemaPath } = this.#cliGenerateOptions;
42
+ const fullSchema = await getProjectFullSchema({
43
+ schemaOutAbsolutePath: path.resolve(cwd, schemaPath ?? config.schemaOutDir),
44
+ isNextInstalled,
45
+ log,
46
+ config,
47
+ });
48
+ return fullSchema;
49
+ }
50
+ watch({ throttleDelay }) {
51
+ const { openapiSpec, schemaPath } = this.#cliGenerateOptions;
52
+ const { log, cwd, config } = this.#projectInfo;
53
+ if (openapiSpec) {
54
+ log.debug(`Watching OpenAPI spec: ${openapiSpec}`);
55
+ this.watchOpenApiSpec({ openApiSpec: openapiSpec, throttleDelay });
56
+ }
57
+ else {
58
+ const resolvedSchemaPath = path.resolve(cwd, schemaPath ?? config.schemaOutDir);
59
+ log.debug(`Watching schema directory: ${resolvedSchemaPath}`);
60
+ this.watchSchema({ schemaPath: resolvedSchemaPath, throttleDelay });
61
+ }
62
+ }
63
+ watchSchema({ schemaPath, throttleDelay }) {
64
+ const { log } = this.#projectInfo;
65
+ let lastGenerationTime = 0;
66
+ chokidar
67
+ .watch(schemaPath, {
68
+ persistent: true,
69
+ ignoreInitial: true,
70
+ })
71
+ .on('all', (event, path) => {
72
+ if (event === 'change' || event === 'add' || event === 'ready' || event === 'unlink') {
73
+ log.debug(`Schema file ${event}: ${path}`);
74
+ const now = Date.now();
75
+ const shouldGenerateImmediately = now - lastGenerationTime > throttleDelay;
76
+ const generateCode = async () => {
77
+ try {
78
+ lastGenerationTime = Date.now();
79
+ await this.generate();
80
+ log.debug(`Regenerated from schema changes`);
81
+ }
82
+ catch (error) {
83
+ log.error(`Failed to regenerate from schema: ${error instanceof Error ? error.message : String(error)}`);
84
+ }
85
+ };
86
+ // Generate immediately if it's been a while since the last generation
87
+ if (shouldGenerateImmediately) {
88
+ generateCode();
89
+ }
90
+ }
91
+ });
92
+ }
93
+ watchOpenApiSpec({ openApiSpec, throttleDelay }) {
94
+ const fileSpecs = openApiSpec.filter((spec) => !spec.startsWith('http://') && !spec.startsWith('https://'));
95
+ const remoteSpecs = openApiSpec.filter((spec) => spec.startsWith('http://') || spec.startsWith('https://'));
96
+ if (fileSpecs.length) {
97
+ this.watchOpenApiSpecLocal({
98
+ openApiSpecPaths: fileSpecs,
99
+ throttleDelay,
100
+ });
101
+ }
102
+ if (remoteSpecs.length) {
103
+ remoteSpecs.forEach((spec) => {
104
+ this.watchOpenApiSpecRemote({
105
+ openApiSpecUrl: spec,
106
+ throttleDelay,
107
+ });
108
+ });
109
+ }
110
+ }
111
+ watchOpenApiSpecLocal({ openApiSpecPaths, throttleDelay }) {
112
+ const { log, cwd } = this.#projectInfo;
113
+ let lastGenerationTime = 0;
114
+ chokidar
115
+ .watch(openApiSpecPaths, {
116
+ cwd,
117
+ persistent: true,
118
+ ignoreInitial: false,
119
+ })
120
+ .on('all', (event, path) => {
121
+ if (event === 'change' || event === 'add' || event === 'ready' || event === 'unlink') {
122
+ log.debug(`OpenAPI spec file changed: ${path}`);
123
+ const now = Date.now();
124
+ const shouldGenerateImmediately = now - lastGenerationTime > throttleDelay;
125
+ const generateCode = async () => {
126
+ try {
127
+ lastGenerationTime = Date.now();
128
+ await this.generate();
129
+ log.debug(`Regenerated from OpenAPI spec changes`);
130
+ }
131
+ catch (error) {
132
+ log.error(`Failed to regenerate from OpenAPI spec: ${error instanceof Error ? error.message : String(error)}`);
133
+ }
134
+ };
135
+ // Generate immediately if it's been a while since the last generation
136
+ if (shouldGenerateImmediately) {
137
+ generateCode();
138
+ }
139
+ }
140
+ });
141
+ }
142
+ watchOpenApiSpecRemote({ openApiSpecUrl, throttleDelay }) {
143
+ const { log } = this.#projectInfo;
144
+ let lastContent = null;
145
+ let isPolling = false;
146
+ log.info(`Polling remote OpenAPI spec at ${chalkHighlightThing(openApiSpecUrl)} every ${throttleDelay}ms`);
147
+ const pollRemoteSpec = async () => {
148
+ if (isPolling)
149
+ return;
150
+ isPolling = true;
151
+ try {
152
+ const response = await fetch(openApiSpecUrl, {
153
+ headers: {
154
+ Accept: 'application/json, application/yaml',
155
+ },
156
+ });
157
+ if (!response.ok) {
158
+ log.error(`Failed to fetch OpenAPI spec: ${response.status} ${response.statusText}`);
159
+ return;
160
+ }
161
+ const content = await response.text();
162
+ if (content !== lastContent) {
163
+ log.info(`Remote OpenAPI spec changed at ${chalkHighlightThing(openApiSpecUrl)}`);
164
+ lastContent = content;
165
+ try {
166
+ await this.generate();
167
+ log.debug(`Regenerated from remote OpenAPI spec changes`);
168
+ }
169
+ catch (error) {
170
+ log.error(`Failed to regenerate from OpenAPI spec: ${error instanceof Error ? error.message : String(error)}`);
171
+ }
172
+ }
173
+ }
174
+ catch (error) {
175
+ log.error(`Error polling OpenAPI spec: ${error instanceof Error ? error.message : String(error)}`);
176
+ }
177
+ finally {
178
+ isPolling = false;
179
+ }
180
+ };
181
+ // Initial fetch
182
+ pollRemoteSpec();
183
+ // Set up polling
184
+ setInterval(pollRemoteSpec, throttleDelay);
185
+ }
186
+ }
@@ -0,0 +1,42 @@
1
+ import { VovkReadmeConfig, VovkSamplesConfig, type VovkSchema, type VovkStrictConfig } from 'vovk';
2
+ import type { PackageJson } from 'type-fest';
3
+ import type { ProjectInfo } from '../getProjectInfo/index.mjs';
4
+ import type { ClientTemplateFile } from './getClientTemplateFiles.mjs';
5
+ import type { Segment } from '../locateSegments.mjs';
6
+ import { OpenAPIObject } from 'openapi3-ts/oas31';
7
+ export declare function normalizeOutTemplatePath(out: string, packageJson: PackageJson): string;
8
+ export default function writeOneClientFile({ cwd, projectInfo, clientTemplateFile, fullSchema, prettifyClient, segmentName, templateContent, matterResult: { data, content }, openAPIObject, package: packageJson, readme, samples, reExports, isEnsuringClient, outCwdRelativeDir, templateDef, locatedSegments, isNodeNextResolution, hasMixins, isVovkProject, vovkCliPackage, isBundle, origin, configKey, cliSchemaPath, projectConfig, }: {
9
+ cwd: string;
10
+ projectInfo: ProjectInfo;
11
+ clientTemplateFile: ClientTemplateFile;
12
+ fullSchema: VovkSchema;
13
+ prettifyClient: boolean;
14
+ segmentName: string | null;
15
+ templateContent: string;
16
+ matterResult: {
17
+ data: {
18
+ imports?: string[];
19
+ };
20
+ content: string;
21
+ };
22
+ openAPIObject: OpenAPIObject;
23
+ package: PackageJson;
24
+ readme: VovkReadmeConfig;
25
+ samples: VovkSamplesConfig;
26
+ reExports: VovkStrictConfig['outputConfig']['reExports'];
27
+ isEnsuringClient: boolean;
28
+ outCwdRelativeDir: string;
29
+ templateDef: VovkStrictConfig['clientTemplateDefs'][string];
30
+ locatedSegments: Segment[];
31
+ isNodeNextResolution: boolean;
32
+ hasMixins: boolean;
33
+ isVovkProject: boolean;
34
+ vovkCliPackage: PackageJson;
35
+ isBundle: boolean;
36
+ origin: string | null;
37
+ configKey: 'composedClient' | 'segmentedClient';
38
+ cliSchemaPath: string | null;
39
+ projectConfig: VovkStrictConfig;
40
+ }): Promise<{
41
+ written: boolean;
42
+ }>;
@@ -0,0 +1,151 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'node:path';
3
+ import ejs from 'ejs';
4
+ import _ from 'lodash';
5
+ import { createCodeSamples, VovkSchemaIdEnum, } from 'vovk';
6
+ import * as YAML from 'yaml';
7
+ import TOML from '@iarna/toml';
8
+ import prettify from '../utils/prettify.mjs';
9
+ import { ROOT_SEGMENT_FILE_NAME } from '../dev/writeOneSegmentSchemaFile.mjs';
10
+ import { compileJSONSchemaToTypeScriptType } from '../utils/compileJSONSchemaToTypeScriptType.mjs';
11
+ import getTemplateClientImports from './getTemplateClientImports.mjs';
12
+ import getMetaSchema from '../getProjectInfo/getMetaSchema.mjs';
13
+ export function normalizeOutTemplatePath(out, packageJson) {
14
+ return out.replace('[package_name]', packageJson.name?.replace(/-/g, '_') ?? 'my_package_name');
15
+ }
16
+ export default async function writeOneClientFile({ cwd, projectInfo, clientTemplateFile, fullSchema, prettifyClient, segmentName,
17
+ // imports,
18
+ templateContent, matterResult: { data, content }, openAPIObject, package: packageJson, readme, samples, reExports, isEnsuringClient, outCwdRelativeDir, templateDef, locatedSegments, isNodeNextResolution, hasMixins, isVovkProject, vovkCliPackage, isBundle, origin, configKey, cliSchemaPath, projectConfig, }) {
19
+ const { config } = projectInfo;
20
+ const { templateFilePath, relativeDir } = clientTemplateFile;
21
+ const locatedSegmentsByName = _.keyBy(locatedSegments, 'segmentName');
22
+ const outPath = normalizeOutTemplatePath(path.resolve(cwd, outCwdRelativeDir, typeof segmentName === 'string' ? segmentName || ROOT_SEGMENT_FILE_NAME : '', relativeDir, path.basename(templateFilePath).replace('.ejs', '')), packageJson);
23
+ let placeholder = templateFilePath.endsWith('.json.ejs')
24
+ ? ''
25
+ : `// This is a temporary placeholder to avoid compilation errors if client is imported before it's generated.
26
+ // If you still see this text, the client is not generated yet because of an unknown problem.
27
+ // Feel free to report an issue at https://github.com/finom/vovk/issues`;
28
+ placeholder = outPath.endsWith('.py') ? placeholder.replace(/\/\//g, '#') : placeholder;
29
+ const getFirstLineBanner = (type = 'c') => {
30
+ const text = `Generated by vovk-cli v${vovkCliPackage.version} at ${new Date().toISOString()}`;
31
+ switch (type) {
32
+ case 'html':
33
+ return `<!-- ${text} -->`;
34
+ case 'sh':
35
+ return `# ${text}`;
36
+ case 'c':
37
+ return `// ${text}`;
38
+ }
39
+ };
40
+ reExports = _.mapValues(reExports ?? {}, (p) => p.startsWith('.')
41
+ ? path.relative(path.join(outCwdRelativeDir, typeof segmentName === 'string' ? segmentName || ROOT_SEGMENT_FILE_NAME : '.'), path.resolve(cwd, p))
42
+ : p);
43
+ // Data for the EJS templates:
44
+ const t = {
45
+ _, // lodash
46
+ hasMixins,
47
+ isVovkProject,
48
+ package: packageJson,
49
+ readme,
50
+ samples,
51
+ reExports,
52
+ openapi: openAPIObject,
53
+ ROOT_SEGMENT_FILE_NAME,
54
+ apiRoot: origin ? `${origin}/${config.rootEntry}` : undefined,
55
+ imports: {},
56
+ schema: fullSchema,
57
+ config: projectConfig,
58
+ VovkSchemaIdEnum,
59
+ createCodeSamples,
60
+ compileJSONSchemaToTypeScriptType,
61
+ YAML,
62
+ TOML,
63
+ getFirstLineBanner,
64
+ publicMeta: getMetaSchema({ config: projectConfig, useEmitConfig: true }),
65
+ nodeNextResolutionExt: {
66
+ ts: isNodeNextResolution ? '.ts' : '',
67
+ js: isNodeNextResolution ? '.js' : '',
68
+ cjs: isNodeNextResolution ? '.cjs' : '',
69
+ mjs: isNodeNextResolution ? '.mjs' : '',
70
+ },
71
+ schemaOutDir: typeof segmentName === 'string'
72
+ ? path.relative(path.join(outCwdRelativeDir, segmentName || ROOT_SEGMENT_FILE_NAME), cliSchemaPath ?? config.schemaOutDir)
73
+ : path.relative(outCwdRelativeDir, cliSchemaPath ?? config.schemaOutDir),
74
+ commonImports: getTemplateClientImports({
75
+ config: projectConfig,
76
+ fullSchema,
77
+ isBundle,
78
+ outCwdRelativeDir,
79
+ segmentName,
80
+ outputConfigs: [templateDef.outputConfig ?? {}],
81
+ })['composedClient'],
82
+ segmentImports: Object.fromEntries(Object.values(fullSchema.segments).map(({ segmentName: sName }) => {
83
+ const clientImports = getTemplateClientImports({
84
+ config: projectConfig,
85
+ fullSchema,
86
+ segmentName: sName,
87
+ isBundle,
88
+ outCwdRelativeDir,
89
+ outputConfigs: [templateDef.outputConfig ?? {}],
90
+ });
91
+ const imports = configKey === 'composedClient' ? clientImports['composedClient'] : clientImports['segmentedClient'][sName];
92
+ return [sName, imports];
93
+ })),
94
+ segmentMeta: Object.fromEntries(Object.values(fullSchema.segments).map(({ segmentName: sName, forceApiRoot }) => {
95
+ const { routeFilePath = null } = locatedSegmentsByName[sName] ?? {};
96
+ const segmentImportPath = routeFilePath
97
+ ? path.relative(path.resolve(cwd, outCwdRelativeDir, typeof segmentName === 'string' ? segmentName || ROOT_SEGMENT_FILE_NAME : '.'), path.resolve(cwd, routeFilePath))
98
+ : null;
99
+ const segmentConfig = {
100
+ ...config.outputConfig.segments?.[sName],
101
+ // ...templateDef.outputConfig?.segments?.[sName],
102
+ };
103
+ const { origin: segmentConfigOrigin, rootEntry: segmentConfigRootEntry, segmentNameOverride } = segmentConfig;
104
+ return [
105
+ sName,
106
+ {
107
+ forceApiRoot: forceApiRoot ??
108
+ (segmentConfigOrigin || segmentConfigRootEntry
109
+ ? `${segmentConfigOrigin ?? origin}/${segmentConfigRootEntry ?? config.rootEntry}`
110
+ : null),
111
+ routeFilePath,
112
+ segmentImportPath,
113
+ segmentNameOverride,
114
+ },
115
+ ];
116
+ })),
117
+ };
118
+ if (data.imports instanceof Array) {
119
+ for (const imp of data.imports) {
120
+ t.imports = {
121
+ [imp]: await import(imp),
122
+ };
123
+ }
124
+ }
125
+ // Render the template
126
+ let rendered = templateFilePath.endsWith('.ejs')
127
+ ? await ejs.render(content, { t }, {
128
+ filename: templateFilePath,
129
+ async: true,
130
+ })
131
+ : templateContent;
132
+ // Optionally prettify
133
+ if (prettifyClient) {
134
+ rendered = await prettify(rendered, outPath);
135
+ }
136
+ if (isEnsuringClient) {
137
+ rendered = rendered + `\n\n${placeholder}`;
138
+ }
139
+ // Read existing file content to compare
140
+ const existingContent = await fs.readFile(outPath, 'utf-8').catch(() => null);
141
+ // Determine if we need to rewrite the file, ignore 1st line
142
+ const needsWriting = isEnsuringClient
143
+ ? !existingContent
144
+ : !existingContent ||
145
+ existingContent.trim().split('\n').slice(1).join('\n') !== rendered.trim().split('\n').slice(1).join('\n');
146
+ if (needsWriting) {
147
+ await fs.mkdir(path.dirname(outPath), { recursive: true });
148
+ await fs.writeFile(outPath, rendered, 'utf-8');
149
+ }
150
+ return { written: needsWriting };
151
+ }
@@ -0,0 +1,5 @@
1
+ export default function getConfigAbsolutePaths({ cwd, configPath, relativePath, }: {
2
+ cwd: string;
3
+ configPath?: string;
4
+ relativePath?: string;
5
+ }): Promise<string[]>;
@@ -1,6 +1,9 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- export default async function getConfigAbsolutePaths({ cwd, relativePath, }) {
3
+ export default async function getConfigAbsolutePaths({ cwd, configPath, relativePath, }) {
4
+ if (configPath) {
5
+ return [path.resolve(cwd, configPath)];
6
+ }
4
7
  const rootDir = path.resolve(cwd, relativePath || '');
5
8
  const baseName = 'vovk.config';
6
9
  const extensions = ['cjs', 'mjs', 'js'];
@@ -1,3 +1,3 @@
1
1
  export default function getRelativeSrcRoot({ cwd }: {
2
2
  cwd: string;
3
- }): Promise<"." | "./src">;
3
+ }): Promise<"." | "./src" | null>;
@@ -1,5 +1,5 @@
1
1
  import path from 'node:path';
2
- import getFileSystemEntryType, { FileSystemEntryType } from '../utils/getFileSystemEntryType.mjs';
2
+ import getFileSystemEntryType, { FileSystemEntryType } from '../../utils/getFileSystemEntryType.mjs';
3
3
  export default async function getRelativeSrcRoot({ cwd }) {
4
4
  // Next.js Docs: src/app or src/pages will be ignored if app or pages are present in the root directory.
5
5
  if ((await getFileSystemEntryType(path.join(cwd, 'app'))) === FileSystemEntryType.DIRECTORY) {
@@ -8,5 +8,5 @@ export default async function getRelativeSrcRoot({ cwd }) {
8
8
  else if ((await getFileSystemEntryType(path.join(cwd, 'src/app'))) === FileSystemEntryType.DIRECTORY) {
9
9
  return './src';
10
10
  }
11
- throw new Error(`${cwd} Could not find app router directory. Check Next.js docs for more info.`);
11
+ return null;
12
12
  }
@@ -0,0 +1,24 @@
1
+ import type { VovkStrictConfig } from 'vovk';
2
+ export declare enum BuiltInTemplateName {
3
+ ts = "ts",
4
+ cjs = "cjs",
5
+ mjs = "mjs",
6
+ schemaTs = "schemaTs",
7
+ schemaCjs = "schemaCjs",
8
+ schemaJson = "schemaJson",
9
+ openapiTs = "openapiTs",
10
+ openapiCjs = "openapiCjs",
11
+ openapiJson = "openapiJson",
12
+ readme = "readme",
13
+ packageJson = "packageJson",
14
+ mixins = "mixins",
15
+ rsSrc = "rsSrc",
16
+ rsPkg = "rsPkg",
17
+ rsReadme = "rsReadme",
18
+ rs = "rs",
19
+ pySrc = "pySrc",
20
+ pyPkg = "pyPkg",
21
+ pyReadme = "pyReadme",
22
+ py = "py"
23
+ }
24
+ export default function getTemplateDefs(userTemplateDefs?: VovkStrictConfig['clientTemplateDefs']): VovkStrictConfig['clientTemplateDefs'];
@@ -0,0 +1,165 @@
1
+ export var BuiltInTemplateName;
2
+ (function (BuiltInTemplateName) {
3
+ // ts/js
4
+ BuiltInTemplateName["ts"] = "ts";
5
+ BuiltInTemplateName["cjs"] = "cjs";
6
+ BuiltInTemplateName["mjs"] = "mjs";
7
+ // schema
8
+ BuiltInTemplateName["schemaTs"] = "schemaTs";
9
+ BuiltInTemplateName["schemaCjs"] = "schemaCjs";
10
+ BuiltInTemplateName["schemaJson"] = "schemaJson";
11
+ // openapi
12
+ BuiltInTemplateName["openapiTs"] = "openapiTs";
13
+ BuiltInTemplateName["openapiCjs"] = "openapiCjs";
14
+ BuiltInTemplateName["openapiJson"] = "openapiJson";
15
+ // misc
16
+ BuiltInTemplateName["readme"] = "readme";
17
+ BuiltInTemplateName["packageJson"] = "packageJson";
18
+ BuiltInTemplateName["mixins"] = "mixins";
19
+ // other languages (packages installed separately)
20
+ BuiltInTemplateName["rsSrc"] = "rsSrc";
21
+ BuiltInTemplateName["rsPkg"] = "rsPkg";
22
+ BuiltInTemplateName["rsReadme"] = "rsReadme";
23
+ BuiltInTemplateName["rs"] = "rs";
24
+ BuiltInTemplateName["pySrc"] = "pySrc";
25
+ BuiltInTemplateName["pyPkg"] = "pyPkg";
26
+ BuiltInTemplateName["pyReadme"] = "pyReadme";
27
+ BuiltInTemplateName["py"] = "py";
28
+ })(BuiltInTemplateName || (BuiltInTemplateName = {}));
29
+ export default function getTemplateDefs(userTemplateDefs = {}) {
30
+ const defs = {};
31
+ const builtInDefs = {
32
+ [BuiltInTemplateName.openapiTs]: {
33
+ templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.openapiTs}/`,
34
+ requires: {
35
+ [BuiltInTemplateName.openapiJson]: './',
36
+ },
37
+ },
38
+ [BuiltInTemplateName.openapiCjs]: {
39
+ templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.openapiCjs}/`,
40
+ requires: {
41
+ [BuiltInTemplateName.openapiJson]: './',
42
+ },
43
+ },
44
+ [BuiltInTemplateName.openapiJson]: {
45
+ templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.openapiJson}/`,
46
+ },
47
+ [BuiltInTemplateName.ts]: {
48
+ templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.ts}/`,
49
+ requires: {
50
+ [BuiltInTemplateName.schemaTs]: './',
51
+ [BuiltInTemplateName.openapiTs]: './',
52
+ [BuiltInTemplateName.mixins]: './', // used conditionally if OpenAPI mixins are used
53
+ },
54
+ },
55
+ [BuiltInTemplateName.cjs]: {
56
+ templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.cjs}/`,
57
+ requires: {
58
+ [BuiltInTemplateName.schemaCjs]: './',
59
+ [BuiltInTemplateName.openapiCjs]: './',
60
+ [BuiltInTemplateName.mixins]: './', // used conditionally if OpenAPI mixins are used
61
+ },
62
+ },
63
+ [BuiltInTemplateName.mjs]: {
64
+ templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.mjs}/`,
65
+ requires: {
66
+ [BuiltInTemplateName.schemaCjs]: './',
67
+ [BuiltInTemplateName.openapiCjs]: './',
68
+ [BuiltInTemplateName.mixins]: './', // used conditionally if OpenAPI mixins are used
69
+ },
70
+ },
71
+ [BuiltInTemplateName.schemaTs]: {
72
+ templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.schemaTs}/`,
73
+ },
74
+ [BuiltInTemplateName.schemaCjs]: {
75
+ templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.schemaCjs}/`,
76
+ },
77
+ [BuiltInTemplateName.schemaJson]: {
78
+ templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.schemaJson}/`,
79
+ },
80
+ [BuiltInTemplateName.readme]: {
81
+ templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.readme}/`,
82
+ },
83
+ [BuiltInTemplateName.packageJson]: {
84
+ templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.packageJson}/`,
85
+ },
86
+ [BuiltInTemplateName.mixins]: {
87
+ templatePath: `vovk-cli/client-templates/${BuiltInTemplateName.mixins}/`,
88
+ },
89
+ [BuiltInTemplateName.rsSrc]: {
90
+ templatePath: `vovk-rust/client-templates/${BuiltInTemplateName.rsSrc}/`,
91
+ requires: {
92
+ [BuiltInTemplateName.schemaJson]: './',
93
+ },
94
+ },
95
+ [BuiltInTemplateName.rsPkg]: {
96
+ templatePath: `vovk-rust/client-templates/${BuiltInTemplateName.rsPkg}/`,
97
+ },
98
+ [BuiltInTemplateName.rsReadme]: {
99
+ templatePath: `vovk-rust/client-templates/${BuiltInTemplateName.rsReadme}/`,
100
+ },
101
+ [BuiltInTemplateName.rs]: {
102
+ composedClient: {
103
+ outDir: 'dist_rust',
104
+ },
105
+ requires: {
106
+ [BuiltInTemplateName.rsSrc]: './src/',
107
+ [BuiltInTemplateName.rsPkg]: './',
108
+ [BuiltInTemplateName.rsReadme]: './',
109
+ },
110
+ },
111
+ [BuiltInTemplateName.pySrc]: {
112
+ templatePath: `vovk-python/client-templates/${BuiltInTemplateName.pySrc}/`,
113
+ requires: {
114
+ [BuiltInTemplateName.schemaJson]: './',
115
+ },
116
+ },
117
+ [BuiltInTemplateName.pyPkg]: {
118
+ templatePath: `vovk-python/client-templates/${BuiltInTemplateName.pyPkg}/`,
119
+ },
120
+ [BuiltInTemplateName.pyReadme]: {
121
+ templatePath: `vovk-python/client-templates/${BuiltInTemplateName.pyReadme}/`,
122
+ },
123
+ [BuiltInTemplateName.py]: {
124
+ composedClient: {
125
+ outDir: 'dist_python',
126
+ },
127
+ requires: {
128
+ [BuiltInTemplateName.pySrc]: './src/[package_name]/',
129
+ [BuiltInTemplateName.pyPkg]: './',
130
+ [BuiltInTemplateName.pyReadme]: './',
131
+ },
132
+ },
133
+ };
134
+ for (const [name, templateDef] of Object.entries(userTemplateDefs)) {
135
+ if ('extends' in templateDef) {
136
+ if (templateDef.extends) {
137
+ const builtIn = builtInDefs[templateDef.extends];
138
+ if (!builtIn) {
139
+ throw new Error(`Unknown template extends: ${templateDef.extends}`);
140
+ }
141
+ defs[name] = {
142
+ ...builtIn,
143
+ ...templateDef,
144
+ composedClient: {
145
+ ...builtIn.composedClient,
146
+ ...templateDef.composedClient,
147
+ },
148
+ segmentedClient: {
149
+ ...builtIn.segmentedClient,
150
+ ...templateDef.segmentedClient,
151
+ },
152
+ outputConfig: {
153
+ ...builtIn.outputConfig,
154
+ ...templateDef.outputConfig,
155
+ },
156
+ // 'requires' and other props will be overridden
157
+ };
158
+ }
159
+ }
160
+ else {
161
+ defs[name] = templateDef;
162
+ }
163
+ }
164
+ return { ...builtInDefs, ...defs };
165
+ }
@@ -1,5 +1,6 @@
1
- import type { VovkConfig } from '../types.mjs';
2
- declare function getUserConfig({ cwd, }: {
1
+ import type { VovkConfig } from 'vovk';
2
+ declare function getUserConfig({ configPath: givenConfigPath, cwd, }: {
3
+ configPath?: string;
3
4
  cwd: string;
4
5
  }): Promise<{
5
6
  userConfig: VovkConfig | null;
@@ -1,11 +1,11 @@
1
1
  import { pathToFileURL } from 'node:url';
2
2
  import getConfigAbsolutePaths from './getConfigAbsolutePaths.mjs';
3
3
  import importUncachedModule from './importUncachedModule.mjs';
4
- async function getUserConfig({ cwd, }) {
5
- const configAbsolutePaths = await getConfigAbsolutePaths({ cwd });
4
+ async function getUserConfig({ configPath: givenConfigPath, cwd, }) {
5
+ const configAbsolutePaths = await getConfigAbsolutePaths({ configPath: givenConfigPath, cwd });
6
6
  let userConfig;
7
7
  if (!configAbsolutePaths.length) {
8
- return { userConfig: null, configAbsolutePaths, error: new Error('No config file found') };
8
+ return { userConfig: null, configAbsolutePaths };
9
9
  }
10
10
  const configPath = configAbsolutePaths[0];
11
11
  // TODO explain
@@ -1,14 +1,11 @@
1
1
  // importUncachedModule.js
2
2
  import { Worker } from 'node:worker_threads';
3
3
  import path from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
4
  import './importUncachedModuleWorker.mjs'; // required for TS compilation
6
5
  function importUncachedModule(modulePath) {
7
6
  return new Promise((resolve, reject) => {
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = path.dirname(__filename);
10
7
  // Resolve the path to the worker script
11
- const workerPath = path.resolve(__dirname, 'importUncachedModuleWorker.mjs');
8
+ const workerPath = path.resolve(import.meta.dirname, 'importUncachedModuleWorker.mjs');
12
9
  // Initialize the worker thread
13
10
  const worker = new Worker(workerPath, {
14
11
  workerData: { modulePath },