vovk-cli 0.0.1-draft.12 → 0.0.1-draft.121
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 +29 -1
- package/client-templates/fullSchema/fullSchema.cjs.ejs +13 -0
- package/client-templates/fullSchema/fullSchema.d.cts.ejs +11 -0
- package/client-templates/main/main.cjs.ejs +15 -0
- package/client-templates/main/main.d.cts.ejs +15 -0
- package/client-templates/module/module.d.mts.ejs +15 -0
- package/client-templates/module/module.mjs.ejs +21 -0
- package/client-templates/ts/index.ts.ejs +24 -0
- package/dist/dev/diffSegmentSchema.d.mts +36 -0
- package/dist/{watcher/diffSchema.mjs → dev/diffSegmentSchema.mjs} +4 -12
- package/dist/{watcher → dev}/ensureSchemaFiles.d.mts +3 -0
- package/dist/{watcher → dev}/ensureSchemaFiles.mjs +14 -32
- package/dist/dev/index.d.mts +6 -0
- package/dist/{watcher → dev}/index.mjs +152 -79
- package/dist/dev/isSegmentSchemaEmpty.d.mts +2 -0
- package/dist/dev/isSegmentSchemaEmpty.mjs +4 -0
- package/dist/dev/logDiffResult.d.mts +3 -0
- package/dist/dev/logDiffResult.mjs +57 -0
- package/dist/dev/writeConfigJson.d.mts +2 -0
- package/dist/dev/writeConfigJson.mjs +15 -0
- package/dist/dev/writeOneSegmentSchemaFile.d.mts +12 -0
- package/dist/{watcher/writeOneSchemaFile.mjs → dev/writeOneSegmentSchemaFile.mjs} +11 -7
- package/dist/generate/ensureClient.d.mts +4 -0
- package/dist/generate/ensureClient.mjs +41 -0
- package/dist/generate/getClientTemplates.d.mts +24 -0
- package/dist/generate/getClientTemplates.mjs +86 -0
- package/dist/generate/getFullSchemaFromJSON.d.mts +3 -0
- package/dist/generate/getFullSchemaFromJSON.mjs +64 -0
- package/dist/generate/index.d.mts +13 -0
- package/dist/generate/index.mjs +115 -0
- package/dist/getProjectInfo/getConfig.d.mts +5 -4
- package/dist/getProjectInfo/getConfig.mjs +26 -7
- package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +2 -1
- package/dist/getProjectInfo/getConfigAbsolutePaths.mjs +6 -3
- package/dist/getProjectInfo/getRelativeSrcRoot.mjs +1 -1
- package/dist/getProjectInfo/getUserConfig.d.mts +3 -2
- package/dist/getProjectInfo/getUserConfig.mjs +5 -3
- package/dist/getProjectInfo/importUncachedModule.mjs +0 -1
- package/dist/getProjectInfo/importUncachedModuleWorker.mjs +0 -1
- package/dist/getProjectInfo/index.d.mts +14 -6
- package/dist/getProjectInfo/index.mjs +23 -15
- package/dist/index.d.mts +0 -28
- package/dist/index.mjs +60 -64
- package/dist/init/checkTSConfigForExperimentalDecorators.mjs +2 -2
- package/dist/init/createConfig.d.mts +3 -4
- package/dist/init/createConfig.mjs +14 -10
- package/dist/init/getTemplateFilesFromPackage.d.mts +2 -1
- package/dist/init/getTemplateFilesFromPackage.mjs +4 -5
- package/dist/init/index.d.mts +2 -3
- package/dist/init/index.mjs +31 -88
- package/dist/init/installDependencies.d.mts +1 -1
- package/dist/init/installDependencies.mjs +1 -1
- package/dist/init/logUpdateDependenciesError.d.mts +2 -2
- package/dist/init/logUpdateDependenciesError.mjs +3 -3
- package/dist/init/updateDependenciesWithoutInstalling.d.mts +3 -2
- package/dist/init/updateDependenciesWithoutInstalling.mjs +7 -9
- package/dist/init/updateNPMScripts.d.mts +3 -1
- package/dist/init/updateNPMScripts.mjs +10 -7
- package/dist/init/updateTypeScriptConfig.mjs +2 -2
- package/dist/initProgram.d.mts +2 -0
- package/dist/initProgram.mjs +21 -0
- package/dist/locateSegments.d.mts +7 -1
- package/dist/locateSegments.mjs +9 -6
- package/dist/new/addClassToSegmentCode.d.mts +1 -2
- package/dist/new/addClassToSegmentCode.mjs +5 -5
- package/dist/new/addCommonTerms.mjs +1 -0
- package/dist/new/index.d.mts +2 -2
- package/dist/new/index.mjs +3 -2
- package/dist/new/newModule.d.mts +3 -2
- package/dist/new/newModule.mjs +41 -37
- package/dist/new/newSegment.mjs +8 -6
- package/dist/new/render.d.mts +6 -3
- package/dist/new/render.mjs +25 -13
- package/dist/types.d.mts +36 -40
- package/dist/utils/debounceWithArgs.d.mts +2 -2
- package/dist/utils/debounceWithArgs.mjs +24 -9
- package/dist/utils/formatLoggedSegmentName.mjs +1 -1
- package/dist/utils/getAvailablePort.mjs +2 -1
- package/dist/utils/getFileSystemEntryType.mjs +1 -1
- package/dist/utils/resolveAbsoluteModulePath.d.mts +1 -0
- package/dist/utils/resolveAbsoluteModulePath.mjs +6 -0
- package/package.json +26 -21
- package/templates/controller.ejs +22 -23
- package/templates/service.ejs +13 -13
- package/dist/generateClient.d.mts +0 -7
- package/dist/generateClient.mjs +0 -97
- package/dist/postinstall.d.mts +0 -1
- package/dist/postinstall.mjs +0 -24
- package/dist/watcher/diffSchema.d.mts +0 -43
- package/dist/watcher/index.d.mts +0 -6
- package/dist/watcher/isMetadataEmpty.d.mts +0 -2
- package/dist/watcher/isMetadataEmpty.mjs +0 -4
- package/dist/watcher/logDiffResult.d.mts +0 -3
- package/dist/watcher/logDiffResult.mjs +0 -90
- package/dist/watcher/writeOneSchemaFile.d.mts +0 -11
- package/templates/worker.ejs +0 -24
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { glob } from 'node:fs/promises';
|
|
3
|
+
import resolveAbsoluteModulePath from '../utils/resolveAbsoluteModulePath.mjs';
|
|
4
|
+
export const DEFAULT_FULL_SCHEMA_FILE_NAME = 'full-schema.json';
|
|
5
|
+
export var BuiltInTemplateName;
|
|
6
|
+
(function (BuiltInTemplateName) {
|
|
7
|
+
BuiltInTemplateName["ts"] = "ts";
|
|
8
|
+
BuiltInTemplateName["main"] = "main";
|
|
9
|
+
BuiltInTemplateName["module"] = "module";
|
|
10
|
+
BuiltInTemplateName["fullSchema"] = "fullSchema";
|
|
11
|
+
})(BuiltInTemplateName || (BuiltInTemplateName = {}));
|
|
12
|
+
export default async function getClientTemplates({ config, cwd, generateFrom = [], }) {
|
|
13
|
+
const templatesDir = path.join(import.meta.dirname, '../..', 'client-templates');
|
|
14
|
+
const clientOutDirAbsolutePath = path.resolve(cwd, config.clientOutDir);
|
|
15
|
+
const builtIn = {
|
|
16
|
+
ts: {
|
|
17
|
+
templateName: BuiltInTemplateName.ts,
|
|
18
|
+
templatePath: path.resolve(templatesDir, 'ts/*'),
|
|
19
|
+
outDir: clientOutDirAbsolutePath,
|
|
20
|
+
fullSchema: false,
|
|
21
|
+
origin: null,
|
|
22
|
+
},
|
|
23
|
+
main: {
|
|
24
|
+
templateName: BuiltInTemplateName.main,
|
|
25
|
+
templatePath: path.resolve(templatesDir, 'main/*'),
|
|
26
|
+
outDir: clientOutDirAbsolutePath,
|
|
27
|
+
fullSchema: false,
|
|
28
|
+
origin: null,
|
|
29
|
+
},
|
|
30
|
+
module: {
|
|
31
|
+
templateName: BuiltInTemplateName.module,
|
|
32
|
+
templatePath: path.resolve(templatesDir, 'module/*'),
|
|
33
|
+
outDir: clientOutDirAbsolutePath,
|
|
34
|
+
fullSchema: false,
|
|
35
|
+
origin: null,
|
|
36
|
+
},
|
|
37
|
+
fullSchema: {
|
|
38
|
+
templateName: BuiltInTemplateName.fullSchema,
|
|
39
|
+
templatePath: path.resolve(templatesDir, 'fullSchema/*'),
|
|
40
|
+
outDir: clientOutDirAbsolutePath,
|
|
41
|
+
fullSchema: false,
|
|
42
|
+
origin: null,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const generateFromStrict = generateFrom.map((template) => {
|
|
46
|
+
if (typeof template === 'string') {
|
|
47
|
+
if (template in builtIn) {
|
|
48
|
+
return builtIn[template];
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
templateName: template,
|
|
52
|
+
templatePath: resolveAbsoluteModulePath(template, cwd),
|
|
53
|
+
outDir: clientOutDirAbsolutePath,
|
|
54
|
+
fullSchema: false,
|
|
55
|
+
origin: null,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
templateName: template.templateName ?? template.templatePath,
|
|
60
|
+
templatePath: resolveAbsoluteModulePath(template.templatePath, cwd),
|
|
61
|
+
outDir: template.outDir ? path.resolve(cwd, template.outDir) : clientOutDirAbsolutePath,
|
|
62
|
+
fullSchema: template.fullSchema ?? false,
|
|
63
|
+
origin: template.origin ?? null,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
if (['ts', 'main', 'module'].some((template) => generateFromStrict.some((item) => item.templateName === template))) {
|
|
67
|
+
generateFromStrict.unshift(builtIn.fullSchema);
|
|
68
|
+
}
|
|
69
|
+
const templateFiles = [];
|
|
70
|
+
for (const generateFromItem of generateFromStrict) {
|
|
71
|
+
const files = await glob(generateFromItem.templatePath);
|
|
72
|
+
for await (const templatePath of files) {
|
|
73
|
+
const fullSchemaOutAbsolutePath = generateFromItem.fullSchema
|
|
74
|
+
? path.resolve(generateFromItem.outDir, generateFromItem.fullSchema === 'string' ? generateFromItem.fullSchema : DEFAULT_FULL_SCHEMA_FILE_NAME)
|
|
75
|
+
: null;
|
|
76
|
+
templateFiles.push({
|
|
77
|
+
templateName: generateFromItem.templateName,
|
|
78
|
+
templatePath,
|
|
79
|
+
outPath: path.join(generateFromItem.outDir, path.basename(templatePath).replace('.ejs', '')),
|
|
80
|
+
fullSchemaOutAbsolutePath,
|
|
81
|
+
origin: generateFromItem.origin,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { clientOutDirAbsolutePath, templateFiles };
|
|
86
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { readFile, access } from 'node:fs/promises';
|
|
2
|
+
import { glob } from 'glob';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
export async function getFullSchemaFromJSON(schemaOutAbsolutePath, projectInfo) {
|
|
5
|
+
const result = {
|
|
6
|
+
config: {},
|
|
7
|
+
segments: {},
|
|
8
|
+
};
|
|
9
|
+
const { log } = projectInfo;
|
|
10
|
+
// Handle config.json
|
|
11
|
+
const configPath = path.join(schemaOutAbsolutePath, 'config.json');
|
|
12
|
+
try {
|
|
13
|
+
const configContent = await readFile(configPath, 'utf-8');
|
|
14
|
+
result.config = JSON.parse(configContent);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
log.warn(`Warning: config.json not found at ${configPath}. Using empty config as fallback.`);
|
|
18
|
+
result.config = {};
|
|
19
|
+
}
|
|
20
|
+
// Handle segments directory
|
|
21
|
+
const segmentsDir = path.join(schemaOutAbsolutePath, 'segments');
|
|
22
|
+
try {
|
|
23
|
+
await access(segmentsDir); // Check if directory exists
|
|
24
|
+
// Use glob to get all JSON files recursively
|
|
25
|
+
const files = await glob(`${segmentsDir}/**/*.json`);
|
|
26
|
+
// Process each JSON file
|
|
27
|
+
for await (const filePath of files) {
|
|
28
|
+
try {
|
|
29
|
+
const content = await readFile(filePath, 'utf-8');
|
|
30
|
+
const jsonData = JSON.parse(content);
|
|
31
|
+
// Get relative path from segments directory and convert to key
|
|
32
|
+
let relativePath = path
|
|
33
|
+
.relative(segmentsDir, filePath)
|
|
34
|
+
.replace(/\.json$/, '') // Remove .json extension
|
|
35
|
+
.replace(/\\/g, '/'); // Normalize to forward slashes
|
|
36
|
+
// Special case for _root.json
|
|
37
|
+
if (path.basename(filePath) === '_root.json' && path.dirname(filePath) === segmentsDir) {
|
|
38
|
+
relativePath = '';
|
|
39
|
+
}
|
|
40
|
+
result.segments[relativePath] = jsonData;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
log.warn(`Warning: Failed to process file ${filePath}: ${error}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
log.warn(`Warning: Segments directory not found at ${segmentsDir}. Using empty segments as fallback.`);
|
|
49
|
+
result.segments = {};
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
// Example usage:
|
|
54
|
+
/*
|
|
55
|
+
async function main() {
|
|
56
|
+
try {
|
|
57
|
+
const schema = await getSchemaFromJSON('/path/to/directory');
|
|
58
|
+
console.log(schema);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('Error:', error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
main();
|
|
64
|
+
*/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { VovkFullSchema } from 'vovk';
|
|
2
|
+
import type { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
3
|
+
import type { Segment } from '../locateSegments.mjs';
|
|
4
|
+
import type { GenerateOptions } from '../types.mjs';
|
|
5
|
+
export default function generate({ projectInfo, segments, forceNothingWrittenLog, templates, prettify: prettifyClient, fullSchema, emitFullSchema, }: {
|
|
6
|
+
projectInfo: ProjectInfo;
|
|
7
|
+
segments: Segment[];
|
|
8
|
+
forceNothingWrittenLog?: boolean;
|
|
9
|
+
fullSchema: VovkFullSchema;
|
|
10
|
+
} & Pick<GenerateOptions, 'templates' | 'prettify' | 'emitFullSchema'>): Promise<{
|
|
11
|
+
written: boolean;
|
|
12
|
+
path: string;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import ejs from 'ejs';
|
|
4
|
+
import matter from 'gray-matter';
|
|
5
|
+
import uniq from 'lodash/uniq.js';
|
|
6
|
+
import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
|
|
7
|
+
import prettify from '../utils/prettify.mjs';
|
|
8
|
+
import getClientTemplates, { DEFAULT_FULL_SCHEMA_FILE_NAME } from './getClientTemplates.mjs';
|
|
9
|
+
import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
|
|
10
|
+
import _ from 'lodash';
|
|
11
|
+
import { ROOT_SEGMENT_SCHEMA_NAME, SEGMENTS_SCHEMA_DIR_NAME } from '../dev/writeOneSegmentSchemaFile.mjs';
|
|
12
|
+
export default async function generate({ projectInfo, segments, forceNothingWrittenLog, templates, prettify: prettifyClient, fullSchema, emitFullSchema, }) {
|
|
13
|
+
const segmentsSchema = fullSchema.segments;
|
|
14
|
+
const generateFrom = templates ?? projectInfo.config.generateFrom;
|
|
15
|
+
const noClient = templates?.[0] === 'none';
|
|
16
|
+
const { config, cwd, log, clientImports, apiRoot } = projectInfo;
|
|
17
|
+
const { clientOutDirAbsolutePath, templateFiles } = await getClientTemplates({ config, cwd, generateFrom });
|
|
18
|
+
// Ensure that each segment has a matching schema if it needs to be emitted:
|
|
19
|
+
for (let i = 0; i < segments.length; i++) {
|
|
20
|
+
const { segmentName } = segments[i];
|
|
21
|
+
const schema = segmentsSchema[segmentName];
|
|
22
|
+
if (!schema) {
|
|
23
|
+
throw new Error(`Unable to generate client. No schema found for ${formatLoggedSegmentName(segmentName)}`);
|
|
24
|
+
}
|
|
25
|
+
if (!schema.emitSchema)
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const now = Date.now();
|
|
29
|
+
const schemaOutDir = path.relative(config.clientOutDir, config.schemaOutDir).replace(/\\/g, '/'); // windows fix
|
|
30
|
+
// Process each template in parallel
|
|
31
|
+
const processedTemplates = noClient
|
|
32
|
+
? []
|
|
33
|
+
: await Promise.all(templateFiles.map(async ({ templatePath, outPath, templateName, origin }) => {
|
|
34
|
+
/*const parsed = matter((await ejs.render(codeTemplate, { t }, { async: true, filename: templateFileName })).trim());
|
|
35
|
+
const { dir, fileName, sourceName, compiledName } = parsed.data as VovkModuleRenderResult;
|
|
36
|
+
const code = empty ? (sourceName ? `export default class ${sourceName} {}` : '') : parsed.content;*/
|
|
37
|
+
// Read the EJS template
|
|
38
|
+
const templateContent = await fs.readFile(templatePath, 'utf-8');
|
|
39
|
+
const { data, content } = matter(templateContent);
|
|
40
|
+
// Data for the EJS templates:
|
|
41
|
+
const t = {
|
|
42
|
+
_, // lodash
|
|
43
|
+
ROOT_SEGMENT_SCHEMA_NAME,
|
|
44
|
+
SEGMENTS_SCHEMA_DIR_NAME,
|
|
45
|
+
apiRoot: origin ? `${origin}/${config.rootEntry}` : apiRoot,
|
|
46
|
+
imports: clientImports,
|
|
47
|
+
fullSchema,
|
|
48
|
+
schemaOutDir,
|
|
49
|
+
segmentMeta: Object.fromEntries(segments.map(({ segmentName, ...s }) => [segmentName, s])),
|
|
50
|
+
};
|
|
51
|
+
if (data.imports instanceof Array) {
|
|
52
|
+
for (const imp of data.imports) {
|
|
53
|
+
t.imports = {
|
|
54
|
+
...t.imports,
|
|
55
|
+
[imp]: await import(imp),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Render the template
|
|
60
|
+
let rendered = templatePath.endsWith('.ejs')
|
|
61
|
+
? ejs.render(content, { t }, {
|
|
62
|
+
filename: templatePath,
|
|
63
|
+
})
|
|
64
|
+
: templateContent;
|
|
65
|
+
// Optionally prettify
|
|
66
|
+
if (prettifyClient || config.prettifyClient) {
|
|
67
|
+
rendered = await prettify(rendered, outPath);
|
|
68
|
+
}
|
|
69
|
+
// Read existing file content to compare
|
|
70
|
+
const existingContent = await fs.readFile(outPath, 'utf-8').catch(() => '');
|
|
71
|
+
// Determine if we need to rewrite the file, ignore 1st line
|
|
72
|
+
const needsWriting = existingContent.trim().split('\n').slice(1).join('\n') !== rendered.trim().split('\n').slice(1).join('\n');
|
|
73
|
+
return {
|
|
74
|
+
outPath,
|
|
75
|
+
rendered,
|
|
76
|
+
needsWriting,
|
|
77
|
+
templateName,
|
|
78
|
+
};
|
|
79
|
+
}));
|
|
80
|
+
const usedTemplateNames = uniq(processedTemplates.filter(({ needsWriting }) => needsWriting).map(({ templateName }) => templateName));
|
|
81
|
+
let schemaPaths = templateFiles
|
|
82
|
+
.map(({ fullSchemaOutAbsolutePath }) => fullSchemaOutAbsolutePath)
|
|
83
|
+
.filter(Boolean);
|
|
84
|
+
if (emitFullSchema) {
|
|
85
|
+
const fullSchemaOutAbsolutePath = emitFullSchema
|
|
86
|
+
? path.resolve(clientOutDirAbsolutePath, emitFullSchema === 'string' ? emitFullSchema : DEFAULT_FULL_SCHEMA_FILE_NAME)
|
|
87
|
+
: null;
|
|
88
|
+
if (fullSchemaOutAbsolutePath) {
|
|
89
|
+
schemaPaths.push(fullSchemaOutAbsolutePath);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
schemaPaths = uniq(schemaPaths);
|
|
93
|
+
if (schemaPaths.length) {
|
|
94
|
+
await Promise.all(schemaPaths.map(async (fullSchemaOutAbsolutePath) => {
|
|
95
|
+
fs.mkdir(path.dirname(fullSchemaOutAbsolutePath), { recursive: true });
|
|
96
|
+
return fs.writeFile(fullSchemaOutAbsolutePath, JSON.stringify(fullSchema, null, 2));
|
|
97
|
+
}));
|
|
98
|
+
log.info(`Full schema has been written to ${schemaPaths.map((s) => `"${s}"`).join(', ')}`);
|
|
99
|
+
}
|
|
100
|
+
if (usedTemplateNames.length === 0) {
|
|
101
|
+
const logOrDebug = forceNothingWrittenLog ? log.info : log.debug;
|
|
102
|
+
logOrDebug(`Client is up to date and doesn't need to be regenerated (${Date.now() - now}ms)`);
|
|
103
|
+
return { written: false, path: clientOutDirAbsolutePath };
|
|
104
|
+
}
|
|
105
|
+
// Write updated files where needed
|
|
106
|
+
await Promise.all(processedTemplates.map(async ({ outPath, rendered, needsWriting }) => {
|
|
107
|
+
if (needsWriting) {
|
|
108
|
+
await fs.mkdir(path.dirname(outPath), { recursive: true });
|
|
109
|
+
return fs.writeFile(outPath, rendered);
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}));
|
|
113
|
+
log.info(`Client generated from template${usedTemplateNames.length !== 1 ? 's' : ''} ${chalkHighlightThing(usedTemplateNames.map((s) => `"${s}"`).join(', '))} in ${Date.now() - now}ms`);
|
|
114
|
+
return { written: true, path: clientOutDirAbsolutePath };
|
|
115
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export default function getConfig({ clientOutDir, cwd }: {
|
|
1
|
+
import type { VovkStrictConfig } from 'vovk';
|
|
2
|
+
export default function getConfig({ clientOutDir, configPath, cwd, }: {
|
|
3
3
|
clientOutDir?: string;
|
|
4
|
+
configPath?: string;
|
|
4
5
|
cwd: string;
|
|
5
6
|
}): Promise<{
|
|
6
|
-
config:
|
|
7
|
+
config: VovkStrictConfig;
|
|
7
8
|
srcRoot: string;
|
|
8
9
|
configAbsolutePaths: string[];
|
|
9
|
-
userConfig: VovkConfig | null;
|
|
10
|
+
userConfig: import("vovk").VovkConfig | null;
|
|
10
11
|
error: Error | undefined;
|
|
11
12
|
}>;
|
|
@@ -1,29 +1,48 @@
|
|
|
1
1
|
import getUserConfig from './getUserConfig.mjs';
|
|
2
2
|
import getRelativeSrcRoot from './getRelativeSrcRoot.mjs';
|
|
3
|
-
export default async function getConfig({ clientOutDir, cwd }) {
|
|
3
|
+
export default async function getConfig({ clientOutDir, configPath, cwd, }) {
|
|
4
4
|
const env = process.env;
|
|
5
|
-
const { configAbsolutePaths, error, userConfig } = await getUserConfig({ cwd });
|
|
5
|
+
const { configAbsolutePaths, error, userConfig } = await getUserConfig({ configPath, cwd });
|
|
6
6
|
const conf = userConfig ?? {};
|
|
7
7
|
const srcRoot = await getRelativeSrcRoot({ cwd });
|
|
8
|
+
const validateOnClientImport = env.VOVK_VALIDATE_ON_CLIENT_PATH ?? conf.imports?.validateOnClient ?? null;
|
|
9
|
+
const fetcherImport = env.VOVK_FETCHER_PATH ?? conf.imports?.fetcher ?? 'vovk';
|
|
10
|
+
const createRPCImport = env.VOVK_CREATE_RPC_PATH ?? conf.imports?.createRPC ?? 'vovk';
|
|
11
|
+
const defaultClientTemplates = ['module', 'main'];
|
|
8
12
|
const config = {
|
|
13
|
+
emitConfig: [],
|
|
9
14
|
modulesDir: env.VOVK_MODULES_DIR ?? conf.modulesDir ?? './' + [srcRoot, 'modules'].filter(Boolean).join('/'),
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
imports: {
|
|
16
|
+
fetcher: typeof fetcherImport === 'string' ? [fetcherImport] : fetcherImport,
|
|
17
|
+
validateOnClient: typeof validateOnClientImport === 'string' ? [validateOnClientImport] : (validateOnClientImport ?? null),
|
|
18
|
+
createRPC: typeof createRPCImport === 'string' ? [createRPCImport, 'vovk'] : createRPCImport,
|
|
19
|
+
},
|
|
13
20
|
schemaOutDir: env.VOVK_SCHEMA_OUT_DIR ?? conf.schemaOutDir ?? './.vovk-schema',
|
|
14
21
|
clientOutDir: clientOutDir ?? env.VOVK_CLIENT_OUT_DIR ?? conf.clientOutDir ?? './node_modules/.vovk-client',
|
|
15
22
|
origin: (env.VOVK_ORIGIN ?? conf.origin ?? '').replace(/\/$/, ''), // Remove trailing slash
|
|
16
23
|
rootEntry: env.VOVK_ROOT_ENTRY ?? conf.rootEntry ?? 'api',
|
|
17
24
|
rootSegmentModulesDirName: env.VOVK_ROOT_SEGMENT_MODULES_DIR_NAME ?? conf.rootSegmentModulesDirName ?? '',
|
|
18
|
-
logLevel: env.VOVK_LOG_LEVEL ?? conf.logLevel ?? '
|
|
25
|
+
logLevel: env.VOVK_LOG_LEVEL ?? conf.logLevel ?? 'info',
|
|
19
26
|
prettifyClient: (env.VOVK_PRETTIFY_CLIENT ? !!env.VOVK_PRETTIFY_CLIENT : null) ?? conf.prettifyClient ?? false,
|
|
20
27
|
devHttps: (env.VOVK_DEV_HTTPS ? !!env.VOVK_DEV_HTTPS : null) ?? conf.devHttps ?? false,
|
|
28
|
+
generateFrom: typeof conf.generateFrom === 'function'
|
|
29
|
+
? conf.generateFrom(defaultClientTemplates)
|
|
30
|
+
: (conf.generateFrom ?? defaultClientTemplates),
|
|
21
31
|
templates: {
|
|
22
32
|
service: 'vovk-cli/templates/service.ejs',
|
|
23
33
|
controller: 'vovk-cli/templates/controller.ejs',
|
|
24
|
-
worker: 'vovk-cli/templates/worker.ejs',
|
|
25
34
|
...conf.templates,
|
|
26
35
|
},
|
|
36
|
+
libs: conf.libs ?? {},
|
|
27
37
|
};
|
|
38
|
+
if (typeof conf.emitConfig === 'undefined') {
|
|
39
|
+
config.emitConfig = ['libs'];
|
|
40
|
+
}
|
|
41
|
+
else if (conf.emitConfig === true) {
|
|
42
|
+
config.emitConfig = Object.keys(config);
|
|
43
|
+
}
|
|
44
|
+
else if (Array.isArray(conf.emitConfig)) {
|
|
45
|
+
config.emitConfig = conf.emitConfig;
|
|
46
|
+
} // else it's false and emitConfig already is []
|
|
28
47
|
return { config, srcRoot, configAbsolutePaths, userConfig, error };
|
|
29
48
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
export default async function getConfigAbsolutePaths({ cwd, relativePath, }) {
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import path from 'path';
|
|
1
|
+
import path from 'node:path';
|
|
2
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.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { VovkConfig } from '
|
|
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,7 +1,8 @@
|
|
|
1
|
+
import { pathToFileURL } from 'node:url';
|
|
1
2
|
import getConfigAbsolutePaths from './getConfigAbsolutePaths.mjs';
|
|
2
3
|
import importUncachedModule from './importUncachedModule.mjs';
|
|
3
|
-
async function getUserConfig({ cwd, }) {
|
|
4
|
-
const configAbsolutePaths = await getConfigAbsolutePaths({ cwd });
|
|
4
|
+
async function getUserConfig({ configPath: givenConfigPath, cwd, }) {
|
|
5
|
+
const configAbsolutePaths = await getConfigAbsolutePaths({ configPath: givenConfigPath, cwd });
|
|
5
6
|
let userConfig;
|
|
6
7
|
if (!configAbsolutePaths.length) {
|
|
7
8
|
return { userConfig: null, configAbsolutePaths, error: new Error('No config file found') };
|
|
@@ -14,7 +15,8 @@ async function getUserConfig({ cwd, }) {
|
|
|
14
15
|
catch {
|
|
15
16
|
try {
|
|
16
17
|
const cacheBuster = Date.now();
|
|
17
|
-
|
|
18
|
+
const configPathUrl = pathToFileURL(configPath).href;
|
|
19
|
+
({ default: userConfig } = (await import(`${configPathUrl}?cache=${cacheBuster}`)));
|
|
18
20
|
}
|
|
19
21
|
catch (e) {
|
|
20
22
|
return { userConfig: null, configAbsolutePaths, error: e };
|
|
@@ -3,7 +3,6 @@ import { Worker } from 'node:worker_threads';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import './importUncachedModuleWorker.mjs'; // required for TS compilation
|
|
6
|
-
// TODO comments
|
|
7
6
|
function importUncachedModule(modulePath) {
|
|
8
7
|
return new Promise((resolve, reject) => {
|
|
9
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
export type ProjectInfo = Awaited<ReturnType<typeof getProjectInfo>>;
|
|
2
|
-
export default function getProjectInfo({ port: givenPort, clientOutDir, cwd, }?: {
|
|
2
|
+
export default function getProjectInfo({ port: givenPort, clientOutDir, configPath, cwd, }?: {
|
|
3
3
|
port?: number;
|
|
4
4
|
clientOutDir?: string;
|
|
5
|
+
configPath?: string;
|
|
5
6
|
cwd?: string;
|
|
6
7
|
}): Promise<{
|
|
7
8
|
cwd: string;
|
|
8
9
|
port: string;
|
|
9
|
-
|
|
10
|
+
apiRoot: string;
|
|
10
11
|
apiDir: string;
|
|
11
12
|
srcRoot: string;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
config: import("vovk").VovkStrictConfig;
|
|
14
|
+
clientImports: {
|
|
15
|
+
fetcher: string;
|
|
16
|
+
createRPC: string;
|
|
17
|
+
validateOnClient: string | null;
|
|
18
|
+
module: {
|
|
19
|
+
fetcher: string;
|
|
20
|
+
createRPC: string;
|
|
21
|
+
validateOnClient: string | null;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
16
24
|
log: {
|
|
17
25
|
info: (msg: string) => void;
|
|
18
26
|
warn: (msg: string) => void;
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import path from 'path';
|
|
1
|
+
import path from 'node:path';
|
|
2
2
|
import getConfig from './getConfig.mjs';
|
|
3
3
|
import getLogger from '../utils/getLogger.mjs';
|
|
4
|
-
export default async function getProjectInfo({ port: givenPort, clientOutDir, cwd = process.cwd(), } = {}) {
|
|
4
|
+
export default async function getProjectInfo({ port: givenPort, clientOutDir, configPath, cwd = process.cwd(), } = {}) {
|
|
5
5
|
const port = givenPort?.toString() ?? process.env.PORT ?? '3000';
|
|
6
6
|
// Make PORT available to the config file at getConfig
|
|
7
7
|
process.env.PORT = port;
|
|
8
|
-
const { config, srcRoot, configAbsolutePaths, userConfig, error } = await getConfig({
|
|
9
|
-
|
|
8
|
+
const { config, srcRoot, configAbsolutePaths, userConfig, error } = await getConfig({
|
|
9
|
+
clientOutDir,
|
|
10
|
+
configPath,
|
|
11
|
+
cwd,
|
|
12
|
+
});
|
|
13
|
+
const apiRoot = `${config.origin ?? ''}/${config.rootEntry}`;
|
|
10
14
|
const apiDir = path.join(srcRoot, 'app', config.rootEntry);
|
|
11
|
-
const schemaOutImportPath = path.relative(config.clientOutDir, config.schemaOutDir);
|
|
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
15
|
const log = getLogger(config.logLevel);
|
|
19
16
|
if (configAbsolutePaths.length > 1) {
|
|
20
17
|
log.warn(`Multiple config files found. Using the first one: ${configAbsolutePaths[0]}`);
|
|
@@ -22,16 +19,27 @@ export default async function getProjectInfo({ port: givenPort, clientOutDir, cw
|
|
|
22
19
|
if (!userConfig && configAbsolutePaths.length > 0) {
|
|
23
20
|
log.error(`Error reading config file at ${configAbsolutePaths[0]}: ${error?.message ?? 'Unknown Error'}`);
|
|
24
21
|
}
|
|
22
|
+
const getImportPath = (p) => (p.startsWith('.') ? path.relative(config.clientOutDir, p) : p);
|
|
23
|
+
const clientImports = {
|
|
24
|
+
fetcher: getImportPath(config.imports.fetcher[0]),
|
|
25
|
+
createRPC: getImportPath(config.imports.createRPC[0]),
|
|
26
|
+
validateOnClient: config.imports.validateOnClient ? getImportPath(config.imports.validateOnClient[0]) : null,
|
|
27
|
+
module: {
|
|
28
|
+
fetcher: getImportPath(config.imports.fetcher[1] ?? config.imports.fetcher[0]),
|
|
29
|
+
createRPC: getImportPath(config.imports.createRPC[1] ?? config.imports.createRPC[0]),
|
|
30
|
+
validateOnClient: config.imports.validateOnClient
|
|
31
|
+
? getImportPath(config.imports.validateOnClient[1] ?? config.imports.validateOnClient[0])
|
|
32
|
+
: null,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
25
35
|
return {
|
|
26
36
|
cwd,
|
|
27
37
|
port,
|
|
28
|
-
|
|
38
|
+
apiRoot,
|
|
29
39
|
apiDir,
|
|
30
40
|
srcRoot,
|
|
31
|
-
schemaOutImportPath,
|
|
32
|
-
fetcherClientImportPath,
|
|
33
|
-
validateOnClientImportPath,
|
|
34
41
|
config,
|
|
42
|
+
clientImports,
|
|
35
43
|
log,
|
|
36
44
|
};
|
|
37
45
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,30 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import type { LogLevelNames } from 'loglevel';
|
|
4
|
-
import type { VovkConfig, VovkEnv } from './types.mjs';
|
|
5
2
|
import 'dotenv/config';
|
|
6
|
-
export type { VovkConfig, VovkEnv };
|
|
7
|
-
export interface InitOptions {
|
|
8
|
-
yes?: boolean;
|
|
9
|
-
logLevel: LogLevelNames;
|
|
10
|
-
useNpm?: boolean;
|
|
11
|
-
useYarn?: boolean;
|
|
12
|
-
usePnpm?: boolean;
|
|
13
|
-
useBun?: boolean;
|
|
14
|
-
skipInstall?: boolean;
|
|
15
|
-
updateTsConfig?: boolean;
|
|
16
|
-
updateScripts?: 'implicit' | 'explicit';
|
|
17
|
-
validationLibrary?: string | null;
|
|
18
|
-
validateOnClient?: boolean;
|
|
19
|
-
dryRun?: boolean;
|
|
20
|
-
channel?: 'latest' | 'beta' | 'dev';
|
|
21
|
-
}
|
|
22
|
-
export interface NewOptions {
|
|
23
|
-
dryRun?: boolean;
|
|
24
|
-
template?: string;
|
|
25
|
-
dir?: string;
|
|
26
|
-
overwrite?: boolean;
|
|
27
|
-
noSegmentUpdate?: boolean;
|
|
28
|
-
}
|
|
29
|
-
declare const program: Command;
|
|
30
|
-
export declare function initProgram(p: typeof program, command: string): Command;
|