vovk-cli 0.0.1-beta.0 → 0.0.1-beta.2
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/{.eslintrc.js → .eslintrc.mjs} +1 -1
- package/dist/getProjectInfo/directoryExists.mjs +10 -0
- package/dist/getProjectInfo/{getConfig.d.ts → getConfig.d.mts} +1 -1
- package/dist/getProjectInfo/getConfig.mjs +23 -0
- package/dist/getProjectInfo/getCwdPath.mjs +13 -0
- package/dist/getProjectInfo/getSrcRoot.mjs +13 -0
- package/dist/getProjectInfo/index.d.mts +26 -0
- package/dist/getProjectInfo/index.mjs +45 -0
- package/dist/getProjectInfo/{readConfig.d.ts → readConfig.d.mts} +1 -1
- package/dist/getProjectInfo/readConfig.mjs +37 -0
- package/dist/{index.d.ts → index.d.mts} +1 -1
- package/dist/index.mjs +79 -0
- package/dist/{init.js → init.mjs} +2 -4
- package/dist/locateSegments.mjs +29 -0
- package/dist/postinstall.mjs +22 -0
- package/dist/server/{createMetadataServer.js → createMetadataServer.mjs} +3 -9
- package/dist/server/{diffMetadata.js → diffMetadata.mjs} +5 -10
- package/dist/server/{ensureMetadataFiles.d.ts → ensureMetadataFiles.d.mts} +1 -1
- package/dist/server/ensureMetadataFiles.mjs +70 -0
- package/dist/server/{generateClient.d.ts → generateClient.d.mts} +2 -2
- package/dist/server/{generateClient.js → generateClient.mjs} +15 -21
- package/dist/server/{index.js → index.mjs} +32 -61
- package/dist/server/isMetadataEmpty.mjs +4 -0
- package/dist/server/{logDiffResult.d.ts → logDiffResult.d.mts} +2 -2
- package/dist/server/{logDiffResult.js → logDiffResult.mjs} +11 -17
- package/dist/server/{writeOneMetadataFile.d.ts → writeOneMetadataFile.d.mts} +1 -1
- package/dist/server/writeOneMetadataFile.mjs +27 -0
- package/dist/types.mjs +1 -0
- package/dist/utils/{debounceWithArgs.d.ts → debounceWithArgs.d.mts} +1 -1
- package/dist/utils/debounceWithArgs.mjs +14 -0
- package/dist/utils/fileExists.mjs +10 -0
- package/dist/utils/{getAvailablePort.js → getAvailablePort.mjs} +3 -8
- package/package.json +8 -8
- package/src/getProjectInfo/{getConfig.ts → getConfig.mts} +4 -4
- package/src/getProjectInfo/{getSrcRoot.ts → getSrcRoot.mts} +1 -1
- package/src/getProjectInfo/{index.ts → index.mts} +3 -7
- package/src/getProjectInfo/{readConfig.ts → readConfig.mts} +2 -8
- package/src/{index.ts → index.mts} +18 -17
- package/src/{locateSegments.ts → locateSegments.mts} +1 -1
- package/src/{postinstall.ts → postinstall.mts} +1 -1
- package/src/server/{diffMetadata.ts → diffMetadata.mts} +2 -2
- package/src/server/{ensureMetadataFiles.ts → ensureMetadataFiles.mts} +3 -3
- package/src/server/{generateClient.ts → generateClient.mts} +2 -2
- package/src/server/{index.ts → index.mts} +11 -10
- package/src/server/{isMetadataEmpty.ts → isMetadataEmpty.mts} +1 -1
- package/src/server/{logDiffResult.ts → logDiffResult.mts} +2 -2
- package/src/server/{writeOneMetadataFile.ts → writeOneMetadataFile.mts} +1 -1
- package/src/utils/{debounceWithArgs.ts → debounceWithArgs.mts} +2 -2
- package/test/{metadata-diff.test.ts → metadata-diff.test.mts} +1 -1
- package/test/{metadata-write.test.ts → metadata-write.test.mts} +1 -1
- package/test/{utils.test.ts → utils.test.mts} +1 -1
- package/tsconfig.json +6 -0
- package/tsconfig.test.json +1 -1
- package/dist/getProjectInfo/directoryExists.js +0 -16
- package/dist/getProjectInfo/getConfig.js +0 -29
- package/dist/getProjectInfo/getCwdPath.js +0 -19
- package/dist/getProjectInfo/getSrcRoot.js +0 -19
- package/dist/getProjectInfo/index.d.ts +0 -48
- package/dist/getProjectInfo/index.js +0 -78
- package/dist/getProjectInfo/readConfig.js +0 -73
- package/dist/index.js +0 -104
- package/dist/locateSegments.js +0 -58
- package/dist/postinstall.js +0 -27
- package/dist/server/ensureMetadataFiles.js +0 -100
- package/dist/server/isMetadataEmpty.js +0 -7
- package/dist/server/writeOneMetadataFile.js +0 -34
- package/dist/types.js +0 -2
- package/dist/utils/debounceWithArgs.js +0 -20
- package/dist/utils/fileExists.js +0 -16
- /package/dist/getProjectInfo/{directoryExists.d.ts → directoryExists.d.mts} +0 -0
- /package/dist/getProjectInfo/{getCwdPath.d.ts → getCwdPath.d.mts} +0 -0
- /package/dist/getProjectInfo/{getSrcRoot.d.ts → getSrcRoot.d.mts} +0 -0
- /package/dist/{init.d.ts → init.d.mts} +0 -0
- /package/dist/{locateSegments.d.ts → locateSegments.d.mts} +0 -0
- /package/dist/{postinstall.d.ts → postinstall.d.mts} +0 -0
- /package/dist/server/{createMetadataServer.d.ts → createMetadataServer.d.mts} +0 -0
- /package/dist/server/{diffMetadata.d.ts → diffMetadata.d.mts} +0 -0
- /package/dist/server/{index.d.ts → index.d.mts} +0 -0
- /package/dist/server/{isMetadataEmpty.d.ts → isMetadataEmpty.d.mts} +0 -0
- /package/dist/{types.d.ts → types.d.mts} +0 -0
- /package/dist/utils/{fileExists.d.ts → fileExists.d.mts} +0 -0
- /package/dist/utils/{getAvailablePort.d.ts → getAvailablePort.d.mts} +0 -0
- /package/src/getProjectInfo/{directoryExists.ts → directoryExists.mts} +0 -0
- /package/src/getProjectInfo/{getCwdPath.ts → getCwdPath.mts} +0 -0
- /package/src/{init.ts → init.mts} +0 -0
- /package/src/server/{createMetadataServer.ts → createMetadataServer.mts} +0 -0
- /package/src/{types.ts → types.mts} +0 -0
- /package/src/utils/{fileExists.ts → fileExists.mts} +0 -0
- /package/src/utils/{getAvailablePort.ts → getAvailablePort.mts} +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import readConfig from './readConfig.mjs';
|
|
2
|
+
import getCwdPath from './getCwdPath.mjs';
|
|
3
|
+
import getSrcRoot from './getSrcRoot.mjs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
export default async function getConfig({ clientOutDir }) {
|
|
6
|
+
const env = process.env;
|
|
7
|
+
const userConfig = await readConfig();
|
|
8
|
+
const srcRoot = await getSrcRoot();
|
|
9
|
+
const cwd = process.cwd();
|
|
10
|
+
const config = {
|
|
11
|
+
modulesDir: path.join(cwd, env.VOVK_MODULES_DIR ?? userConfig.modulesDir ?? './' + [srcRoot, 'modules'].filter(Boolean).join('/')),
|
|
12
|
+
validateOnClient: getCwdPath(env.VOVK_VALIDATE_ON_CLIENT ?? userConfig.validateOnClient ?? null),
|
|
13
|
+
validationLibrary: getCwdPath(env.VOVK_VALIDATION_LIBRARY ?? userConfig.validationLibrary ?? null),
|
|
14
|
+
fetcher: getCwdPath(env.VOVK_FETCHER ?? userConfig.fetcher ?? 'vovk/client/defaultFetcher'),
|
|
15
|
+
metadataOutDir: env.VOVK_METADATA_OUT_DIR ?? userConfig.metadataOutDir ?? './.vovk-schema',
|
|
16
|
+
clientOutDir: clientOutDir ?? env.VOVK_CLIENT_OUT_DIR ?? userConfig.clientOutDir ?? './node_modules/.vovk',
|
|
17
|
+
origin: (env.VOVK_ORIGIN ?? userConfig.origin ?? '').replace(/\/$/, ''), // Remove trailing slash
|
|
18
|
+
rootEntry: env.VOVK_ROOT_ENTRY ?? userConfig.rootEntry ?? 'api',
|
|
19
|
+
rootSegmentModulesDirName: env.VOVK_ROOT_SEGMENT_MODULES_DIR_NAME ?? userConfig.rootSegmentModulesDirName ?? '',
|
|
20
|
+
logLevel: env.VOVK_LOG_LEVEL ?? userConfig.logLevel ?? 'debug', // TODO: change to 'warn' when v3 is ready
|
|
21
|
+
};
|
|
22
|
+
return { config, srcRoot };
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
// TODO Rename
|
|
3
|
+
export default function getCwdPath(inputPath, baseDir = process.cwd()) {
|
|
4
|
+
if (inputPath === null) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
// Check if the path is absolute
|
|
8
|
+
if (path.isAbsolute(inputPath) || inputPath.startsWith('./') || inputPath.startsWith('../')) {
|
|
9
|
+
return path.resolve(baseDir, inputPath);
|
|
10
|
+
}
|
|
11
|
+
// If it's a module or absolute path, keep it as is
|
|
12
|
+
return inputPath;
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import directoryExists from './directoryExists.mjs';
|
|
3
|
+
export default async function getSrcRoot() {
|
|
4
|
+
const cwd = process.cwd();
|
|
5
|
+
// Next.js Docs: src/app or src/pages will be ignored if app or pages are present in the root directory.
|
|
6
|
+
if (await directoryExists(path.join(cwd, 'app'))) {
|
|
7
|
+
return cwd;
|
|
8
|
+
}
|
|
9
|
+
else if (await directoryExists(path.join(cwd, 'src/app'))) {
|
|
10
|
+
return path.join(cwd, 'src');
|
|
11
|
+
}
|
|
12
|
+
throw new Error(`Could not find app router directory. Check Next.js docs for more info.`);
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import loglevel from 'loglevel';
|
|
2
|
+
export type ProjectInfo = Awaited<ReturnType<typeof getProjectInfo>>;
|
|
3
|
+
export default function getProjectInfo({ port: givenPort, clientOutDir, }?: {
|
|
4
|
+
port?: number;
|
|
5
|
+
clientOutDir?: string;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
cwd: string;
|
|
8
|
+
port: string;
|
|
9
|
+
vovkPort: string;
|
|
10
|
+
apiEntryPoint: string;
|
|
11
|
+
apiPrefix: string;
|
|
12
|
+
apiDir: string;
|
|
13
|
+
srcRoot: string;
|
|
14
|
+
metadataOutFullPath: string;
|
|
15
|
+
metadataOutImportPath: string;
|
|
16
|
+
clientOutFullPath: string;
|
|
17
|
+
fetcherClientImportPath: string;
|
|
18
|
+
config: Required<import("../types.mjs").VovkConfig>;
|
|
19
|
+
log: {
|
|
20
|
+
info: (msg: string) => void;
|
|
21
|
+
warn: (msg: string) => void;
|
|
22
|
+
error: (msg: string) => void;
|
|
23
|
+
debug: (msg: string) => void;
|
|
24
|
+
raw: loglevel.RootLogger;
|
|
25
|
+
};
|
|
26
|
+
}>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import loglevel from 'loglevel';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import getConfig from './getConfig.mjs';
|
|
5
|
+
export default async function getProjectInfo({ port: givenPort, clientOutDir, } = {}) {
|
|
6
|
+
const env = process.env;
|
|
7
|
+
const port = givenPort?.toString() ?? process.env.PORT ?? '3000';
|
|
8
|
+
// Make PORT available to the config file at getConfig
|
|
9
|
+
process.env.PORT = port;
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
const { config, srcRoot } = await getConfig({ clientOutDir });
|
|
12
|
+
const vovkPort = env.VOVK_PORT || (parseInt(port) + 6969).toString();
|
|
13
|
+
const apiEntryPoint = `${config.origin}/${config.rootEntry}`; // ??? TODO
|
|
14
|
+
const apiPrefix = `${config.origin}/${config.rootEntry}`; // ??? TODO
|
|
15
|
+
const apiDir = path.join(srcRoot, 'app', config.rootEntry);
|
|
16
|
+
const metadataOutFullPath = path.join(cwd, config.metadataOutDir);
|
|
17
|
+
const metadataOutImportPath = path.relative(config.clientOutDir, metadataOutFullPath);
|
|
18
|
+
const fetcherClientImportPath = config.fetcher.startsWith('.')
|
|
19
|
+
? path.relative(config.clientOutDir, config.fetcher)
|
|
20
|
+
: config.fetcher;
|
|
21
|
+
const clientOutFullPath = path.join(cwd, config.clientOutDir);
|
|
22
|
+
const log = {
|
|
23
|
+
info: (msg) => loglevel.info(chalk.blueBright(`🐺 ${msg}`)),
|
|
24
|
+
warn: (msg) => loglevel.warn(chalk.yellowBright(`🐺 ${msg}`)),
|
|
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);
|
|
30
|
+
return {
|
|
31
|
+
cwd,
|
|
32
|
+
port,
|
|
33
|
+
vovkPort,
|
|
34
|
+
apiEntryPoint,
|
|
35
|
+
apiPrefix,
|
|
36
|
+
apiDir,
|
|
37
|
+
srcRoot,
|
|
38
|
+
metadataOutFullPath,
|
|
39
|
+
metadataOutImportPath,
|
|
40
|
+
clientOutFullPath,
|
|
41
|
+
fetcherClientImportPath,
|
|
42
|
+
config,
|
|
43
|
+
log,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
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
|
+
}
|
|
19
|
+
async function readConfig() {
|
|
20
|
+
const configPath = await findConfigPath();
|
|
21
|
+
let config = {};
|
|
22
|
+
if (!configPath) {
|
|
23
|
+
return config;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
if (configPath.endsWith('.cjs') || configPath.endsWith('.js')) {
|
|
27
|
+
const cacheBuster = Date.now();
|
|
28
|
+
({ default: config } = (await import(`${configPath}?cache=${cacheBuster}`)));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.error('🐺 ❌ Error reading config file:', e.message);
|
|
34
|
+
}
|
|
35
|
+
return config;
|
|
36
|
+
}
|
|
37
|
+
export default readConfig;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
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 path from 'path';
|
|
10
|
+
import { readFileSync } from 'fs';
|
|
11
|
+
const program = new Command();
|
|
12
|
+
const packageJSON = JSON.parse(readFileSync(path.join(import.meta.dirname, '../package.json'), 'utf-8'));
|
|
13
|
+
program.name('vovk').description('Vovk CLI').version(packageJSON.version);
|
|
14
|
+
program
|
|
15
|
+
.command('dev')
|
|
16
|
+
.description('Start development server (optional flag --next-dev to start Vovk Server with Next.js)')
|
|
17
|
+
.option('--project <path>', 'Path to Next.js project', process.cwd())
|
|
18
|
+
.option('--client-out <path>', 'Path to client output directory')
|
|
19
|
+
.option('--next-dev', 'Start Vovk Server and Next.js with automatic port allocation', false)
|
|
20
|
+
.allowUnknownOption(true)
|
|
21
|
+
.action(async (options, command) => {
|
|
22
|
+
const portAttempts = 30;
|
|
23
|
+
const PORT = !options.nextDev
|
|
24
|
+
? process.env.PORT
|
|
25
|
+
: process.env.PORT ||
|
|
26
|
+
(await getAvailablePort(3000, portAttempts, 0, (failedPort, tryingPort) =>
|
|
27
|
+
// eslint-disable-next-line no-console
|
|
28
|
+
console.warn(`🐺 Next.js Port ${failedPort} is in use, trying ${tryingPort} instead.`)).catch(() => {
|
|
29
|
+
throw new Error(`🐺 ❌ Failed to find available Next port after ${portAttempts} attempts`);
|
|
30
|
+
}));
|
|
31
|
+
if (!PORT) {
|
|
32
|
+
throw new Error('🐺 ❌ PORT env variable is required');
|
|
33
|
+
}
|
|
34
|
+
if (options.nextDev) {
|
|
35
|
+
const { result } = concurrently([
|
|
36
|
+
{
|
|
37
|
+
command: `node ${import.meta.dirname}/server/index.mjs`,
|
|
38
|
+
name: 'Vovk.ts Metadata Server',
|
|
39
|
+
env: Object.assign({ PORT, __VOVK_START_SERVER_IN_STANDALONE_MODE__: 'true' }, options.clientOut ? { VOVK_CLIENT_OUT_DIR: options.clientOut } : {}),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
command: `cd ${options.project} && npx next dev ${command.args.join(' ')}`,
|
|
43
|
+
name: 'Next.js Development Server',
|
|
44
|
+
env: { PORT },
|
|
45
|
+
},
|
|
46
|
+
], {
|
|
47
|
+
killOthers: ['failure', 'success'],
|
|
48
|
+
prefix: 'none',
|
|
49
|
+
});
|
|
50
|
+
try {
|
|
51
|
+
await result;
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
// eslint-disable-next-line no-console
|
|
55
|
+
console.log('🐺 Exiting...');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
void new VovkCLIServer().startServer({ clientOutDir: options.clientOut });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
program
|
|
63
|
+
.command('generate')
|
|
64
|
+
.description('Generate client')
|
|
65
|
+
.option('--client-out <path>', 'Path to output directory')
|
|
66
|
+
.action(async (options) => {
|
|
67
|
+
const projectInfo = await getProjectInfo({ clientOutDir: options.clientOut });
|
|
68
|
+
const segments = await locateSegments(projectInfo.apiDir);
|
|
69
|
+
const metadata = (await import(path.join(projectInfo.metadataOutFullPath, 'index.js')));
|
|
70
|
+
await generateClient(projectInfo, segments, metadata.default);
|
|
71
|
+
});
|
|
72
|
+
program
|
|
73
|
+
.command('help')
|
|
74
|
+
.description('Show help message')
|
|
75
|
+
.action(() => program.help());
|
|
76
|
+
program.parse(process.argv);
|
|
77
|
+
if (!process.argv.slice(2).length) {
|
|
78
|
+
program.outputHelp();
|
|
79
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
2
|
/*
|
|
4
3
|
npx vovk-cli init
|
|
5
4
|
- Check if the project is already initialized
|
|
@@ -57,12 +56,11 @@ npx vovk-cli init
|
|
|
57
56
|
- Show how to make a request to the example route
|
|
58
57
|
- Show success message
|
|
59
58
|
*/
|
|
60
|
-
|
|
61
|
-
const prompts_1 = require("@inquirer/prompts");
|
|
59
|
+
import { confirm } from '@inquirer/prompts';
|
|
62
60
|
// Or
|
|
63
61
|
// import confirm from '@inquirer/confirm';
|
|
64
62
|
// eslint-disable-next-line no-console
|
|
65
|
-
void
|
|
63
|
+
void confirm({ message: 'Continue?' }).then(console.info);
|
|
66
64
|
/*
|
|
67
65
|
const wizard = [
|
|
68
66
|
{
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import fileExists from './utils/fileExists.mjs';
|
|
4
|
+
export default async function locateSegments(dir, rootDir = dir) {
|
|
5
|
+
let results = [];
|
|
6
|
+
// Read the contents of the directory
|
|
7
|
+
const list = await fs.readdir(dir);
|
|
8
|
+
// Iterate through each item in the directory
|
|
9
|
+
for (const file of list) {
|
|
10
|
+
const filePath = path.join(dir, file);
|
|
11
|
+
const stat = await fs.stat(filePath);
|
|
12
|
+
if (stat.isDirectory()) {
|
|
13
|
+
// Check if the directory name matches the pattern [[...something]]
|
|
14
|
+
if (file.startsWith('[[...') && file.endsWith(']]')) {
|
|
15
|
+
// Check if there's a route.ts file inside this directory
|
|
16
|
+
const routeFilePath = path.join(filePath, 'route.ts');
|
|
17
|
+
if (await fileExists(routeFilePath)) {
|
|
18
|
+
// Calculate the basePath relative to the root directory
|
|
19
|
+
const segmentName = path.relative(rootDir, dir);
|
|
20
|
+
results.push({ routeFilePath, segmentName });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// Recursively search inside subdirectories
|
|
24
|
+
const subDirResults = await locateSegments(filePath, rootDir);
|
|
25
|
+
results = results.concat(subDirResults);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return results;
|
|
29
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Checks if a file exists at the given path.
|
|
5
|
+
* @param {string} filePath - The path to the file.
|
|
6
|
+
* @returns {Promise<boolean>} - A promise that resolves to true if the file exists, false otherwise.
|
|
7
|
+
*/
|
|
8
|
+
const fileExists = async (filePath) => !!(await fs.stat(filePath).catch(() => false));
|
|
9
|
+
async function postinstall() {
|
|
10
|
+
const vovk = path.join(import.meta.dirname, '../../.vovk');
|
|
11
|
+
const js = path.join(vovk, 'client.js');
|
|
12
|
+
const ts = path.join(vovk, 'client.d.ts');
|
|
13
|
+
const index = path.join(vovk, 'index.ts');
|
|
14
|
+
if ((await fileExists(js)) || (await fileExists(ts)) || (await fileExists(index))) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
await fs.mkdir(vovk, { recursive: true });
|
|
18
|
+
await fs.writeFile(js, '/* postinstall */');
|
|
19
|
+
await fs.writeFile(ts, '/* postinstall */');
|
|
20
|
+
await fs.writeFile(index, '/* postinstall */');
|
|
21
|
+
}
|
|
22
|
+
void postinstall();
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return (
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.default = createMetadataServer;
|
|
7
|
-
const http_1 = __importDefault(require("http"));
|
|
8
|
-
function createMetadataServer(then, catchFn) {
|
|
9
|
-
return http_1.default.createServer((req, res) => {
|
|
1
|
+
import http from 'http';
|
|
2
|
+
export default function createMetadataServer(then, catchFn) {
|
|
3
|
+
return http.createServer((req, res) => {
|
|
10
4
|
if (req.method === 'POST' && req.url === '/__metadata') {
|
|
11
5
|
let body = '';
|
|
12
6
|
req.on('data', (chunk) => {
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.diffHandlers = diffHandlers;
|
|
4
|
-
exports.diffWorkersOrControllers = diffWorkersOrControllers;
|
|
5
|
-
exports.default = diffMetadata;
|
|
6
|
-
const lodash_1 = require("lodash");
|
|
7
|
-
function diffHandlers(oldHandlers, newHandlers, nameOfClass) {
|
|
1
|
+
import isEqual from 'lodash/isEqual.js';
|
|
2
|
+
export function diffHandlers(oldHandlers, newHandlers, nameOfClass) {
|
|
8
3
|
const added = [];
|
|
9
4
|
const removed = [];
|
|
10
5
|
const changed = []; // Array to store changed handlers
|
|
@@ -12,7 +7,7 @@ function diffHandlers(oldHandlers, newHandlers, nameOfClass) {
|
|
|
12
7
|
if (!(handler in oldHandlers)) {
|
|
13
8
|
added.push(handler);
|
|
14
9
|
}
|
|
15
|
-
else if (!
|
|
10
|
+
else if (!isEqual(newHandler, oldHandlers[handler])) {
|
|
16
11
|
changed.push(handler); // Add to changed if handlers are not shallow equal
|
|
17
12
|
}
|
|
18
13
|
}
|
|
@@ -23,7 +18,7 @@ function diffHandlers(oldHandlers, newHandlers, nameOfClass) {
|
|
|
23
18
|
}
|
|
24
19
|
return { nameOfClass, added, removed, changed };
|
|
25
20
|
}
|
|
26
|
-
function diffWorkersOrControllers(oldItems, newItems) {
|
|
21
|
+
export function diffWorkersOrControllers(oldItems, newItems) {
|
|
27
22
|
const added = [];
|
|
28
23
|
const removed = [];
|
|
29
24
|
const handlersDiff = [];
|
|
@@ -67,7 +62,7 @@ example output:
|
|
|
67
62
|
}
|
|
68
63
|
}
|
|
69
64
|
*/
|
|
70
|
-
function diffMetadata(oldJson, newJson) {
|
|
65
|
+
export default function diffMetadata(oldJson, newJson) {
|
|
71
66
|
const workersDiff = diffWorkersOrControllers(oldJson.workers ?? {}, newJson.workers ?? {});
|
|
72
67
|
const controllersDiff = diffWorkersOrControllers(oldJson.controllers ?? {}, newJson.controllers ?? {});
|
|
73
68
|
return {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { ProjectInfo } from '../getProjectInfo';
|
|
1
|
+
import { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
2
2
|
export default function ensureMetadataFiles(metadataOutFullPath: string, segmentNames: string[], projectInfo: ProjectInfo | null): Promise<void>;
|
|
3
3
|
export declare const debouncedEnsureMetadataFiles: import("lodash").DebouncedFunc<typeof ensureMetadataFiles>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import debounce from 'lodash/debounce.js';
|
|
4
|
+
import writeOneMetadataFile, { ROOT_SEGMENT_SCHEMA_NAME } from './writeOneMetadataFile.mjs';
|
|
5
|
+
export default async function ensureMetadataFiles(metadataOutFullPath, segmentNames, projectInfo) {
|
|
6
|
+
const now = Date.now();
|
|
7
|
+
let hasChanged = false;
|
|
8
|
+
// Create index.js file
|
|
9
|
+
const indexContent = segmentNames
|
|
10
|
+
.map((segmentName) => {
|
|
11
|
+
return `module.exports['${segmentName}'] = require('./${segmentName || ROOT_SEGMENT_SCHEMA_NAME}.json');`;
|
|
12
|
+
})
|
|
13
|
+
.join('\n');
|
|
14
|
+
const dTsContent = `import type { VovkMetadata } from 'vovk';
|
|
15
|
+
declare const segmentMetadata: Record<string, VovkMetadata>;
|
|
16
|
+
export default segmentMetadata;`;
|
|
17
|
+
await fs.mkdir(metadataOutFullPath, { recursive: true });
|
|
18
|
+
await fs.writeFile(path.join(metadataOutFullPath, 'index.js'), indexContent);
|
|
19
|
+
await fs.writeFile(path.join(metadataOutFullPath, 'index.d.ts'), dTsContent);
|
|
20
|
+
// Create JSON files (if not exist) with name [segmentName].json (where segmentName can include /, which means the folder structure can be nested) : {} (empty object)
|
|
21
|
+
await Promise.all(segmentNames.map(async (segmentName) => {
|
|
22
|
+
const { isCreated } = await writeOneMetadataFile({
|
|
23
|
+
metadataOutFullPath,
|
|
24
|
+
metadata: {
|
|
25
|
+
emitMetadata: false,
|
|
26
|
+
segmentName,
|
|
27
|
+
controllers: {},
|
|
28
|
+
workers: {},
|
|
29
|
+
},
|
|
30
|
+
skipIfExists: true,
|
|
31
|
+
});
|
|
32
|
+
if (isCreated) {
|
|
33
|
+
projectInfo?.log.debug(`Created empty metadata file for segment "${segmentName}"`);
|
|
34
|
+
hasChanged = true;
|
|
35
|
+
}
|
|
36
|
+
}));
|
|
37
|
+
// Recursive function to delete unnecessary JSON files and folders
|
|
38
|
+
async function deleteUnnecessaryJsonFiles(dirPath) {
|
|
39
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
40
|
+
await Promise.all(entries.map(async (entry) => {
|
|
41
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
42
|
+
if (entry.isDirectory()) {
|
|
43
|
+
// Recursively delete unnecessary files and folders within nested directories
|
|
44
|
+
await deleteUnnecessaryJsonFiles(fullPath);
|
|
45
|
+
// Check if the directory is empty after deletion and remove it if so
|
|
46
|
+
const remainingEntries = await fs.readdir(fullPath);
|
|
47
|
+
if (remainingEntries.length === 0) {
|
|
48
|
+
await fs.rmdir(fullPath);
|
|
49
|
+
projectInfo?.log.debug(`Deleted unnecessary metadata directory "${fullPath}"`);
|
|
50
|
+
hasChanged = true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (entry.isFile() && entry.name.endsWith('.json')) {
|
|
54
|
+
const relativePath = path.relative(metadataOutFullPath, fullPath);
|
|
55
|
+
const segmentName = relativePath.replace(/\\/g, '/').slice(0, -5); // Remove '.json' extension
|
|
56
|
+
if (!segmentNames.includes(segmentName) &&
|
|
57
|
+
!segmentNames.includes(segmentName.replace(ROOT_SEGMENT_SCHEMA_NAME, ''))) {
|
|
58
|
+
await fs.unlink(fullPath);
|
|
59
|
+
projectInfo?.log.debug(`Deleted unnecessary metadata file for segment "${segmentName}"`);
|
|
60
|
+
hasChanged = true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
// Start the recursive deletion from the root directory
|
|
66
|
+
await deleteUnnecessaryJsonFiles(metadataOutFullPath);
|
|
67
|
+
if (hasChanged)
|
|
68
|
+
projectInfo?.log.info(`Metadata files updated in ${Date.now() - now}ms`);
|
|
69
|
+
}
|
|
70
|
+
export const debouncedEnsureMetadataFiles = debounce(ensureMetadataFiles, 1000);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ProjectInfo } from '../getProjectInfo';
|
|
2
|
-
import type { Segment } from '../locateSegments';
|
|
1
|
+
import type { ProjectInfo } from '../getProjectInfo/index.mjs';
|
|
2
|
+
import type { Segment } from '../locateSegments.mjs';
|
|
3
3
|
import { VovkMetadata } from 'vovk';
|
|
4
4
|
export default function generateClient(projectInfo: ProjectInfo, segments: Segment[], segmentsMetadata: Record<string, VovkMetadata>): Promise<{
|
|
5
5
|
written: boolean;
|
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.default = generateClient;
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
-
async function generateClient(projectInfo, segments, segmentsMetadata) {
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
export default async function generateClient(projectInfo, segments, segmentsMetadata) {
|
|
10
4
|
const now = Date.now();
|
|
11
5
|
const outDir = projectInfo.clientOutFullPath;
|
|
12
6
|
const validatePath = projectInfo.config.validateOnClient?.startsWith('.')
|
|
13
|
-
?
|
|
7
|
+
? path.join(projectInfo.cwd, projectInfo.config.validateOnClient)
|
|
14
8
|
: projectInfo.config.validateOnClient;
|
|
15
9
|
let dts = `// auto-generated
|
|
16
10
|
/* eslint-disable */
|
|
@@ -44,7 +38,7 @@ import metadata from '${projectInfo.metadataOutImportPath}';
|
|
|
44
38
|
}
|
|
45
39
|
if (!metadata.emitMetadata)
|
|
46
40
|
continue;
|
|
47
|
-
const importRouteFilePath =
|
|
41
|
+
const importRouteFilePath = path.relative(projectInfo.config.clientOutDir, routeFilePath);
|
|
48
42
|
dts += `import type { Controllers as Controllers${i}, Workers as Workers${i} } from "${importRouteFilePath}";\n`;
|
|
49
43
|
ts += `import type { Controllers as Controllers${i}, Workers as Workers${i} } from "${importRouteFilePath}";\n`;
|
|
50
44
|
}
|
|
@@ -79,20 +73,20 @@ const prefix = '${projectInfo.apiPrefix}';
|
|
|
79
73
|
ts += `export const ${key} = promisifyWorker<Workers${i}["${key}"]>(null, metadata['${segmentName}'].workers.${key});\n`;
|
|
80
74
|
}
|
|
81
75
|
}
|
|
82
|
-
const localJsPath =
|
|
83
|
-
const localDtsPath =
|
|
84
|
-
const localTsPath =
|
|
85
|
-
const existingJs = await
|
|
86
|
-
const existingDts = await
|
|
87
|
-
const existingTs = await
|
|
76
|
+
const localJsPath = path.join(outDir, 'client.js');
|
|
77
|
+
const localDtsPath = path.join(outDir, 'client.d.ts');
|
|
78
|
+
const localTsPath = path.join(outDir, 'index.ts');
|
|
79
|
+
const existingJs = await fs.readFile(localJsPath, 'utf-8').catch(() => '');
|
|
80
|
+
const existingDts = await fs.readFile(localDtsPath, 'utf-8').catch(() => '');
|
|
81
|
+
const existingTs = await fs.readFile(localTsPath, 'utf-8').catch(() => '');
|
|
88
82
|
if (existingJs === js && existingDts === dts && existingTs === ts) {
|
|
89
83
|
projectInfo.log.info(`Client is up to date and doesn't need to be regenerated (${Date.now() - now}ms).`);
|
|
90
84
|
return { written: false, path: outDir };
|
|
91
85
|
}
|
|
92
|
-
await
|
|
93
|
-
await
|
|
94
|
-
await
|
|
95
|
-
await
|
|
86
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
87
|
+
await fs.writeFile(localJsPath, js);
|
|
88
|
+
await fs.writeFile(localDtsPath, dts);
|
|
89
|
+
await fs.writeFile(localTsPath, ts);
|
|
96
90
|
projectInfo.log.info(`Client generated in ${Date.now() - now}ms`);
|
|
97
91
|
return { written: true, path: outDir };
|
|
98
92
|
}
|