vovk-cli 0.0.1-beta.2 → 0.0.1-beta.4
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/getProjectInfo/index.d.mts +0 -1
- package/dist/getProjectInfo/index.mjs +1 -3
- package/dist/server/generateClient.mjs +2 -2
- package/dist/server/index.mjs +2 -2
- package/package.json +3 -2
- package/.eslintrc.mjs +0 -20
- 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
|
+
|
|
@@ -10,8 +10,7 @@ export default async function getProjectInfo({ port: givenPort, clientOutDir, }
|
|
|
10
10
|
const cwd = process.cwd();
|
|
11
11
|
const { config, srcRoot } = await getConfig({ clientOutDir });
|
|
12
12
|
const vovkPort = env.VOVK_PORT || (parseInt(port) + 6969).toString();
|
|
13
|
-
const apiEntryPoint = `${config.origin}/${config.rootEntry}`;
|
|
14
|
-
const apiPrefix = `${config.origin}/${config.rootEntry}`; // ??? TODO
|
|
13
|
+
const apiEntryPoint = `${config.origin ?? ''}/${config.rootEntry}`;
|
|
15
14
|
const apiDir = path.join(srcRoot, 'app', config.rootEntry);
|
|
16
15
|
const metadataOutFullPath = path.join(cwd, config.metadataOutDir);
|
|
17
16
|
const metadataOutImportPath = path.relative(config.clientOutDir, metadataOutFullPath);
|
|
@@ -32,7 +31,6 @@ export default async function getProjectInfo({ port: givenPort, clientOutDir, }
|
|
|
32
31
|
port,
|
|
33
32
|
vovkPort,
|
|
34
33
|
apiEntryPoint,
|
|
35
|
-
apiPrefix,
|
|
36
34
|
apiDir,
|
|
37
35
|
srcRoot,
|
|
38
36
|
metadataOutFullPath,
|
|
@@ -48,11 +48,11 @@ type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
|
|
|
48
48
|
ts += `
|
|
49
49
|
${validatePath ? `import validateOnClient from '${validatePath}';\n` : '\nconst validateOnClient = undefined;'}
|
|
50
50
|
type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
|
|
51
|
-
const prefix = '${projectInfo.
|
|
51
|
+
const prefix = '${projectInfo.apiEntryPoint}';
|
|
52
52
|
`;
|
|
53
53
|
js += `
|
|
54
54
|
const { default: validateOnClient = null } = ${validatePath ? `require('${validatePath}')` : '{}'};
|
|
55
|
-
const prefix = '${projectInfo.
|
|
55
|
+
const prefix = '${projectInfo.apiEntryPoint}';
|
|
56
56
|
`;
|
|
57
57
|
for (let i = 0; i < segments.length; i++) {
|
|
58
58
|
const { segmentName } = segments[i];
|
package/dist/server/index.mjs
CHANGED
|
@@ -183,8 +183,8 @@ export class VovkCLIServer {
|
|
|
183
183
|
}
|
|
184
184
|
};
|
|
185
185
|
#ping = debounceWithArgs((segmentName) => {
|
|
186
|
-
const { apiEntryPoint, log } = this.#projectInfo;
|
|
187
|
-
const endpoint = `${apiEntryPoint}/${segmentName ? `${segmentName}/` : ''}_vovk-ping_`;
|
|
186
|
+
const { apiEntryPoint, log, port } = this.#projectInfo;
|
|
187
|
+
const endpoint = `${apiEntryPoint.startsWith('http') ? apiEntryPoint : `http://localhost:${port}${apiEntryPoint}`}/${segmentName ? `${segmentName}/` : ''}_vovk-ping_`;
|
|
188
188
|
log.debug(`Pinging segment "${segmentName}" at ${endpoint}`);
|
|
189
189
|
const req = http.get(endpoint, (resp) => {
|
|
190
190
|
if (resp.statusCode !== 200) {
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vovk-cli",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.4",
|
|
4
4
|
"bin": {
|
|
5
5
|
"vovk": "./dist/index.mjs"
|
|
6
6
|
},
|
|
7
7
|
"type": "module",
|
|
8
8
|
"types": "./dist/index.d.mts",
|
|
9
|
+
"prefix": ".",
|
|
9
10
|
"scripts": {
|
|
10
11
|
"build": "rm -rf dist tsconfig.tsbuildinfo && tsc",
|
|
11
12
|
"test": "ts-node test/index.mts",
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
},
|
|
30
31
|
"homepage": "https://vovk.dev",
|
|
31
32
|
"peerDependencies": {
|
|
32
|
-
"vovk": "^3.0.0-beta.
|
|
33
|
+
"vovk": "^3.0.0-beta.8"
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
35
36
|
"@inquirer/prompts": "^5.3.8",
|
package/.eslintrc.mjs
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
extends: '../../.eslintrc.js',
|
|
3
|
-
rules: {
|
|
4
|
-
'@typescript-eslint/no-unsafe-argument': 'off',
|
|
5
|
-
'@typescript-eslint/no-unsafe-assignment': 'off',
|
|
6
|
-
'@typescript-eslint/no-var-requires': 'off',
|
|
7
|
-
'@typescript-eslint/no-unsafe-call': 'off',
|
|
8
|
-
'@typescript-eslint/no-unsafe-member-access': 'off',
|
|
9
|
-
'@typescript-eslint/no-unsafe-return': 'off',
|
|
10
|
-
'no-console': 'error',
|
|
11
|
-
},
|
|
12
|
-
parserOptions: {
|
|
13
|
-
ecmaVersion: 2022,
|
|
14
|
-
sourceType: 'module',
|
|
15
|
-
project: './tsconfig.test.json',
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
17
|
-
tsconfigRootDir: __dirname,
|
|
18
|
-
createDefaultProgram: true,
|
|
19
|
-
},
|
|
20
|
-
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import readConfig from './readConfig.mjs';
|
|
2
|
-
import type { VovkConfig, VovkEnv } from '../types.mjs';
|
|
3
|
-
import getCwdPath from './getCwdPath.mjs';
|
|
4
|
-
import getSrcRoot from './getSrcRoot.mjs';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
|
|
7
|
-
export default async function getConfig({ clientOutDir }: { clientOutDir?: string }) {
|
|
8
|
-
const env = process.env as VovkEnv;
|
|
9
|
-
const userConfig = await readConfig();
|
|
10
|
-
const srcRoot = await getSrcRoot();
|
|
11
|
-
const cwd = process.cwd();
|
|
12
|
-
const config: Required<VovkConfig> = {
|
|
13
|
-
modulesDir: path.join(
|
|
14
|
-
cwd,
|
|
15
|
-
env.VOVK_MODULES_DIR ?? userConfig.modulesDir ?? './' + [srcRoot, 'modules'].filter(Boolean).join('/')
|
|
16
|
-
),
|
|
17
|
-
validateOnClient: getCwdPath(env.VOVK_VALIDATE_ON_CLIENT ?? userConfig.validateOnClient ?? null),
|
|
18
|
-
validationLibrary: getCwdPath(env.VOVK_VALIDATION_LIBRARY ?? userConfig.validationLibrary ?? null),
|
|
19
|
-
fetcher: getCwdPath(env.VOVK_FETCHER ?? userConfig.fetcher ?? 'vovk/client/defaultFetcher'),
|
|
20
|
-
metadataOutDir: env.VOVK_METADATA_OUT_DIR ?? userConfig.metadataOutDir ?? './.vovk-schema',
|
|
21
|
-
clientOutDir: clientOutDir ?? env.VOVK_CLIENT_OUT_DIR ?? userConfig.clientOutDir ?? './node_modules/.vovk',
|
|
22
|
-
origin: (env.VOVK_ORIGIN ?? userConfig.origin ?? '').replace(/\/$/, ''), // Remove trailing slash
|
|
23
|
-
rootEntry: env.VOVK_ROOT_ENTRY ?? userConfig.rootEntry ?? 'api',
|
|
24
|
-
rootSegmentModulesDirName: env.VOVK_ROOT_SEGMENT_MODULES_DIR_NAME ?? userConfig.rootSegmentModulesDirName ?? '',
|
|
25
|
-
logLevel: env.VOVK_LOG_LEVEL ?? userConfig.logLevel ?? 'debug', // TODO: change to 'warn' when v3 is ready
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
return { config, srcRoot };
|
|
29
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
|
|
3
|
-
// TODO Rename
|
|
4
|
-
export default function getCwdPath<T extends string | null>(inputPath: T, baseDir = process.cwd()): T {
|
|
5
|
-
if (inputPath === null) {
|
|
6
|
-
return null as T;
|
|
7
|
-
}
|
|
8
|
-
// Check if the path is absolute
|
|
9
|
-
if (path.isAbsolute(inputPath) || inputPath.startsWith('./') || inputPath.startsWith('../')) {
|
|
10
|
-
return path.resolve(baseDir, inputPath) as T;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// If it's a module or absolute path, keep it as is
|
|
14
|
-
return inputPath;
|
|
15
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import directoryExists from './directoryExists.mjs';
|
|
3
|
-
|
|
4
|
-
export default async function getSrcRoot() {
|
|
5
|
-
const cwd = process.cwd();
|
|
6
|
-
// Next.js Docs: src/app or src/pages will be ignored if app or pages are present in the root directory.
|
|
7
|
-
if (await directoryExists(path.join(cwd, 'app'))) {
|
|
8
|
-
return cwd;
|
|
9
|
-
} else if (await directoryExists(path.join(cwd, 'src/app'))) {
|
|
10
|
-
return path.join(cwd, 'src');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
throw new Error(`Could not find app router directory. Check Next.js docs for more info.`);
|
|
14
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type { VovkEnv } from '../types.mjs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import loglevel from 'loglevel';
|
|
4
|
-
import chalk from 'chalk';
|
|
5
|
-
import getConfig from './getConfig.mjs';
|
|
6
|
-
|
|
7
|
-
export type ProjectInfo = Awaited<ReturnType<typeof getProjectInfo>>;
|
|
8
|
-
|
|
9
|
-
export default async function getProjectInfo({
|
|
10
|
-
port: givenPort,
|
|
11
|
-
clientOutDir,
|
|
12
|
-
}: { port?: number; clientOutDir?: string } = {}) {
|
|
13
|
-
const env = process.env as VovkEnv;
|
|
14
|
-
const port = givenPort?.toString() ?? process.env.PORT ?? '3000';
|
|
15
|
-
|
|
16
|
-
// Make PORT available to the config file at getConfig
|
|
17
|
-
process.env.PORT = port;
|
|
18
|
-
|
|
19
|
-
const cwd = process.cwd();
|
|
20
|
-
const { config, srcRoot } = await getConfig({ clientOutDir });
|
|
21
|
-
const vovkPort = env.VOVK_PORT || (parseInt(port) + 6969).toString();
|
|
22
|
-
const apiEntryPoint = `${config.origin}/${config.rootEntry}`; // ??? TODO
|
|
23
|
-
const apiPrefix = `${config.origin}/${config.rootEntry}`; // ??? TODO
|
|
24
|
-
const apiDir = path.join(srcRoot, 'app', config.rootEntry);
|
|
25
|
-
|
|
26
|
-
const metadataOutFullPath = path.join(cwd, config.metadataOutDir);
|
|
27
|
-
const metadataOutImportPath = path.relative(config.clientOutDir, metadataOutFullPath);
|
|
28
|
-
const fetcherClientImportPath = config.fetcher.startsWith('.')
|
|
29
|
-
? path.relative(config.clientOutDir, config.fetcher)
|
|
30
|
-
: config.fetcher;
|
|
31
|
-
|
|
32
|
-
const clientOutFullPath = path.join(cwd, config.clientOutDir);
|
|
33
|
-
|
|
34
|
-
const log = {
|
|
35
|
-
info: (msg: string) => loglevel.info(chalk.blueBright(`🐺 ${msg}`)),
|
|
36
|
-
warn: (msg: string) => loglevel.warn(chalk.yellowBright(`🐺 ${msg}`)),
|
|
37
|
-
error: (msg: string) => loglevel.error(chalk.redBright(`🐺 ${msg}`)),
|
|
38
|
-
debug: (msg: string) => loglevel.debug(chalk.gray(`🐺 ${msg}`)),
|
|
39
|
-
raw: loglevel,
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
loglevel.setLevel(config.logLevel);
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
cwd,
|
|
46
|
-
port,
|
|
47
|
-
vovkPort,
|
|
48
|
-
apiEntryPoint,
|
|
49
|
-
apiPrefix,
|
|
50
|
-
apiDir,
|
|
51
|
-
srcRoot,
|
|
52
|
-
metadataOutFullPath,
|
|
53
|
-
metadataOutImportPath,
|
|
54
|
-
clientOutFullPath,
|
|
55
|
-
fetcherClientImportPath,
|
|
56
|
-
config,
|
|
57
|
-
log,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import type { VovkConfig } from '../types.mjs';
|
|
4
|
-
|
|
5
|
-
async function findConfigPath(): Promise<string | null> {
|
|
6
|
-
const rootDir = process.cwd();
|
|
7
|
-
const baseName = 'vovk.config';
|
|
8
|
-
const extensions = ['cjs', 'mjs', 'js'];
|
|
9
|
-
|
|
10
|
-
for (const ext of extensions) {
|
|
11
|
-
const filePath = path.join(rootDir, `${baseName}.${ext}`);
|
|
12
|
-
try {
|
|
13
|
-
await fs.stat(filePath);
|
|
14
|
-
return filePath; // Return the path if the file exists
|
|
15
|
-
} catch {
|
|
16
|
-
// If the file doesn't exist, an error is thrown. Catch it and continue checking.
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return null; // Return null if no config file was found
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function readConfig(): Promise<VovkConfig> {
|
|
24
|
-
const configPath = await findConfigPath();
|
|
25
|
-
let config: VovkConfig = {};
|
|
26
|
-
|
|
27
|
-
if (!configPath) {
|
|
28
|
-
return config;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
if (configPath.endsWith('.cjs') || configPath.endsWith('.js')) {
|
|
33
|
-
const cacheBuster = Date.now();
|
|
34
|
-
({ default: config } = (await import(`${configPath}?cache=${cacheBuster}`)) as { default: VovkConfig });
|
|
35
|
-
}
|
|
36
|
-
} catch (e) {
|
|
37
|
-
// eslint-disable-next-line no-console
|
|
38
|
-
console.error('🐺 ❌ Error reading config file:', (e as Error).message);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return config;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export default readConfig;
|
package/src/index.mts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import concurrently from 'concurrently';
|
|
4
|
-
import getAvailablePort from './utils/getAvailablePort.mjs';
|
|
5
|
-
import { VovkCLIServer } from './server/index.mjs';
|
|
6
|
-
import getProjectInfo from './getProjectInfo/index.mjs';
|
|
7
|
-
import generateClient from './server/generateClient.mjs';
|
|
8
|
-
import locateSegments from './locateSegments.mjs';
|
|
9
|
-
import { VovkConfig, VovkEnv } from './types.mjs';
|
|
10
|
-
import { VovkMetadata } from 'vovk';
|
|
11
|
-
import path from 'path';
|
|
12
|
-
import { readFileSync } from 'fs';
|
|
13
|
-
|
|
14
|
-
export type { VovkConfig, VovkEnv };
|
|
15
|
-
|
|
16
|
-
interface DevOptions {
|
|
17
|
-
project: string;
|
|
18
|
-
clientOut?: string;
|
|
19
|
-
nextDev: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface GenerateOptions {
|
|
23
|
-
clientOut?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const program = new Command();
|
|
27
|
-
|
|
28
|
-
const packageJSON = JSON.parse(readFileSync(path.join(import.meta.dirname, '../package.json'), 'utf-8')) as {
|
|
29
|
-
version: string;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
program.name('vovk').description('Vovk CLI').version(packageJSON.version);
|
|
33
|
-
|
|
34
|
-
program
|
|
35
|
-
.command('dev')
|
|
36
|
-
.description('Start development server (optional flag --next-dev to start Vovk Server with Next.js)')
|
|
37
|
-
.option('--project <path>', 'Path to Next.js project', process.cwd())
|
|
38
|
-
.option('--client-out <path>', 'Path to client output directory')
|
|
39
|
-
.option('--next-dev', 'Start Vovk Server and Next.js with automatic port allocation', false)
|
|
40
|
-
.allowUnknownOption(true)
|
|
41
|
-
.action(async (options: DevOptions, command: Command) => {
|
|
42
|
-
const portAttempts = 30;
|
|
43
|
-
const PORT = !options.nextDev
|
|
44
|
-
? process.env.PORT
|
|
45
|
-
: process.env.PORT ||
|
|
46
|
-
(await getAvailablePort(3000, portAttempts, 0, (failedPort, tryingPort) =>
|
|
47
|
-
// eslint-disable-next-line no-console
|
|
48
|
-
console.warn(`🐺 Next.js Port ${failedPort} is in use, trying ${tryingPort} instead.`)
|
|
49
|
-
).catch(() => {
|
|
50
|
-
throw new Error(`🐺 ❌ Failed to find available Next port after ${portAttempts} attempts`);
|
|
51
|
-
}));
|
|
52
|
-
|
|
53
|
-
if (!PORT) {
|
|
54
|
-
throw new Error('🐺 ❌ PORT env variable is required');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (options.nextDev) {
|
|
58
|
-
const { result } = concurrently(
|
|
59
|
-
[
|
|
60
|
-
{
|
|
61
|
-
command: `node ${import.meta.dirname}/server/index.mjs`,
|
|
62
|
-
name: 'Vovk.ts Metadata Server',
|
|
63
|
-
env: Object.assign(
|
|
64
|
-
{ PORT, __VOVK_START_SERVER_IN_STANDALONE_MODE__: 'true' as const },
|
|
65
|
-
options.clientOut ? { VOVK_CLIENT_OUT_DIR: options.clientOut } : {}
|
|
66
|
-
),
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
command: `cd ${options.project} && npx next dev ${command.args.join(' ')}`,
|
|
70
|
-
name: 'Next.js Development Server',
|
|
71
|
-
env: { PORT },
|
|
72
|
-
},
|
|
73
|
-
],
|
|
74
|
-
{
|
|
75
|
-
killOthers: ['failure', 'success'],
|
|
76
|
-
prefix: 'none',
|
|
77
|
-
}
|
|
78
|
-
);
|
|
79
|
-
try {
|
|
80
|
-
await result;
|
|
81
|
-
} finally {
|
|
82
|
-
// eslint-disable-next-line no-console
|
|
83
|
-
console.log('🐺 Exiting...');
|
|
84
|
-
}
|
|
85
|
-
} else {
|
|
86
|
-
void new VovkCLIServer().startServer({ clientOutDir: options.clientOut });
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
program
|
|
91
|
-
.command('generate')
|
|
92
|
-
.description('Generate client')
|
|
93
|
-
.option('--client-out <path>', 'Path to output directory')
|
|
94
|
-
.action(async (options: GenerateOptions) => {
|
|
95
|
-
const projectInfo = await getProjectInfo({ clientOutDir: options.clientOut });
|
|
96
|
-
const segments = await locateSegments(projectInfo.apiDir);
|
|
97
|
-
const metadata = (await import(path.join(projectInfo.metadataOutFullPath, 'index.js'))) as {
|
|
98
|
-
default: Record<string, VovkMetadata>;
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
await generateClient(projectInfo, segments, metadata.default);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
program
|
|
105
|
-
.command('help')
|
|
106
|
-
.description('Show help message')
|
|
107
|
-
.action(() => program.help());
|
|
108
|
-
|
|
109
|
-
program.parse(process.argv);
|
|
110
|
-
|
|
111
|
-
if (!process.argv.slice(2).length) {
|
|
112
|
-
program.outputHelp();
|
|
113
|
-
}
|
package/src/init.mts
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/*
|
|
3
|
-
npx vovk-cli init
|
|
4
|
-
- Check if the project is already initialized
|
|
5
|
-
- Do you want to reinitialize the project?
|
|
6
|
-
- Yes
|
|
7
|
-
- No (exit)
|
|
8
|
-
- Check for package.json, if not found, show error and exit
|
|
9
|
-
- Check for tsconfig.json, if not found, show error and exit
|
|
10
|
-
- Check Next.js installed
|
|
11
|
-
- Choose validation library: add to the installation list
|
|
12
|
-
- vovk-zod
|
|
13
|
-
- Further installation notes: install zod
|
|
14
|
-
- vovk-yup
|
|
15
|
-
- Further installation notes: install yup
|
|
16
|
-
- vovk-dto
|
|
17
|
-
- Further installation notes: install class-validator and class-transformer
|
|
18
|
-
- None
|
|
19
|
-
- If validation library is not None,
|
|
20
|
-
- Do you want to enable client validation?
|
|
21
|
-
- Yes
|
|
22
|
-
- Add client validation to the config
|
|
23
|
-
- No
|
|
24
|
-
- Do you want to use concurrently? (NO NEED, USE CONCURRENTLY BY DEFAULT)
|
|
25
|
-
- Yes (recommended)
|
|
26
|
-
- Add concurrently to the installation list
|
|
27
|
-
- No
|
|
28
|
-
- Do you want to update NPM scripts?
|
|
29
|
-
- Yes
|
|
30
|
-
- Update NPM scripts
|
|
31
|
-
- No
|
|
32
|
-
- if experimentalDecorators is not found in tsconfig.json,
|
|
33
|
-
- Do you want to add experimentalDecorators to tsconfig.json?
|
|
34
|
-
- Yes
|
|
35
|
-
- Add experimentalDecorators to tsconfig.json
|
|
36
|
-
- No
|
|
37
|
-
- Do you want to create route file with example service and controller? (NO NEED)
|
|
38
|
-
- Yes
|
|
39
|
-
- Create route file with example controller
|
|
40
|
-
- No, I will create it myself
|
|
41
|
-
- End
|
|
42
|
-
- If there are any packages to install, install them
|
|
43
|
-
- Show installation notes
|
|
44
|
-
- If there are any files to create, create
|
|
45
|
-
- If there are any config files to update, update
|
|
46
|
-
- If example route file is NOT created, show example route file and controller
|
|
47
|
-
- Show how to run the project
|
|
48
|
-
- If npm scripts are updated
|
|
49
|
-
- npm run dev
|
|
50
|
-
- If npm scripts are NOT updated
|
|
51
|
-
- If concurrently is installed
|
|
52
|
-
- concurrently "vovk dev" "next dev"
|
|
53
|
-
- If concurrently is NOT installed
|
|
54
|
-
- vovk dev --next-dev
|
|
55
|
-
- Open http://localhost:3000/api/hello-world
|
|
56
|
-
- Show how to make a request to the example route
|
|
57
|
-
- Show success message
|
|
58
|
-
*/
|
|
59
|
-
|
|
60
|
-
import { confirm } from '@inquirer/prompts';
|
|
61
|
-
// Or
|
|
62
|
-
// import confirm from '@inquirer/confirm';
|
|
63
|
-
|
|
64
|
-
// eslint-disable-next-line no-console
|
|
65
|
-
void confirm({ message: 'Continue?' }).then(console.info);
|
|
66
|
-
|
|
67
|
-
/*
|
|
68
|
-
const wizard = [
|
|
69
|
-
{
|
|
70
|
-
description: 'Check if the project is already initialized',
|
|
71
|
-
type: 'check',
|
|
72
|
-
choices: {
|
|
73
|
-
type: 'choice',
|
|
74
|
-
choices: [
|
|
75
|
-
{
|
|
76
|
-
label: 'Yes',
|
|
77
|
-
action: 'continue',
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
label: 'No',
|
|
81
|
-
action: 'exit',
|
|
82
|
-
exitMessage: 'Exiting...',
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
description: 'Check for package.json',
|
|
89
|
-
type: 'check',
|
|
90
|
-
handleCheck: () => {
|
|
91
|
-
// Check if the project is already initialized
|
|
92
|
-
},
|
|
93
|
-
yes: {
|
|
94
|
-
action: 'continue',
|
|
95
|
-
},
|
|
96
|
-
no: {
|
|
97
|
-
action: 'exit',
|
|
98
|
-
exitMessage: 'Exiting...',
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
description: 'Check for tsconfig.json',
|
|
103
|
-
type: 'check',
|
|
104
|
-
handleCheck: () => {
|
|
105
|
-
// Check for package.json
|
|
106
|
-
},
|
|
107
|
-
yes: {
|
|
108
|
-
action: 'continue',
|
|
109
|
-
},
|
|
110
|
-
no: {
|
|
111
|
-
action: 'exit',
|
|
112
|
-
exitMessage: 'Exiting...',
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
description: 'Check Next.js installed with app router',
|
|
117
|
-
type: 'check',
|
|
118
|
-
handleCheck: () => {
|
|
119
|
-
// Check for tsconfig.json
|
|
120
|
-
},
|
|
121
|
-
yes: {
|
|
122
|
-
action: 'continue',
|
|
123
|
-
},
|
|
124
|
-
no: {
|
|
125
|
-
action: 'exit',
|
|
126
|
-
exitMessage: 'Exiting...',
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
description: 'Choose validation library',
|
|
131
|
-
type: 'install',
|
|
132
|
-
choices: {
|
|
133
|
-
type: 'choice',
|
|
134
|
-
choices: [
|
|
135
|
-
{
|
|
136
|
-
package: 'vovk-zod',
|
|
137
|
-
action: 'install',
|
|
138
|
-
notes: 'Further installation notes: install zod',
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
package: 'vovk-yup',
|
|
142
|
-
action: 'install',
|
|
143
|
-
notes: 'Further installation notes: install yup',
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
package: 'vovk-dto',
|
|
147
|
-
action: 'install',
|
|
148
|
-
notes: 'Further installation notes: install class-validator and class-transformer',
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
package: null,
|
|
152
|
-
action: 'continue',
|
|
153
|
-
},
|
|
154
|
-
],
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
description: 'Do you want to enable client validation?',
|
|
159
|
-
type: 'choice',
|
|
160
|
-
choices: [
|
|
161
|
-
{
|
|
162
|
-
label: 'Yes',
|
|
163
|
-
action: 'updateConfig',
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
label: 'No',
|
|
167
|
-
action: 'updateConfig',
|
|
168
|
-
},
|
|
169
|
-
],
|
|
170
|
-
},
|
|
171
|
-
];
|
|
172
|
-
|
|
173
|
-
// export default console.info(wizard);
|
|
174
|
-
*/
|
package/src/locateSegments.mts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import fileExists from './utils/fileExists.mjs';
|
|
4
|
-
|
|
5
|
-
export type Segment = {
|
|
6
|
-
routeFilePath: string;
|
|
7
|
-
segmentName: string;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export default async function locateSegments(dir: string, rootDir = dir): Promise<Segment[]> {
|
|
11
|
-
let results: Segment[] = [];
|
|
12
|
-
|
|
13
|
-
// Read the contents of the directory
|
|
14
|
-
const list = await fs.readdir(dir);
|
|
15
|
-
|
|
16
|
-
// Iterate through each item in the directory
|
|
17
|
-
for (const file of list) {
|
|
18
|
-
const filePath = path.join(dir, file);
|
|
19
|
-
const stat = await fs.stat(filePath);
|
|
20
|
-
|
|
21
|
-
if (stat.isDirectory()) {
|
|
22
|
-
// Check if the directory name matches the pattern [[...something]]
|
|
23
|
-
if (file.startsWith('[[...') && file.endsWith(']]')) {
|
|
24
|
-
// Check if there's a route.ts file inside this directory
|
|
25
|
-
const routeFilePath = path.join(filePath, 'route.ts');
|
|
26
|
-
if (await fileExists(routeFilePath)) {
|
|
27
|
-
// Calculate the basePath relative to the root directory
|
|
28
|
-
const segmentName = path.relative(rootDir, dir);
|
|
29
|
-
results.push({ routeFilePath, segmentName });
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Recursively search inside subdirectories
|
|
34
|
-
const subDirResults = await locateSegments(filePath, rootDir);
|
|
35
|
-
results = results.concat(subDirResults);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return results;
|
|
40
|
-
}
|
package/src/postinstall.mts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Checks if a file exists at the given path.
|
|
6
|
-
* @param {string} filePath - The path to the file.
|
|
7
|
-
* @returns {Promise<boolean>} - A promise that resolves to true if the file exists, false otherwise.
|
|
8
|
-
*/
|
|
9
|
-
const fileExists = async (filePath: string): Promise<boolean> => !!(await fs.stat(filePath).catch(() => false));
|
|
10
|
-
|
|
11
|
-
async function postinstall(): Promise<void> {
|
|
12
|
-
const vovk = path.join(import.meta.dirname, '../../.vovk');
|
|
13
|
-
const js = path.join(vovk, 'client.js');
|
|
14
|
-
const ts = path.join(vovk, 'client.d.ts');
|
|
15
|
-
const index = path.join(vovk, 'index.ts');
|
|
16
|
-
|
|
17
|
-
if ((await fileExists(js)) || (await fileExists(ts)) || (await fileExists(index))) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
await fs.mkdir(vovk, { recursive: true });
|
|
22
|
-
await fs.writeFile(js, '/* postinstall */');
|
|
23
|
-
await fs.writeFile(ts, '/* postinstall */');
|
|
24
|
-
await fs.writeFile(index, '/* postinstall */');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
void postinstall();
|