vovk-cli 0.0.1-draft.98 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +24 -16
- package/client-templates/jsBase/index.d.ts.ejs +21 -0
- package/client-templates/jsBase/index.js.ejs +18 -0
- package/client-templates/mixins/mixins.d.ts.ejs +64 -0
- package/client-templates/mixins/mixins.json.ejs +1 -0
- package/client-templates/openapiJs/openapi.d.ts.ejs +4 -0
- package/client-templates/openapiJs/openapi.js.ejs +4 -0
- package/client-templates/openapiJson/openapi.json.ejs +1 -0
- package/client-templates/openapiTs/openapi.ts.ejs +4 -0
- package/client-templates/packageJson/package.json.ejs +1 -0
- package/client-templates/readme/README.md.ejs +39 -0
- package/client-templates/schemaJs/schema.d.ts.ejs +10 -0
- package/client-templates/schemaJs/schema.js.ejs +29 -0
- package/client-templates/schemaJson/schema.json.ejs +1 -0
- package/client-templates/schemaTs/schema.ts.ejs +28 -0
- package/client-templates/tsBase/index.ts.ejs +27 -0
- package/dist/bundle/index.d.mts +8 -0
- package/dist/bundle/index.mjs +76 -0
- package/dist/dev/{diffSchema.d.mts → diffSegmentSchema.d.mts} +3 -3
- package/dist/dev/{diffSchema.mjs → diffSegmentSchema.mjs} +1 -1
- package/dist/dev/ensureSchemaFiles.d.mts +1 -1
- package/dist/dev/ensureSchemaFiles.mjs +15 -46
- package/dist/dev/index.d.mts +2 -0
- package/dist/dev/index.mjs +113 -64
- package/dist/dev/logDiffResult.d.mts +2 -2
- package/dist/dev/logDiffResult.mjs +6 -6
- package/dist/dev/writeMetaJson.d.mts +2 -0
- package/dist/dev/writeMetaJson.mjs +19 -0
- package/dist/dev/writeOneSegmentSchemaFile.d.mts +12 -0
- package/dist/dev/{writeOneSchemaFile.mjs → writeOneSegmentSchemaFile.mjs} +11 -8
- package/dist/generate/ensureClient.d.mts +2 -4
- package/dist/generate/ensureClient.mjs +26 -28
- package/dist/generate/generate.d.mts +13 -0
- package/dist/generate/generate.mjs +306 -0
- package/dist/generate/getClientTemplateFiles.d.mts +20 -0
- package/dist/generate/getClientTemplateFiles.mjs +85 -0
- package/dist/generate/getProjectFullSchema.d.mts +9 -0
- package/dist/generate/getProjectFullSchema.mjs +64 -0
- package/dist/generate/getTemplateClientImports.d.mts +18 -0
- package/dist/generate/getTemplateClientImports.mjs +36 -0
- package/dist/generate/index.d.mts +32 -12
- package/dist/generate/index.mjs +177 -85
- package/dist/generate/writeOneClientFile.d.mts +43 -0
- package/dist/generate/writeOneClientFile.mjs +150 -0
- package/dist/getProjectInfo/getConfig/getConfigAbsolutePaths.d.mts +5 -0
- package/dist/getProjectInfo/{getConfigAbsolutePaths.mjs → getConfig/getConfigAbsolutePaths.mjs} +4 -1
- package/dist/getProjectInfo/getConfig/getRelativeSrcRoot.d.mts +3 -0
- package/dist/getProjectInfo/{getRelativeSrcRoot.mjs → getConfig/getRelativeSrcRoot.mjs} +3 -3
- package/dist/getProjectInfo/getConfig/getTemplateDefs.d.mts +25 -0
- package/dist/getProjectInfo/getConfig/getTemplateDefs.mjs +168 -0
- package/dist/getProjectInfo/getConfig/getUserConfig.d.mts +9 -0
- package/dist/getProjectInfo/getConfig/getUserConfig.mjs +143 -0
- package/dist/getProjectInfo/getConfig/index.d.mts +15 -0
- package/dist/getProjectInfo/getConfig/index.mjs +92 -0
- package/dist/getProjectInfo/getMetaSchema.d.mts +4 -0
- package/dist/getProjectInfo/getMetaSchema.mjs +12 -0
- package/dist/getProjectInfo/index.d.mts +12 -16
- package/dist/getProjectInfo/index.mjs +23 -29
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +119 -40
- package/dist/init/checkTSConfigForExperimentalDecorators.d.mts +1 -1
- package/dist/init/checkTSConfigForExperimentalDecorators.mjs +2 -2
- package/dist/init/createConfig.d.mts +5 -3
- package/dist/init/createConfig.mjs +77 -27
- package/dist/init/index.d.mts +2 -2
- package/dist/init/index.mjs +117 -102
- package/dist/init/installDependencies.d.mts +8 -6
- package/dist/init/installDependencies.mjs +7 -5
- package/dist/init/logUpdateDependenciesError.d.mts +6 -6
- package/dist/init/logUpdateDependenciesError.mjs +8 -4
- package/dist/init/updateDependenciesWithoutInstalling.d.mts +2 -2
- package/dist/init/updateDependenciesWithoutInstalling.mjs +41 -11
- package/dist/init/updateNPMScripts.d.mts +6 -1
- package/dist/init/updateNPMScripts.mjs +9 -5
- package/dist/init/updateTypeScriptConfig.d.mts +4 -1
- package/dist/init/updateTypeScriptConfig.mjs +12 -8
- package/dist/new/addClassToSegmentCode.d.mts +1 -1
- package/dist/new/addClassToSegmentCode.mjs +3 -3
- package/dist/new/addCommonTerms.d.mts +1 -1
- package/dist/new/addCommonTerms.mjs +1 -1
- package/dist/new/index.d.mts +2 -1
- package/dist/new/index.mjs +6 -5
- package/dist/new/newModule.d.mts +5 -3
- package/dist/new/newModule.mjs +31 -25
- package/dist/new/newSegment.d.mts +5 -2
- package/dist/new/newSegment.mjs +23 -16
- package/dist/new/render.d.mts +6 -3
- package/dist/new/render.mjs +15 -14
- package/dist/types.d.mts +64 -61
- package/dist/utils/chalkHighlightThing.d.mts +1 -1
- package/dist/utils/chalkHighlightThing.mjs +1 -1
- package/dist/utils/compileJSONSchemaToTypeScriptType.d.mts +5 -0
- package/dist/utils/compileJSONSchemaToTypeScriptType.mjs +9 -0
- package/dist/utils/compileTs.d.mts +12 -0
- package/dist/utils/compileTs.mjs +261 -0
- package/dist/utils/debounceWithArgs.d.mts +1 -2
- package/dist/utils/debounceWithArgs.mjs +2 -1
- package/dist/utils/formatLoggedSegmentName.d.mts +3 -1
- package/dist/utils/formatLoggedSegmentName.mjs +4 -3
- package/dist/utils/generateFnName.d.mts +23 -0
- package/dist/utils/generateFnName.mjs +76 -0
- package/dist/utils/getAvailablePort.d.mts +1 -2
- package/dist/utils/getAvailablePort.mjs +1 -2
- package/dist/utils/getFileSystemEntryType.d.mts +1 -1
- package/dist/utils/getFileSystemEntryType.mjs +1 -1
- package/dist/utils/getLogger.d.mts +1 -1
- package/dist/utils/getLogger.mjs +1 -1
- package/dist/utils/getNPMPackageMetadata.d.mts +1 -1
- package/dist/utils/getNPMPackageMetadata.mjs +1 -1
- package/dist/utils/getPackageJson.d.mts +3 -0
- package/dist/utils/getPackageJson.mjs +23 -0
- package/dist/utils/getPublicModuleNameFromPath.d.mts +4 -0
- package/dist/utils/getPublicModuleNameFromPath.mjs +9 -0
- package/dist/utils/locateSegments.d.mts +12 -0
- package/dist/{locateSegments.mjs → utils/locateSegments.mjs} +14 -7
- package/dist/utils/normalizeOpenAPIMixin.d.mts +15 -0
- package/dist/utils/normalizeOpenAPIMixin.mjs +96 -0
- package/dist/utils/pickSegmentFullSchema.d.mts +3 -0
- package/dist/utils/pickSegmentFullSchema.mjs +15 -0
- package/dist/utils/prettify.d.mts +1 -1
- package/dist/utils/prettify.mjs +1 -1
- package/dist/utils/removeUnlistedDirectories.d.mts +9 -0
- package/dist/utils/removeUnlistedDirectories.mjs +60 -0
- package/dist/utils/resolveAbsoluteModulePath.d.mts +2 -0
- package/dist/utils/resolveAbsoluteModulePath.mjs +32 -0
- package/dist/utils/updateConfigProperty.d.mts +2 -0
- package/dist/utils/updateConfigProperty.mjs +132 -0
- package/module-templates/arktype/controller.ts.ejs +90 -0
- package/module-templates/type/controller.ts.ejs +80 -0
- package/module-templates/type/service.ts.ejs +43 -0
- package/module-templates/valibot/controller.ts.ejs +91 -0
- package/module-templates/zod/controller.ts.ejs +98 -0
- package/package.json +50 -25
- package/client-templates/main/main.cjs.ejs +0 -15
- package/client-templates/main/main.d.cts.ejs +0 -14
- package/client-templates/module/module.d.mts.ejs +0 -14
- package/client-templates/module/module.mjs.ejs +0 -24
- package/client-templates/python/__init__.py +0 -276
- package/client-templates/ts/index.ts.ejs +0 -25
- package/dist/dev/isSchemaEmpty.d.mts +0 -2
- package/dist/dev/isSchemaEmpty.mjs +0 -4
- package/dist/dev/writeOneSchemaFile.d.mts +0 -12
- package/dist/generate/getClientTemplates.d.mts +0 -16
- package/dist/generate/getClientTemplates.mjs +0 -42
- package/dist/getProjectInfo/getConfig.d.mts +0 -11
- package/dist/getProjectInfo/getConfig.mjs +0 -35
- package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +0 -4
- package/dist/getProjectInfo/getRelativeSrcRoot.d.mts +0 -3
- package/dist/getProjectInfo/getUserConfig.d.mts +0 -9
- package/dist/getProjectInfo/getUserConfig.mjs +0 -27
- package/dist/getProjectInfo/importUncachedModule.d.mts +0 -3
- package/dist/getProjectInfo/importUncachedModule.mjs +0 -40
- package/dist/getProjectInfo/importUncachedModuleWorker.d.mts +0 -1
- package/dist/getProjectInfo/importUncachedModuleWorker.mjs +0 -25
- package/dist/init/getTemplateFilesFromPackage.d.mts +0 -7
- package/dist/init/getTemplateFilesFromPackage.mjs +0 -59
- package/dist/initProgram.d.mts +0 -2
- package/dist/initProgram.mjs +0 -22
- package/dist/locateSegments.d.mts +0 -11
- package/dist/postinstall.d.mts +0 -1
- package/dist/postinstall.mjs +0 -21
- package/templates/controller.ejs +0 -50
- package/templates/service.ejs +0 -27
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import * as YAML from 'yaml';
|
|
4
|
+
import { chalkHighlightThing } from './chalkHighlightThing.mjs';
|
|
5
|
+
import camelCase from 'lodash/camelCase.js';
|
|
6
|
+
import { generateFnName } from './generateFnName.mjs';
|
|
7
|
+
const normalizeGetModuleName = (getModuleName) => {
|
|
8
|
+
if (typeof getModuleName === 'string') {
|
|
9
|
+
const moduleName = getModuleName;
|
|
10
|
+
getModuleName = () => moduleName;
|
|
11
|
+
}
|
|
12
|
+
else if (typeof getModuleName !== 'function') {
|
|
13
|
+
throw new Error('getModuleName must be a function or one of the predefined strings');
|
|
14
|
+
}
|
|
15
|
+
return getModuleName;
|
|
16
|
+
};
|
|
17
|
+
const normalizeGetMethodName = (getMethodName) => {
|
|
18
|
+
if (getMethodName === 'camel-case-operation-id') {
|
|
19
|
+
getMethodName = ({ operationObject }) => {
|
|
20
|
+
const operationId = operationObject.operationId;
|
|
21
|
+
if (!operationId) {
|
|
22
|
+
throw new Error('Operation ID is required for camel-case method name generation');
|
|
23
|
+
}
|
|
24
|
+
return camelCase(operationId);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
else if (getMethodName === 'auto') {
|
|
28
|
+
getMethodName = ({ operationObject, method, path }) => {
|
|
29
|
+
const operationId = operationObject.operationId;
|
|
30
|
+
const isCamelCase = operationId && /^[a-z][a-zA-Z0-9]*$/.test(operationId);
|
|
31
|
+
const isSnakeCase = operationId && /^[a-z][a-z0-9_]+$/.test(operationId);
|
|
32
|
+
return isCamelCase ? operationId : isSnakeCase ? camelCase(operationId) : generateFnName(method, path);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
else if (typeof getMethodName !== 'function') {
|
|
36
|
+
throw new Error('getMethodName must be a function or one of the predefined strings');
|
|
37
|
+
}
|
|
38
|
+
return getMethodName;
|
|
39
|
+
};
|
|
40
|
+
async function getOpenApiSpecLocal(openApiSpecFilePath, cwd) {
|
|
41
|
+
const openApiSpecAbsolutePath = path.resolve(cwd, openApiSpecFilePath);
|
|
42
|
+
const fileName = path.basename(openApiSpecAbsolutePath);
|
|
43
|
+
if (!fileName.endsWith('.json') && !fileName.endsWith('.yaml') && !fileName.endsWith('.yml')) {
|
|
44
|
+
throw new Error(`Invalid OpenAPI spec file format: ${fileName}. Please provide a JSON or YAML file.`);
|
|
45
|
+
}
|
|
46
|
+
const openApiSpecContent = await fs.readFile(openApiSpecAbsolutePath, 'utf8');
|
|
47
|
+
return (fileName.endsWith('.json') ? JSON.parse(openApiSpecContent) : YAML.parse(openApiSpecContent));
|
|
48
|
+
}
|
|
49
|
+
async function getOpenApiSpecRemote({ cwd, url, fallback, log, }) {
|
|
50
|
+
const resp = await fetch(url);
|
|
51
|
+
const text = await resp.text();
|
|
52
|
+
if (!resp.ok) {
|
|
53
|
+
if (fallback) {
|
|
54
|
+
log.warn(`Failed to fetch OpenAPI spec from ${chalkHighlightThing(url)}: ${resp.status} ${resp.statusText}. Falling back to ${chalkHighlightThing(fallback)}`);
|
|
55
|
+
return getOpenApiSpecLocal(fallback, cwd);
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`Failed to fetch OpenAPI spec from ${chalkHighlightThing(url)}: ${resp.status} ${resp.statusText}`);
|
|
58
|
+
}
|
|
59
|
+
if (fallback) {
|
|
60
|
+
const fallbackAbsolutePath = path.resolve(cwd, fallback);
|
|
61
|
+
const existingFallback = await fs.readFile(fallbackAbsolutePath, 'utf8').catch(() => null);
|
|
62
|
+
if (existingFallback !== text) {
|
|
63
|
+
await fs.mkdir(path.dirname(fallbackAbsolutePath), { recursive: true });
|
|
64
|
+
await fs.writeFile(fallbackAbsolutePath, text);
|
|
65
|
+
log.info(`Saved OpenAPI spec to fallback file ${chalkHighlightThing(fallbackAbsolutePath)}`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
log.debug(`OpenAPI spec at ${chalkHighlightThing(url)} is unchanged. Skipping write to fallback file ${chalkHighlightThing(fallbackAbsolutePath)}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return (text.trim().startsWith('{') || text.trim().startsWith('[') ? JSON.parse(text) : YAML.parse(text));
|
|
72
|
+
}
|
|
73
|
+
export async function normalizeOpenAPIMixin({
|
|
74
|
+
// mixinName,
|
|
75
|
+
mixinModule, log, cwd = process.cwd(), }) {
|
|
76
|
+
const { source, getModuleName, getMethodName } = mixinModule;
|
|
77
|
+
let openAPIObject;
|
|
78
|
+
if ('url' in source) {
|
|
79
|
+
openAPIObject = await getOpenApiSpecRemote({ url: source.url, fallback: source.fallback, log, cwd });
|
|
80
|
+
}
|
|
81
|
+
else if ('file' in source) {
|
|
82
|
+
openAPIObject = await getOpenApiSpecLocal(source.file, cwd);
|
|
83
|
+
}
|
|
84
|
+
else if ('object' in source) {
|
|
85
|
+
openAPIObject = source.object;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
throw new Error('Invalid source type for OpenAPI configuration');
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
...mixinModule,
|
|
92
|
+
source: { object: openAPIObject },
|
|
93
|
+
getModuleName: normalizeGetModuleName(getModuleName),
|
|
94
|
+
getMethodName: normalizeGetMethodName(getMethodName),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { VovkSchemaIdEnum } from 'vovk/internal';
|
|
2
|
+
export function pickSegmentFullSchema(schema, segmentNames) {
|
|
3
|
+
return {
|
|
4
|
+
$schema: VovkSchemaIdEnum.SCHEMA,
|
|
5
|
+
meta: schema.meta,
|
|
6
|
+
segments: Object.fromEntries(segmentNames.map((segmentName) => [segmentName, schema.segments[segmentName]])),
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function omitSegmentFullSchema(schema, segmentNames) {
|
|
10
|
+
return {
|
|
11
|
+
$schema: VovkSchemaIdEnum.SCHEMA,
|
|
12
|
+
meta: schema.meta,
|
|
13
|
+
segments: Object.fromEntries(Object.entries(schema.segments).filter(([segmentName]) => !segmentNames.includes(segmentName))),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export declare function prettify(code: string, absoluteFilePath: string): Promise<string>;
|
package/dist/utils/prettify.mjs
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes all directories in a folder that aren't in the provided allowlist
|
|
3
|
+
* Supports nested directory paths like 'foo/bar/baz'
|
|
4
|
+
*
|
|
5
|
+
* @param folderPath - The path to the folder to process
|
|
6
|
+
* @param allowedDirs - Array of relative directory paths to keep
|
|
7
|
+
* @returns Promise that resolves when all operations are complete
|
|
8
|
+
*/
|
|
9
|
+
export declare function removeUnlistedDirectories(folderPath: string, allowedDirs: string[]): Promise<void>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { getFileSystemEntryType, FileSystemEntryType } from './getFileSystemEntryType.mjs';
|
|
4
|
+
/**
|
|
5
|
+
* Removes all directories in a folder that aren't in the provided allowlist
|
|
6
|
+
* Supports nested directory paths like 'foo/bar/baz'
|
|
7
|
+
*
|
|
8
|
+
* @param folderPath - The path to the folder to process
|
|
9
|
+
* @param allowedDirs - Array of relative directory paths to keep
|
|
10
|
+
* @returns Promise that resolves when all operations are complete
|
|
11
|
+
*/
|
|
12
|
+
export async function removeUnlistedDirectories(folderPath, allowedDirs) {
|
|
13
|
+
// Normalize all allowed paths to use the system-specific separator
|
|
14
|
+
const normalizedAllowedDirs = allowedDirs.map((dir) => dir.split('/').join(path.sep));
|
|
15
|
+
// Process the directory tree recursively
|
|
16
|
+
await processDirectory(folderPath, '', normalizedAllowedDirs);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Recursively processes directories to determine which should be kept or removed
|
|
20
|
+
*
|
|
21
|
+
* @param basePath - The absolute base path being processed
|
|
22
|
+
* @param relativePath - The current relative path from the base
|
|
23
|
+
* @param allowedDirs - Normalized list of allowed directory paths
|
|
24
|
+
*/
|
|
25
|
+
async function processDirectory(basePath, relativePath, allowedDirs) {
|
|
26
|
+
const currentDirPath = path.join(basePath, relativePath);
|
|
27
|
+
// check if the current path is a directory
|
|
28
|
+
const type = await getFileSystemEntryType(currentDirPath);
|
|
29
|
+
if (type !== FileSystemEntryType.DIRECTORY) {
|
|
30
|
+
// If it's not a directory, return early
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Read all entries in the current directory
|
|
34
|
+
const entries = await fs.readdir(currentDirPath, { withFileTypes: true });
|
|
35
|
+
// Process only directories
|
|
36
|
+
const dirEntries = entries.filter((entry) => entry.isDirectory());
|
|
37
|
+
// Check each directory
|
|
38
|
+
for (const dir of dirEntries) {
|
|
39
|
+
// Calculate the new relative path
|
|
40
|
+
const newRelativePath = relativePath ? path.join(relativePath, dir.name) : dir.name;
|
|
41
|
+
// Check if this directory or any of its subdirectories should be kept
|
|
42
|
+
const shouldKeep = allowedDirs.some((allowedDir) => {
|
|
43
|
+
// Direct match
|
|
44
|
+
if (allowedDir === newRelativePath)
|
|
45
|
+
return true;
|
|
46
|
+
// Check if it's a parent path of an allowed directory
|
|
47
|
+
// e.g. "foo" is a parent of "foo/bar/baz"
|
|
48
|
+
return allowedDir.startsWith(newRelativePath + path.sep);
|
|
49
|
+
});
|
|
50
|
+
if (shouldKeep) {
|
|
51
|
+
// Recursively process this directory's contents
|
|
52
|
+
await processDirectory(basePath, newRelativePath, allowedDirs);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// Remove this directory since it's not in the allowed list
|
|
56
|
+
const fullPath = path.join(basePath, newRelativePath);
|
|
57
|
+
await fs.rm(fullPath, { recursive: true, force: true });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { getPublicModuleNameFromPath } from './getPublicModuleNameFromPath.mjs';
|
|
4
|
+
// Returns the path up to and including the last occurrence of the given module name
|
|
5
|
+
export function getPathUpToModule(moduleName, fullPath) {
|
|
6
|
+
const idx = fullPath.lastIndexOf(moduleName);
|
|
7
|
+
if (idx === -1)
|
|
8
|
+
return moduleName;
|
|
9
|
+
return fullPath.slice(0, idx + moduleName.length);
|
|
10
|
+
}
|
|
11
|
+
export function resolveAbsoluteModulePath(modulePath, cwd) {
|
|
12
|
+
// If it's an absolute path or starts with '.' (relative), resolve it directly
|
|
13
|
+
if (modulePath.startsWith('/') || modulePath.startsWith('.')) {
|
|
14
|
+
return path.resolve(cwd, modulePath);
|
|
15
|
+
}
|
|
16
|
+
// For npm package names, use Node's module resolution algorithm
|
|
17
|
+
try {
|
|
18
|
+
const { moduleName, restPath } = getPublicModuleNameFromPath(modulePath);
|
|
19
|
+
if (!moduleName) {
|
|
20
|
+
throw new Error(`Invalid module path: ${modulePath}`);
|
|
21
|
+
}
|
|
22
|
+
const require = createRequire(import.meta.url);
|
|
23
|
+
const resolved = require.resolve(moduleName);
|
|
24
|
+
return path.resolve(getPathUpToModule(moduleName, path.dirname(resolved)), restPath);
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
// eslint-disable-next-line no-console
|
|
28
|
+
console.error(`Error resolving module path: ${modulePath}`, e);
|
|
29
|
+
// If resolution fails, fall back to the original behavior
|
|
30
|
+
return path.resolve(cwd, './node_modules', modulePath);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Project, QuoteKind, IndentationText, NewLineKind, SyntaxKind, Node, } from 'ts-morph';
|
|
2
|
+
export function updateConfigProperty(sourceCode, pathToProperty, newValue) {
|
|
3
|
+
const project = createProject();
|
|
4
|
+
const sourceFile = project.createSourceFile('config-temp.mts', sourceCode, { overwrite: true });
|
|
5
|
+
return mutateConfig(sourceFile, pathToProperty, newValue);
|
|
6
|
+
}
|
|
7
|
+
export function updateConfigFileProperty(absolutePathToTheFile, pathToProperty, newValue) {
|
|
8
|
+
const project = createProject();
|
|
9
|
+
const sourceFile = project.addSourceFileAtPath(absolutePathToTheFile);
|
|
10
|
+
const updated = mutateConfig(sourceFile, pathToProperty, newValue);
|
|
11
|
+
sourceFile.saveSync();
|
|
12
|
+
return updated;
|
|
13
|
+
}
|
|
14
|
+
function createProject() {
|
|
15
|
+
return new Project({
|
|
16
|
+
manipulationSettings: {
|
|
17
|
+
quoteKind: QuoteKind.Single,
|
|
18
|
+
indentationText: IndentationText.TwoSpaces,
|
|
19
|
+
newLineKind: NewLineKind.LineFeed,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function findPropertyAssignment(objectNode, key) {
|
|
24
|
+
const properties = objectNode.getProperties();
|
|
25
|
+
for (const prop of properties) {
|
|
26
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
27
|
+
const name = prop.getName();
|
|
28
|
+
// Handle both quoted and unquoted property names
|
|
29
|
+
const unquotedName = name.replace(/^['"]|['"]$/g, '');
|
|
30
|
+
if (unquotedName === key || name === key) {
|
|
31
|
+
return prop;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
function mutateConfig(sourceFile, pathToProperty, newValue) {
|
|
38
|
+
const variableDeclaration = sourceFile.getVariableDeclaration('config');
|
|
39
|
+
if (!variableDeclaration) {
|
|
40
|
+
throw new Error('config variable not found in the file.');
|
|
41
|
+
}
|
|
42
|
+
const initializer = variableDeclaration.getInitializer();
|
|
43
|
+
if (!initializer || !Node.isObjectLiteralExpression(initializer)) {
|
|
44
|
+
throw new Error('config is not initialized as an object literal.');
|
|
45
|
+
}
|
|
46
|
+
let currentNode = initializer;
|
|
47
|
+
for (let i = 0; i < pathToProperty.length; i++) {
|
|
48
|
+
const key = pathToProperty[i];
|
|
49
|
+
const isLastKey = i === pathToProperty.length - 1;
|
|
50
|
+
const property = findPropertyAssignment(currentNode, key);
|
|
51
|
+
if (!property) {
|
|
52
|
+
// Property does not exist
|
|
53
|
+
if (newValue === undefined) {
|
|
54
|
+
// Nothing to remove
|
|
55
|
+
return sourceFile.getFullText();
|
|
56
|
+
}
|
|
57
|
+
if (isLastKey) {
|
|
58
|
+
// Last key - add the final value
|
|
59
|
+
currentNode.addPropertyAssignment({
|
|
60
|
+
name: key,
|
|
61
|
+
initializer: (writer) => writeInitializer(writer, newValue),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// Need to create nested object
|
|
66
|
+
const newObjectAssignment = currentNode.addPropertyAssignment({
|
|
67
|
+
name: key,
|
|
68
|
+
initializer: '{}',
|
|
69
|
+
});
|
|
70
|
+
currentNode = newObjectAssignment.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// Property exists
|
|
75
|
+
const propInitializer = property.getInitializer();
|
|
76
|
+
if (isLastKey) {
|
|
77
|
+
// Last key - update or remove the value
|
|
78
|
+
if (newValue === undefined) {
|
|
79
|
+
property.remove();
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
property.setInitializer((writer) => writeInitializer(writer, newValue));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// Need to go deeper into existing object
|
|
87
|
+
if (propInitializer && Node.isObjectLiteralExpression(propInitializer)) {
|
|
88
|
+
currentNode = propInitializer;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
throw new Error(`Cannot traverse into non-object property at ${pathToProperty.slice(0, i + 1).join('.')}.`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return sourceFile.getFullText();
|
|
97
|
+
}
|
|
98
|
+
function writeInitializer(writer, value) {
|
|
99
|
+
if (typeof value === 'string') {
|
|
100
|
+
writer.quote(value);
|
|
101
|
+
}
|
|
102
|
+
else if (typeof value === 'number' || typeof value === 'boolean') {
|
|
103
|
+
writer.write(value.toString());
|
|
104
|
+
}
|
|
105
|
+
else if (typeof value === 'function') {
|
|
106
|
+
writer.write(value.toString());
|
|
107
|
+
}
|
|
108
|
+
else if (Array.isArray(value)) {
|
|
109
|
+
writer.write('[');
|
|
110
|
+
value.forEach((item, index) => {
|
|
111
|
+
if (index > 0)
|
|
112
|
+
writer.write(', ');
|
|
113
|
+
writeInitializer(writer, item);
|
|
114
|
+
});
|
|
115
|
+
writer.write(']');
|
|
116
|
+
}
|
|
117
|
+
else if (typeof value === 'object' && value !== null) {
|
|
118
|
+
writer.write('{');
|
|
119
|
+
const entries = Object.entries(value);
|
|
120
|
+
entries.forEach(([k, v], index) => {
|
|
121
|
+
if (index > 0)
|
|
122
|
+
writer.write(', ');
|
|
123
|
+
writer.write(k);
|
|
124
|
+
writer.write(': ');
|
|
125
|
+
writeInitializer(writer, v);
|
|
126
|
+
});
|
|
127
|
+
writer.write('}');
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
writer.write('null');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<% const vars = {
|
|
2
|
+
ModuleName: t.TheThing + 'Controller',
|
|
3
|
+
ServiceName: t.TheThing + 'Service',
|
|
4
|
+
}; %>
|
|
5
|
+
---
|
|
6
|
+
outDir: <%= t.defaultOutDir %>
|
|
7
|
+
fileName: <%= vars.ModuleName + '.ts' %>
|
|
8
|
+
sourceName: <%= vars.ModuleName %>
|
|
9
|
+
compiledName: <%= t.TheThing + 'RPC' %>
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
import { procedure, prefix, get, put, post, del, operation } from 'vovk';
|
|
13
|
+
import { type } from 'arktype';
|
|
14
|
+
<% if(t.withService) { %>
|
|
15
|
+
import <%= vars.ServiceName %> from './<%= vars.ServiceName %><%= t.nodeNextResolutionExt.ts %>';
|
|
16
|
+
<% } %>
|
|
17
|
+
|
|
18
|
+
@prefix('<%= t['the-things'] %>')
|
|
19
|
+
export default class <%= vars.ModuleName %> {
|
|
20
|
+
@operation({
|
|
21
|
+
summary: 'Get <%= t.theThings %>',
|
|
22
|
+
})
|
|
23
|
+
@get()
|
|
24
|
+
static get<%= t.TheThings %> = procedure().handle(() => {
|
|
25
|
+
<% if(t.withService) { %>
|
|
26
|
+
return <%= vars.ServiceName %>.get<%= t.TheThings %>();
|
|
27
|
+
<% } else { %>
|
|
28
|
+
return { message: 'TODO: get <%= t.theThings %>' };
|
|
29
|
+
<% } %>
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
@operation({
|
|
33
|
+
summary: 'Get single <%= t.theThing %>',
|
|
34
|
+
})
|
|
35
|
+
@get('{id}')
|
|
36
|
+
static getSingle<%= t.TheThing %> = procedure({
|
|
37
|
+
params: type({ id: type('string') }),
|
|
38
|
+
}).handle((_req, { id }) => {
|
|
39
|
+
<% if(t.withService) { %>
|
|
40
|
+
return <%= vars.ServiceName %>.getSingle<%= t.TheThing %>(id);
|
|
41
|
+
<% } else { %>
|
|
42
|
+
return { message: 'TODO: get single <%= t.theThing %>', id };
|
|
43
|
+
<% } %>
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
@operation({
|
|
47
|
+
summary: 'Update <%= t.theThing %>',
|
|
48
|
+
})
|
|
49
|
+
@put('{id}')
|
|
50
|
+
static update<%= t.TheThing %> = procedure({
|
|
51
|
+
body: type({ todo: type('true') }),
|
|
52
|
+
params: type({ id: type('string') }),
|
|
53
|
+
}).handle(async (req, { id }) => {
|
|
54
|
+
const body = await req.json();
|
|
55
|
+
<% if(t.withService) { %>
|
|
56
|
+
return <%= vars.ServiceName %>.update<%= t.TheThing %>(id, body);
|
|
57
|
+
<% } else { %>
|
|
58
|
+
return { message: `TODO: update <%= t.theThing %>`, id, body };
|
|
59
|
+
<% } %>
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
@operation({
|
|
63
|
+
summary: 'Create <%= t.theThing %>',
|
|
64
|
+
})
|
|
65
|
+
@post()
|
|
66
|
+
static create<%= t.TheThing %> = procedure({
|
|
67
|
+
body: type({ todo: type('true') }),
|
|
68
|
+
}).handle(async (req) => {
|
|
69
|
+
const body = await req.json();
|
|
70
|
+
<% if(t.withService) { %>
|
|
71
|
+
return <%= vars.ServiceName %>.create<%= t.TheThing %>(body);
|
|
72
|
+
<% } else { %>
|
|
73
|
+
return { message: `TODO: create <%= t.theThing %>`, body };
|
|
74
|
+
<% } %>
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
@operation({
|
|
78
|
+
summary: 'Delete <%= t.theThing %>',
|
|
79
|
+
})
|
|
80
|
+
@del('{id}')
|
|
81
|
+
static delete<%= t.TheThing %> = procedure({
|
|
82
|
+
params: type({ id: type('string') }),
|
|
83
|
+
}).handle((_req, { id }) => {
|
|
84
|
+
<% if(t.withService) { %>
|
|
85
|
+
return <%= vars.ServiceName %>.delete<%= t.TheThing %>(id);
|
|
86
|
+
<% } else { %>
|
|
87
|
+
return { message: `TODO: delete <%= t.theThing %>`, id };
|
|
88
|
+
<% } %>
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<% const vars = {
|
|
2
|
+
ControllerName: t.TheThing + 'Controller',
|
|
3
|
+
ServiceName: t.TheThing + 'Service',
|
|
4
|
+
}; %>
|
|
5
|
+
---
|
|
6
|
+
outDir: <%= t.defaultOutDir %>
|
|
7
|
+
fileName: <%= vars.ControllerName + '.ts' %>
|
|
8
|
+
sourceName: <%= vars.ControllerName %>
|
|
9
|
+
compiledName: <%= t.TheThing + 'RPC' %>
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
import { prefix, get, put, post, del, operation, type VovkRequest } from 'vovk';
|
|
13
|
+
<% if(t.withService) { %>
|
|
14
|
+
import <%= vars.ServiceName %> from './<%= vars.ServiceName %><%= t.nodeNextResolutionExt.ts %>';
|
|
15
|
+
<% } %>
|
|
16
|
+
|
|
17
|
+
@prefix('<%= t['the-things'] %>')
|
|
18
|
+
export default class <%= vars.ControllerName %> {
|
|
19
|
+
@operation({
|
|
20
|
+
summary: 'Get <%= t.theThings %>',
|
|
21
|
+
})
|
|
22
|
+
@get()
|
|
23
|
+
static get<%= t.TheThings %> = () => {
|
|
24
|
+
<% if(t.withService) { %>
|
|
25
|
+
return <%= vars.ServiceName %>.get<%= t.TheThings %>();
|
|
26
|
+
<% } else { %>
|
|
27
|
+
return { message: 'TODO: get <%= t.theThings %>' };
|
|
28
|
+
<% } %>
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
@operation({
|
|
32
|
+
summary: 'Get single <%= t.theThing %>',
|
|
33
|
+
})
|
|
34
|
+
@get('{id}')
|
|
35
|
+
static getSingle<%= t.TheThing %> = (_req: VovkRequest, { id }: { id: string }) => {
|
|
36
|
+
<% if(t.withService) { %>
|
|
37
|
+
return <%= vars.ServiceName %>.getSingle<%= t.TheThing %>(id);
|
|
38
|
+
<% } else { %>
|
|
39
|
+
return { message: 'TODO: get single <%= t.theThing %>', id };
|
|
40
|
+
<% } %>
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
@operation({
|
|
44
|
+
summary: 'Update <%= t.theThing %>',
|
|
45
|
+
})
|
|
46
|
+
@put('{id}')
|
|
47
|
+
static update<%= t.TheThing %> = async (req: VovkRequest<{ todo: true }>, { id }: { id: string }) => {
|
|
48
|
+
const body = await req.json();
|
|
49
|
+
<% if(t.withService) { %>
|
|
50
|
+
return <%= vars.ServiceName %>.update<%= t.TheThing %>(id, body);
|
|
51
|
+
<% } else { %>
|
|
52
|
+
return { message: `TODO: update <%= t.theThing %>`, id, body };
|
|
53
|
+
<% } %>
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
@operation({
|
|
57
|
+
summary: 'Create <%= t.theThing %>',
|
|
58
|
+
})
|
|
59
|
+
@post()
|
|
60
|
+
static create<%= t.TheThing %> = async (req: VovkRequest<{ todo: true }>) => {
|
|
61
|
+
const body = await req.json();
|
|
62
|
+
<% if(t.withService) { %>
|
|
63
|
+
return <%= vars.ServiceName %>.create<%= t.TheThing %>(body);
|
|
64
|
+
<% } else { %>
|
|
65
|
+
return { message: `TODO: create <%= t.theThing %>`, body };
|
|
66
|
+
<% } %>
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
@operation({
|
|
70
|
+
summary: 'Delete <%= t.theThing %>',
|
|
71
|
+
})
|
|
72
|
+
@del('{id}')
|
|
73
|
+
static delete<%= t.TheThing %> = (_req: VovkRequest, { id }: { id: string }) => {
|
|
74
|
+
<% if(t.withService) { %>
|
|
75
|
+
return <%= vars.ServiceName %>.delete<%= t.TheThing %>(id);
|
|
76
|
+
<% } else { %>
|
|
77
|
+
return { message: `TODO: delete <%= t.theThing %>`, id };
|
|
78
|
+
<% } %>
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<% const vars = {
|
|
2
|
+
ControllerName: t.TheThing + 'Controller',
|
|
3
|
+
ServiceName: t.TheThing + 'Service',
|
|
4
|
+
}; %>
|
|
5
|
+
---
|
|
6
|
+
outDir: <%= t.defaultOutDir %>
|
|
7
|
+
fileName: <%= vars.ServiceName + '.ts' %>
|
|
8
|
+
sourceName: <%= vars.ServiceName %>
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
import type { VovkBody, VovkParams } from 'vovk';
|
|
12
|
+
import type <%= vars.ControllerName %> from './<%= vars.ControllerName %><%= t.nodeNextResolutionExt.ts %>';
|
|
13
|
+
|
|
14
|
+
export default class <%= vars.ServiceName %> {
|
|
15
|
+
static get<%= t.TheThings %> = () => {
|
|
16
|
+
return { message: 'TODO: get <%= t.theThings %>' };
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
static getSingle<%= t.TheThing %> = (
|
|
20
|
+
id: VovkParams<typeof <%= vars.ControllerName %>.getSingle<%= t.TheThing %>>['id']
|
|
21
|
+
) => {
|
|
22
|
+
return { message: 'TODO: get single <%= t.theThing %>', id };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static update<%= t.TheThing %> = (
|
|
26
|
+
id: VovkParams<typeof <%= vars.ControllerName %>.update<%= t.TheThing %>>['id'],
|
|
27
|
+
body: VovkBody<typeof <%= vars.ControllerName %>.update<%= t.TheThing %>>
|
|
28
|
+
) => {
|
|
29
|
+
return { message: `TODO: update <%= t.theThing %>`, id, body };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
static create<%= t.TheThing %> = (
|
|
33
|
+
body: VovkBody<typeof <%= vars.ControllerName %>.create<%= t.TheThing %>>
|
|
34
|
+
) => {
|
|
35
|
+
return { message: `TODO: create <%= t.theThing %>`, body };
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
static delete<%= t.TheThing %> = (
|
|
39
|
+
id: VovkParams<typeof <%= vars.ControllerName %>.delete<%= t.TheThing %>>['id']
|
|
40
|
+
) => {
|
|
41
|
+
return { message: `TODO: delete <%= t.theThing %>`, id };
|
|
42
|
+
};
|
|
43
|
+
}
|