vovk-cli 0.0.1-beta.2 → 0.0.1-beta.20
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 +23 -0
- package/dist/generateClient.d.mts +7 -0
- package/dist/generateClient.mjs +97 -0
- package/dist/getProjectInfo/getConfig.d.mts +1 -1
- package/dist/getProjectInfo/getConfig.mjs +16 -11
- package/dist/getProjectInfo/getConfigPath.d.mts +1 -0
- package/dist/getProjectInfo/getConfigPath.mjs +18 -0
- package/dist/getProjectInfo/getRelativeSrcRoot.d.mts +1 -0
- package/dist/getProjectInfo/{getSrcRoot.mjs → getRelativeSrcRoot.mjs} +3 -3
- package/dist/getProjectInfo/index.d.mts +4 -8
- package/dist/getProjectInfo/index.mjs +9 -22
- package/dist/getProjectInfo/readConfig.mjs +4 -23
- package/dist/index.d.mts +9 -1
- package/dist/index.mjs +39 -17
- package/dist/init/index.d.mts +20 -0
- package/dist/init/index.mjs +244 -0
- package/dist/init/installDependencies.d.mts +1 -0
- package/dist/init/installDependencies.mjs +28 -0
- package/dist/new/addClassToSegmentCode.d.mts +6 -0
- package/dist/new/addClassToSegmentCode.mjs +32 -0
- package/dist/new/addCommonTerms.d.mts +1 -0
- package/dist/new/addCommonTerms.mjs +91 -0
- package/dist/new/index.d.mts +2 -0
- package/dist/new/index.mjs +24 -0
- package/dist/new/newModule.d.mts +5 -0
- package/dist/new/newModule.mjs +89 -0
- package/dist/new/newSegment.d.mts +4 -0
- package/dist/new/newSegment.mjs +33 -0
- package/dist/new/render.d.mts +12 -0
- package/dist/new/render.mjs +28 -0
- package/dist/types.d.mts +10 -4
- package/dist/utils/chalkHighlightThing.d.mts +1 -0
- package/dist/utils/chalkHighlightThing.mjs +4 -0
- package/dist/utils/formatLoggedSegmentName.d.mts +4 -0
- package/dist/utils/formatLoggedSegmentName.mjs +7 -0
- package/dist/utils/getLogger.d.mts +8 -0
- package/dist/utils/getLogger.mjs +13 -0
- package/dist/utils/prettify.d.mts +1 -0
- package/dist/utils/prettify.mjs +10 -0
- package/dist/{server/diffMetadata.d.mts → watcher/diffSchema.d.mts} +5 -5
- package/dist/{server/diffMetadata.mjs → watcher/diffSchema.mjs} +1 -1
- package/dist/watcher/ensureSchemaFiles.d.mts +3 -0
- package/dist/{server/ensureMetadataFiles.mjs → watcher/ensureSchemaFiles.mjs} +26 -25
- package/dist/watcher/index.d.mts +6 -0
- package/dist/{server → watcher}/index.mjs +109 -96
- package/dist/watcher/isMetadataEmpty.d.mts +2 -0
- package/dist/watcher/isMetadataEmpty.mjs +4 -0
- package/dist/{server → watcher}/logDiffResult.d.mts +1 -1
- package/dist/{server → watcher}/logDiffResult.mjs +11 -10
- package/dist/watcher/writeOneSchemaFile.d.mts +11 -0
- package/dist/{server/writeOneMetadataFile.mjs → watcher/writeOneSchemaFile.mjs} +7 -7
- package/package.json +22 -6
- package/templates/MyThingController.c.only.template.ts +32 -0
- package/templates/MyThingController.c.template.ts +34 -0
- package/templates/MyThingService.s.template.ts +18 -0
- package/templates/controller.ejs +85 -0
- package/templates/service.ejs +9 -0
- package/templates/worker.ejs +9 -0
- package/templates/zod/MyThingController.c.only.template.ts +32 -0
- package/templates/zod/MyThingController.c.template.ts +39 -0
- package/templates/zod/MyThingService.s.template.ts +18 -0
- package/.eslintrc.mjs +0 -20
- package/dist/getProjectInfo/getCwdPath.d.mts +0 -1
- package/dist/getProjectInfo/getCwdPath.mjs +0 -13
- package/dist/getProjectInfo/getSrcRoot.d.mts +0 -1
- package/dist/init.d.mts +0 -2
- package/dist/init.mjs +0 -171
- package/dist/server/createMetadataServer.d.mts +0 -5
- package/dist/server/createMetadataServer.mjs +0 -25
- package/dist/server/ensureMetadataFiles.d.mts +0 -3
- package/dist/server/generateClient.d.mts +0 -7
- package/dist/server/generateClient.mjs +0 -92
- package/dist/server/index.d.mts +0 -6
- package/dist/server/isMetadataEmpty.d.mts +0 -2
- package/dist/server/isMetadataEmpty.mjs +0 -4
- package/dist/server/writeOneMetadataFile.d.mts +0 -11
- package/src/getProjectInfo/directoryExists.mts +0 -10
- package/src/getProjectInfo/getConfig.mts +0 -29
- package/src/getProjectInfo/getCwdPath.mts +0 -15
- package/src/getProjectInfo/getSrcRoot.mts +0 -14
- package/src/getProjectInfo/index.mts +0 -59
- package/src/getProjectInfo/readConfig.mts +0 -44
- package/src/index.mts +0 -113
- package/src/init.mts +0 -174
- package/src/locateSegments.mts +0 -40
- package/src/postinstall.mts +0 -27
- package/src/server/createMetadataServer.mts +0 -30
- package/src/server/diffMetadata.mts +0 -110
- package/src/server/ensureMetadataFiles.mts +0 -92
- package/src/server/generateClient.mts +0 -108
- package/src/server/index.mts +0 -307
- package/src/server/isMetadataEmpty.mts +0 -6
- package/src/server/logDiffResult.mts +0 -114
- package/src/server/writeOneMetadataFile.mts +0 -44
- package/src/types.mts +0 -58
- package/src/utils/debounceWithArgs.mts +0 -22
- package/src/utils/fileExists.mts +0 -10
- package/src/utils/getAvailablePort.mts +0 -50
- package/test/data/segments/[[...vovk]]/route.ts +0 -0
- package/test/data/segments/bar/[[...custom]]/route.ts +0 -0
- package/test/data/segments/baz/[[...vovk]]/noroute.ts +0 -0
- package/test/data/segments/foo/[[...vovk]]/route.ts +0 -0
- package/test/data/segments/garply/waldo/route.ts +0 -0
- package/test/data/segments/grault/xxxx/[[...vovk]]/noroute.ts +0 -0
- package/test/data/segments/quux/corge/[[...vovk]]/route.ts +0 -0
- package/test/index.ts +0 -3
- package/test/metadata-diff.test.mts +0 -300
- package/test/metadata-write.test.mts +0 -82
- package/test/utils.test.mts +0 -49
- package/tsconfig.json +0 -17
- package/tsconfig.test.json +0 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-present Andrii Gubanov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProjectInfo } from './getProjectInfo/index.mjs';
|
|
2
|
+
import type { Segment } from './locateSegments.mjs';
|
|
3
|
+
import type { VovkSchema } from 'vovk';
|
|
4
|
+
export default function generateClient(projectInfo: ProjectInfo, segments: Segment[], segmentsSchema: Record<string, VovkSchema>): Promise<{
|
|
5
|
+
written: boolean;
|
|
6
|
+
path: string;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import formatLoggedSegmentName from './utils/formatLoggedSegmentName.mjs';
|
|
4
|
+
import prettify from './utils/prettify.mjs';
|
|
5
|
+
export default async function generateClient(projectInfo, segments, segmentsSchema) {
|
|
6
|
+
const { config, cwd, log, validateOnClientImportPath, apiEntryPoint, fetcherClientImportPath, schemaOutImportPath } = projectInfo;
|
|
7
|
+
const now = Date.now();
|
|
8
|
+
const clientoOutDirAbsolutePath = path.join(cwd, config.clientOutDir);
|
|
9
|
+
let dts = `// auto-generated
|
|
10
|
+
/* eslint-disable */
|
|
11
|
+
import type { clientizeController } from 'vovk/client';
|
|
12
|
+
import type { promisifyWorker } from 'vovk/worker';
|
|
13
|
+
import type { VovkClientFetcher } from 'vovk/client';
|
|
14
|
+
import type fetcher from '${fetcherClientImportPath}';
|
|
15
|
+
|
|
16
|
+
`;
|
|
17
|
+
let js = `// auto-generated
|
|
18
|
+
/* eslint-disable */
|
|
19
|
+
const { clientizeController } = require('vovk/client');
|
|
20
|
+
const { promisifyWorker } = require('vovk/worker');
|
|
21
|
+
const { default: fetcher } = require('${fetcherClientImportPath}');
|
|
22
|
+
const schema = require('${schemaOutImportPath}');
|
|
23
|
+
`;
|
|
24
|
+
let ts = `// auto-generated
|
|
25
|
+
/* eslint-disable */
|
|
26
|
+
import { clientizeController } from 'vovk/client';
|
|
27
|
+
import { promisifyWorker } from 'vovk/worker';
|
|
28
|
+
import type { VovkClientFetcher } from 'vovk/client';
|
|
29
|
+
import fetcher from '${fetcherClientImportPath}';
|
|
30
|
+
import schema from '${schemaOutImportPath}';
|
|
31
|
+
|
|
32
|
+
`;
|
|
33
|
+
for (let i = 0; i < segments.length; i++) {
|
|
34
|
+
const { routeFilePath, segmentName } = segments[i];
|
|
35
|
+
const schema = segmentsSchema[segmentName];
|
|
36
|
+
if (!schema) {
|
|
37
|
+
throw new Error(`Unable to generate client. No schema found for ${formatLoggedSegmentName(segmentName)}`);
|
|
38
|
+
}
|
|
39
|
+
if (!schema.emitSchema)
|
|
40
|
+
continue;
|
|
41
|
+
const importRouteFilePath = path.relative(config.clientOutDir, routeFilePath);
|
|
42
|
+
dts += `import type { Controllers as Controllers${i}, Workers as Workers${i} } from "${importRouteFilePath}";\n`;
|
|
43
|
+
ts += `import type { Controllers as Controllers${i}, Workers as Workers${i} } from "${importRouteFilePath}";\n`;
|
|
44
|
+
}
|
|
45
|
+
dts += `
|
|
46
|
+
type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
|
|
47
|
+
`;
|
|
48
|
+
ts += `
|
|
49
|
+
${validateOnClientImportPath ? `import validateOnClient from '${validateOnClientImportPath}';\n` : '\nconst validateOnClient = undefined;'}
|
|
50
|
+
type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
|
|
51
|
+
const prefix = '${apiEntryPoint}';
|
|
52
|
+
`;
|
|
53
|
+
js += `
|
|
54
|
+
const { default: validateOnClient = null } = ${validateOnClientImportPath ? `require('${validateOnClientImportPath}')` : '{}'};
|
|
55
|
+
const prefix = '${apiEntryPoint}';
|
|
56
|
+
`;
|
|
57
|
+
for (let i = 0; i < segments.length; i++) {
|
|
58
|
+
const { segmentName } = segments[i];
|
|
59
|
+
const schema = segmentsSchema[segmentName];
|
|
60
|
+
if (!schema) {
|
|
61
|
+
throw new Error(`Unable to generate client. No schema found for ${formatLoggedSegmentName(segmentName)}`);
|
|
62
|
+
}
|
|
63
|
+
if (!schema.emitSchema)
|
|
64
|
+
continue;
|
|
65
|
+
for (const key of Object.keys(schema.controllers)) {
|
|
66
|
+
dts += `export const ${key}: ReturnType<typeof clientizeController<Controllers${i}["${key}"], Options>>;\n`;
|
|
67
|
+
js += `exports.${key} = clientizeController(schema['${segmentName}'].controllers.${key}, '${segmentName}', { fetcher, validateOnClient, defaultOptions: { prefix } });\n`;
|
|
68
|
+
ts += `export const ${key} = clientizeController<Controllers${i}["${key}"], Options>(schema['${segmentName}'].controllers.${key}, '${segmentName}', { fetcher, validateOnClient, defaultOptions: { prefix } });\n`;
|
|
69
|
+
}
|
|
70
|
+
for (const key of Object.keys(schema.workers)) {
|
|
71
|
+
dts += `export const ${key}: ReturnType<typeof promisifyWorker<Workers${i}["${key}"]>>;\n`;
|
|
72
|
+
js += `exports.${key} = promisifyWorker(null, schema['${segmentName}'].workers.${key});\n`;
|
|
73
|
+
ts += `export const ${key} = promisifyWorker<Workers${i}["${key}"]>(null, schema['${segmentName}'].workers.${key});\n`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const localJsAbsolutePath = path.join(clientoOutDirAbsolutePath, 'client.js');
|
|
77
|
+
const localDtsAbsolutePath = path.join(clientoOutDirAbsolutePath, 'client.d.ts');
|
|
78
|
+
const localTsAbsolutePath = path.join(clientoOutDirAbsolutePath, 'index.ts');
|
|
79
|
+
const existingJs = await fs.readFile(localJsAbsolutePath, 'utf-8').catch(() => '');
|
|
80
|
+
const existingDts = await fs.readFile(localDtsAbsolutePath, 'utf-8').catch(() => '');
|
|
81
|
+
const existingTs = await fs.readFile(localTsAbsolutePath, 'utf-8').catch(() => '');
|
|
82
|
+
if (config.prettifyClient) {
|
|
83
|
+
js = await prettify(js, localJsAbsolutePath);
|
|
84
|
+
dts = await prettify(dts, localDtsAbsolutePath);
|
|
85
|
+
ts = await prettify(ts, localTsAbsolutePath);
|
|
86
|
+
}
|
|
87
|
+
if (existingJs === js && existingDts === dts && existingTs === ts) {
|
|
88
|
+
log.info(`Client is up to date and doesn't need to be regenerated (${Date.now() - now}ms)`);
|
|
89
|
+
return { written: false, path: clientoOutDirAbsolutePath };
|
|
90
|
+
}
|
|
91
|
+
await fs.mkdir(clientoOutDirAbsolutePath, { recursive: true });
|
|
92
|
+
await fs.writeFile(localJsAbsolutePath, js);
|
|
93
|
+
await fs.writeFile(localDtsAbsolutePath, dts);
|
|
94
|
+
await fs.writeFile(localTsAbsolutePath, ts);
|
|
95
|
+
log.info(`Client generated in ${Date.now() - now}ms`);
|
|
96
|
+
return { written: true, path: clientoOutDirAbsolutePath };
|
|
97
|
+
}
|
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
import readConfig from './readConfig.mjs';
|
|
2
|
-
import
|
|
3
|
-
import getSrcRoot from './getSrcRoot.mjs';
|
|
4
|
-
import path from 'path';
|
|
2
|
+
import getRelativeSrcRoot from './getRelativeSrcRoot.mjs';
|
|
5
3
|
export default async function getConfig({ clientOutDir }) {
|
|
6
4
|
const env = process.env;
|
|
7
5
|
const userConfig = await readConfig();
|
|
8
|
-
const srcRoot = await
|
|
9
|
-
const cwd = process.cwd();
|
|
6
|
+
const srcRoot = await getRelativeSrcRoot();
|
|
10
7
|
const config = {
|
|
11
|
-
modulesDir:
|
|
12
|
-
validateOnClient:
|
|
13
|
-
validationLibrary:
|
|
14
|
-
fetcher:
|
|
15
|
-
|
|
16
|
-
clientOutDir: clientOutDir ?? env.VOVK_CLIENT_OUT_DIR ?? userConfig.clientOutDir ?? './node_modules/.vovk',
|
|
8
|
+
modulesDir: env.VOVK_MODULES_DIR ?? userConfig.modulesDir ?? './' + [srcRoot, 'modules'].filter(Boolean).join('/'),
|
|
9
|
+
validateOnClient: env.VOVK_VALIDATE_ON_CLIENT ?? userConfig.validateOnClient ?? null,
|
|
10
|
+
validationLibrary: env.VOVK_VALIDATION_LIBRARY ?? userConfig.validationLibrary ?? null,
|
|
11
|
+
fetcher: env.VOVK_FETCHER ?? userConfig.fetcher ?? 'vovk/client/defaultFetcher',
|
|
12
|
+
schemaOutDir: env.VOVK_SCHEMA_OUT_DIR ?? userConfig.schemaOutDir ?? './.vovk-schema',
|
|
13
|
+
clientOutDir: clientOutDir ?? env.VOVK_CLIENT_OUT_DIR ?? userConfig.clientOutDir ?? './node_modules/.vovk-client',
|
|
17
14
|
origin: (env.VOVK_ORIGIN ?? userConfig.origin ?? '').replace(/\/$/, ''), // Remove trailing slash
|
|
18
15
|
rootEntry: env.VOVK_ROOT_ENTRY ?? userConfig.rootEntry ?? 'api',
|
|
19
16
|
rootSegmentModulesDirName: env.VOVK_ROOT_SEGMENT_MODULES_DIR_NAME ?? userConfig.rootSegmentModulesDirName ?? '',
|
|
20
17
|
logLevel: env.VOVK_LOG_LEVEL ?? userConfig.logLevel ?? 'debug', // TODO: change to 'warn' when v3 is ready
|
|
18
|
+
prettifyClient: userConfig.prettifyClient ?? false,
|
|
19
|
+
templates: {
|
|
20
|
+
service: 'vovk-cli/templates/service.ejs',
|
|
21
|
+
controller: 'vovk-cli/templates/controller.ejs',
|
|
22
|
+
worker: 'vovk-cli/templates/worker.ejs',
|
|
23
|
+
...userConfig.templates,
|
|
24
|
+
},
|
|
21
25
|
};
|
|
26
|
+
// forceAppDir is used for testing purposes
|
|
22
27
|
return { config, srcRoot };
|
|
23
28
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function getConfigPath(relativePath?: string): Promise<string | null>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
export default async function getConfigPath(relativePath = '') {
|
|
4
|
+
const rootDir = path.resolve(process.cwd(), relativePath || '');
|
|
5
|
+
const baseName = 'vovk.config';
|
|
6
|
+
const extensions = ['cjs', 'mjs', 'js'];
|
|
7
|
+
for (const ext of extensions) {
|
|
8
|
+
const filePath = path.join(rootDir, `${baseName}.${ext}`);
|
|
9
|
+
try {
|
|
10
|
+
await fs.stat(filePath);
|
|
11
|
+
return filePath; // Return the path if the file exists
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// Empty
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return null; // Return null if no config file was found
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function getRelativeSrcRoot(): Promise<"." | "./src">;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import directoryExists from './directoryExists.mjs';
|
|
3
|
-
export default async function
|
|
3
|
+
export default async function getRelativeSrcRoot() {
|
|
4
4
|
const cwd = process.cwd();
|
|
5
5
|
// Next.js Docs: src/app or src/pages will be ignored if app or pages are present in the root directory.
|
|
6
6
|
if (await directoryExists(path.join(cwd, 'app'))) {
|
|
7
|
-
return
|
|
7
|
+
return '.';
|
|
8
8
|
}
|
|
9
9
|
else if (await directoryExists(path.join(cwd, 'src/app'))) {
|
|
10
|
-
return
|
|
10
|
+
return './src';
|
|
11
11
|
}
|
|
12
12
|
throw new Error(`Could not find app router directory. Check Next.js docs for more info.`);
|
|
13
13
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import loglevel from 'loglevel';
|
|
2
1
|
export type ProjectInfo = Awaited<ReturnType<typeof getProjectInfo>>;
|
|
3
2
|
export default function getProjectInfo({ port: givenPort, clientOutDir, }?: {
|
|
4
3
|
port?: number;
|
|
@@ -6,21 +5,18 @@ export default function getProjectInfo({ port: givenPort, clientOutDir, }?: {
|
|
|
6
5
|
}): Promise<{
|
|
7
6
|
cwd: string;
|
|
8
7
|
port: string;
|
|
9
|
-
vovkPort: string;
|
|
10
8
|
apiEntryPoint: string;
|
|
11
|
-
apiPrefix: string;
|
|
12
9
|
apiDir: string;
|
|
13
10
|
srcRoot: string;
|
|
14
|
-
|
|
15
|
-
metadataOutImportPath: string;
|
|
16
|
-
clientOutFullPath: string;
|
|
11
|
+
schemaOutImportPath: string;
|
|
17
12
|
fetcherClientImportPath: string;
|
|
18
|
-
|
|
13
|
+
validateOnClientImportPath: string | null;
|
|
14
|
+
config: Required<Omit<import("../types.mjs").VovkConfig, "_devForceAppDir">>;
|
|
19
15
|
log: {
|
|
20
16
|
info: (msg: string) => void;
|
|
21
17
|
warn: (msg: string) => void;
|
|
22
18
|
error: (msg: string) => void;
|
|
23
19
|
debug: (msg: string) => void;
|
|
24
|
-
raw: loglevel.RootLogger;
|
|
20
|
+
raw: import("loglevel").RootLogger;
|
|
25
21
|
};
|
|
26
22
|
}>;
|
|
@@ -1,44 +1,31 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import loglevel from 'loglevel';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
2
|
import getConfig from './getConfig.mjs';
|
|
3
|
+
import getLogger from '../utils/getLogger.mjs';
|
|
5
4
|
export default async function getProjectInfo({ port: givenPort, clientOutDir, } = {}) {
|
|
6
|
-
const env = process.env;
|
|
7
5
|
const port = givenPort?.toString() ?? process.env.PORT ?? '3000';
|
|
8
6
|
// Make PORT available to the config file at getConfig
|
|
9
7
|
process.env.PORT = port;
|
|
10
8
|
const cwd = process.cwd();
|
|
11
9
|
const { config, srcRoot } = await getConfig({ clientOutDir });
|
|
12
|
-
const
|
|
13
|
-
const apiEntryPoint = `${config.origin}/${config.rootEntry}`; // ??? TODO
|
|
14
|
-
const apiPrefix = `${config.origin}/${config.rootEntry}`; // ??? TODO
|
|
10
|
+
const apiEntryPoint = `${config.origin ?? ''}/${config.rootEntry}`;
|
|
15
11
|
const apiDir = path.join(srcRoot, 'app', config.rootEntry);
|
|
16
|
-
const
|
|
17
|
-
const metadataOutImportPath = path.relative(config.clientOutDir, metadataOutFullPath);
|
|
12
|
+
const schemaOutImportPath = path.relative(config.clientOutDir, config.schemaOutDir);
|
|
18
13
|
const fetcherClientImportPath = config.fetcher.startsWith('.')
|
|
19
14
|
? path.relative(config.clientOutDir, config.fetcher)
|
|
20
15
|
: config.fetcher;
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
error: (msg) => loglevel.error(chalk.redBright(`🐺 ${msg}`)),
|
|
26
|
-
debug: (msg) => loglevel.debug(chalk.gray(`🐺 ${msg}`)),
|
|
27
|
-
raw: loglevel,
|
|
28
|
-
};
|
|
29
|
-
loglevel.setLevel(config.logLevel);
|
|
16
|
+
const validateOnClientImportPath = config.validateOnClient?.startsWith('.')
|
|
17
|
+
? path.relative(config.clientOutDir, config.validateOnClient)
|
|
18
|
+
: config.validateOnClient;
|
|
19
|
+
const log = getLogger(config.logLevel);
|
|
30
20
|
return {
|
|
31
21
|
cwd,
|
|
32
22
|
port,
|
|
33
|
-
vovkPort,
|
|
34
23
|
apiEntryPoint,
|
|
35
|
-
apiPrefix,
|
|
36
24
|
apiDir,
|
|
37
25
|
srcRoot,
|
|
38
|
-
|
|
39
|
-
metadataOutImportPath,
|
|
40
|
-
clientOutFullPath,
|
|
26
|
+
schemaOutImportPath,
|
|
41
27
|
fetcherClientImportPath,
|
|
28
|
+
validateOnClientImportPath,
|
|
42
29
|
config,
|
|
43
30
|
log,
|
|
44
31
|
};
|
|
@@ -1,32 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import path from 'path';
|
|
3
|
-
async function findConfigPath() {
|
|
4
|
-
const rootDir = process.cwd();
|
|
5
|
-
const baseName = 'vovk.config';
|
|
6
|
-
const extensions = ['cjs', 'mjs', 'js'];
|
|
7
|
-
for (const ext of extensions) {
|
|
8
|
-
const filePath = path.join(rootDir, `${baseName}.${ext}`);
|
|
9
|
-
try {
|
|
10
|
-
await fs.stat(filePath);
|
|
11
|
-
return filePath; // Return the path if the file exists
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
// If the file doesn't exist, an error is thrown. Catch it and continue checking.
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return null; // Return null if no config file was found
|
|
18
|
-
}
|
|
1
|
+
import getConfigPath from './getConfigPath.mjs';
|
|
19
2
|
async function readConfig() {
|
|
20
|
-
const configPath = await
|
|
3
|
+
const configPath = await getConfigPath();
|
|
21
4
|
let config = {};
|
|
22
5
|
if (!configPath) {
|
|
23
6
|
return config;
|
|
24
7
|
}
|
|
25
8
|
try {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
({ default: config } = (await import(`${configPath}?cache=${cacheBuster}`)));
|
|
29
|
-
}
|
|
9
|
+
const cacheBuster = Date.now();
|
|
10
|
+
({ default: config } = (await import(`${configPath}?cache=${cacheBuster}`)));
|
|
30
11
|
}
|
|
31
12
|
catch (e) {
|
|
32
13
|
// eslint-disable-next-line no-console
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import type { LogLevelNames } from 'loglevel';
|
|
3
|
+
import type { VovkConfig, VovkEnv } from './types.mjs';
|
|
3
4
|
export type { VovkConfig, VovkEnv };
|
|
5
|
+
export interface InitOptions {
|
|
6
|
+
yes: boolean;
|
|
7
|
+
logLevel: LogLevelNames;
|
|
8
|
+
}
|
|
9
|
+
export interface NewOptions {
|
|
10
|
+
dryRun: boolean;
|
|
11
|
+
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import path from 'path';
|
|
2
3
|
import { Command } from 'commander';
|
|
4
|
+
import { readFileSync } from 'fs';
|
|
3
5
|
import concurrently from 'concurrently';
|
|
4
6
|
import getAvailablePort from './utils/getAvailablePort.mjs';
|
|
5
|
-
import { VovkCLIServer } from './server/index.mjs';
|
|
6
7
|
import getProjectInfo from './getProjectInfo/index.mjs';
|
|
7
|
-
import generateClient from './
|
|
8
|
+
import generateClient from './generateClient.mjs';
|
|
8
9
|
import locateSegments from './locateSegments.mjs';
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
10
|
+
import { VovkCLIWatcher } from './watcher/index.mjs';
|
|
11
|
+
import { Init } from './init/index.mjs';
|
|
12
|
+
import newComponents from './new/index.mjs';
|
|
11
13
|
const program = new Command();
|
|
12
14
|
const packageJSON = JSON.parse(readFileSync(path.join(import.meta.dirname, '../package.json'), 'utf-8'));
|
|
13
15
|
program.name('vovk').description('Vovk CLI').version(packageJSON.version);
|
|
14
16
|
program
|
|
15
17
|
.command('dev')
|
|
16
|
-
.description('Start
|
|
17
|
-
.option('--project <path>', 'Path to Next.js project', process.cwd())
|
|
18
|
+
.description('Start schema watcher (optional flag --next-dev to start it with Next.js)')
|
|
18
19
|
.option('--client-out <path>', 'Path to client output directory')
|
|
19
|
-
.option('--next-dev', 'Start
|
|
20
|
+
.option('--next-dev', 'Start schema watcher and Next.js with automatic port allocation', false)
|
|
20
21
|
.allowUnknownOption(true)
|
|
21
22
|
.action(async (options, command) => {
|
|
22
23
|
const portAttempts = 30;
|
|
@@ -34,12 +35,12 @@ program
|
|
|
34
35
|
if (options.nextDev) {
|
|
35
36
|
const { result } = concurrently([
|
|
36
37
|
{
|
|
37
|
-
command: `node ${import.meta.dirname}/
|
|
38
|
-
name: 'Vovk.ts
|
|
39
|
-
env: Object.assign({ PORT,
|
|
38
|
+
command: `node ${import.meta.dirname}/watcher/index.mjs`,
|
|
39
|
+
name: 'Vovk.ts Schema Watcher',
|
|
40
|
+
env: Object.assign({ PORT, __VOVK_START_WATCHER_IN_STANDALONE_MODE__: 'true' }, options.clientOut ? { VOVK_CLIENT_OUT_DIR: options.clientOut } : {}),
|
|
40
41
|
},
|
|
41
42
|
{
|
|
42
|
-
command: `
|
|
43
|
+
command: `npx next dev ${command.args.join(' ')}`,
|
|
43
44
|
name: 'Next.js Development Server',
|
|
44
45
|
env: { PORT },
|
|
45
46
|
},
|
|
@@ -51,12 +52,11 @@ program
|
|
|
51
52
|
await result;
|
|
52
53
|
}
|
|
53
54
|
finally {
|
|
54
|
-
//
|
|
55
|
-
console.log('🐺 Exiting...');
|
|
55
|
+
// do nothing, all processes are killed
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
59
|
-
void new
|
|
59
|
+
void new VovkCLIWatcher().start({ clientOutDir: options.clientOut });
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
62
|
program
|
|
@@ -65,14 +65,36 @@ program
|
|
|
65
65
|
.option('--client-out <path>', 'Path to output directory')
|
|
66
66
|
.action(async (options) => {
|
|
67
67
|
const projectInfo = await getProjectInfo({ clientOutDir: options.clientOut });
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
|
|
68
|
+
const { cwd, config, apiDir } = projectInfo;
|
|
69
|
+
const segments = await locateSegments(apiDir);
|
|
70
|
+
const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
|
|
71
|
+
const schema = (await import(path.join(schemaOutAbsolutePath, 'index.js')));
|
|
72
|
+
await generateClient(projectInfo, segments, schema.default);
|
|
71
73
|
});
|
|
74
|
+
program
|
|
75
|
+
.command('init [prefix]')
|
|
76
|
+
.description('Initialize Vovk project')
|
|
77
|
+
.option('-Y, --yes', 'Skip all prompts and use default values')
|
|
78
|
+
.option('--log-level <level>', 'Set log level', 'info')
|
|
79
|
+
.action((prefix = '.', options) => Init.main(prefix, options));
|
|
80
|
+
program
|
|
81
|
+
.command('new [components...]')
|
|
82
|
+
.alias('n')
|
|
83
|
+
.description('Create new components. "vovk new [...components] [segmentName/]moduleName" to create a new module or "vovk new segment [segmentName]" to create a new segment')
|
|
84
|
+
.option('--dry-run', 'Do not write files to disk')
|
|
85
|
+
.action((components, options) => newComponents(components, options));
|
|
72
86
|
program
|
|
73
87
|
.command('help')
|
|
74
88
|
.description('Show help message')
|
|
75
89
|
.action(() => program.help());
|
|
90
|
+
/*
|
|
91
|
+
vovk new segment [segmentName]
|
|
92
|
+
vovk new controller service [segmentName/]moduleName
|
|
93
|
+
vovk new c s w [segmentName/]moduleName
|
|
94
|
+
|
|
95
|
+
vovk c s w userApi/user
|
|
96
|
+
vovk new c s w user
|
|
97
|
+
*/
|
|
76
98
|
program.parse(process.argv);
|
|
77
99
|
if (!process.argv.slice(2).length) {
|
|
78
100
|
program.outputHelp();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import type { VovkConfig } from '../types.mjs';
|
|
3
|
+
import type { InitOptions } from '../index.mjs';
|
|
4
|
+
import getLogger from '../utils/getLogger.mjs';
|
|
5
|
+
declare abstract class Action<T> {
|
|
6
|
+
context: Context;
|
|
7
|
+
data: T;
|
|
8
|
+
action: () => void;
|
|
9
|
+
constructor(context: Context);
|
|
10
|
+
toJSON: () => T;
|
|
11
|
+
}
|
|
12
|
+
declare class Context {
|
|
13
|
+
actions: Action<unknown>[];
|
|
14
|
+
vovkConfig: VovkConfig;
|
|
15
|
+
root: string;
|
|
16
|
+
log: ReturnType<typeof getLogger>;
|
|
17
|
+
static main(prefix: string, { yes, logLevel }: InitOptions): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export declare const Init: typeof Context;
|
|
20
|
+
export {};
|