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.
Files changed (89) hide show
  1. package/{.eslintrc.js → .eslintrc.mjs} +1 -1
  2. package/dist/getProjectInfo/directoryExists.mjs +10 -0
  3. package/dist/getProjectInfo/{getConfig.d.ts → getConfig.d.mts} +1 -1
  4. package/dist/getProjectInfo/getConfig.mjs +23 -0
  5. package/dist/getProjectInfo/getCwdPath.mjs +13 -0
  6. package/dist/getProjectInfo/getSrcRoot.mjs +13 -0
  7. package/dist/getProjectInfo/index.d.mts +26 -0
  8. package/dist/getProjectInfo/index.mjs +45 -0
  9. package/dist/getProjectInfo/{readConfig.d.ts → readConfig.d.mts} +1 -1
  10. package/dist/getProjectInfo/readConfig.mjs +37 -0
  11. package/dist/{index.d.ts → index.d.mts} +1 -1
  12. package/dist/index.mjs +79 -0
  13. package/dist/{init.js → init.mjs} +2 -4
  14. package/dist/locateSegments.mjs +29 -0
  15. package/dist/postinstall.mjs +22 -0
  16. package/dist/server/{createMetadataServer.js → createMetadataServer.mjs} +3 -9
  17. package/dist/server/{diffMetadata.js → diffMetadata.mjs} +5 -10
  18. package/dist/server/{ensureMetadataFiles.d.ts → ensureMetadataFiles.d.mts} +1 -1
  19. package/dist/server/ensureMetadataFiles.mjs +70 -0
  20. package/dist/server/{generateClient.d.ts → generateClient.d.mts} +2 -2
  21. package/dist/server/{generateClient.js → generateClient.mjs} +15 -21
  22. package/dist/server/{index.js → index.mjs} +32 -61
  23. package/dist/server/isMetadataEmpty.mjs +4 -0
  24. package/dist/server/{logDiffResult.d.ts → logDiffResult.d.mts} +2 -2
  25. package/dist/server/{logDiffResult.js → logDiffResult.mjs} +11 -17
  26. package/dist/server/{writeOneMetadataFile.d.ts → writeOneMetadataFile.d.mts} +1 -1
  27. package/dist/server/writeOneMetadataFile.mjs +27 -0
  28. package/dist/types.mjs +1 -0
  29. package/dist/utils/{debounceWithArgs.d.ts → debounceWithArgs.d.mts} +1 -1
  30. package/dist/utils/debounceWithArgs.mjs +14 -0
  31. package/dist/utils/fileExists.mjs +10 -0
  32. package/dist/utils/{getAvailablePort.js → getAvailablePort.mjs} +3 -8
  33. package/package.json +8 -8
  34. package/src/getProjectInfo/{getConfig.ts → getConfig.mts} +4 -4
  35. package/src/getProjectInfo/{getSrcRoot.ts → getSrcRoot.mts} +1 -1
  36. package/src/getProjectInfo/{index.ts → index.mts} +3 -7
  37. package/src/getProjectInfo/{readConfig.ts → readConfig.mts} +2 -8
  38. package/src/{index.ts → index.mts} +18 -17
  39. package/src/{locateSegments.ts → locateSegments.mts} +1 -1
  40. package/src/{postinstall.ts → postinstall.mts} +1 -1
  41. package/src/server/{diffMetadata.ts → diffMetadata.mts} +2 -2
  42. package/src/server/{ensureMetadataFiles.ts → ensureMetadataFiles.mts} +3 -3
  43. package/src/server/{generateClient.ts → generateClient.mts} +2 -2
  44. package/src/server/{index.ts → index.mts} +11 -10
  45. package/src/server/{isMetadataEmpty.ts → isMetadataEmpty.mts} +1 -1
  46. package/src/server/{logDiffResult.ts → logDiffResult.mts} +2 -2
  47. package/src/server/{writeOneMetadataFile.ts → writeOneMetadataFile.mts} +1 -1
  48. package/src/utils/{debounceWithArgs.ts → debounceWithArgs.mts} +2 -2
  49. package/test/{metadata-diff.test.ts → metadata-diff.test.mts} +1 -1
  50. package/test/{metadata-write.test.ts → metadata-write.test.mts} +1 -1
  51. package/test/{utils.test.ts → utils.test.mts} +1 -1
  52. package/tsconfig.json +6 -0
  53. package/tsconfig.test.json +1 -1
  54. package/dist/getProjectInfo/directoryExists.js +0 -16
  55. package/dist/getProjectInfo/getConfig.js +0 -29
  56. package/dist/getProjectInfo/getCwdPath.js +0 -19
  57. package/dist/getProjectInfo/getSrcRoot.js +0 -19
  58. package/dist/getProjectInfo/index.d.ts +0 -48
  59. package/dist/getProjectInfo/index.js +0 -78
  60. package/dist/getProjectInfo/readConfig.js +0 -73
  61. package/dist/index.js +0 -104
  62. package/dist/locateSegments.js +0 -58
  63. package/dist/postinstall.js +0 -27
  64. package/dist/server/ensureMetadataFiles.js +0 -100
  65. package/dist/server/isMetadataEmpty.js +0 -7
  66. package/dist/server/writeOneMetadataFile.js +0 -34
  67. package/dist/types.js +0 -2
  68. package/dist/utils/debounceWithArgs.js +0 -20
  69. package/dist/utils/fileExists.js +0 -16
  70. /package/dist/getProjectInfo/{directoryExists.d.ts → directoryExists.d.mts} +0 -0
  71. /package/dist/getProjectInfo/{getCwdPath.d.ts → getCwdPath.d.mts} +0 -0
  72. /package/dist/getProjectInfo/{getSrcRoot.d.ts → getSrcRoot.d.mts} +0 -0
  73. /package/dist/{init.d.ts → init.d.mts} +0 -0
  74. /package/dist/{locateSegments.d.ts → locateSegments.d.mts} +0 -0
  75. /package/dist/{postinstall.d.ts → postinstall.d.mts} +0 -0
  76. /package/dist/server/{createMetadataServer.d.ts → createMetadataServer.d.mts} +0 -0
  77. /package/dist/server/{diffMetadata.d.ts → diffMetadata.d.mts} +0 -0
  78. /package/dist/server/{index.d.ts → index.d.mts} +0 -0
  79. /package/dist/server/{isMetadataEmpty.d.ts → isMetadataEmpty.d.mts} +0 -0
  80. /package/dist/{types.d.ts → types.d.mts} +0 -0
  81. /package/dist/utils/{fileExists.d.ts → fileExists.d.mts} +0 -0
  82. /package/dist/utils/{getAvailablePort.d.ts → getAvailablePort.d.mts} +0 -0
  83. /package/src/getProjectInfo/{directoryExists.ts → directoryExists.mts} +0 -0
  84. /package/src/getProjectInfo/{getCwdPath.ts → getCwdPath.mts} +0 -0
  85. /package/src/{init.ts → init.mts} +0 -0
  86. /package/src/server/{createMetadataServer.ts → createMetadataServer.mts} +0 -0
  87. /package/src/{types.ts → types.mts} +0 -0
  88. /package/src/utils/{fileExists.ts → fileExists.mts} +0 -0
  89. /package/src/utils/{getAvailablePort.ts → getAvailablePort.mts} +0 -0
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  extends: '../../.eslintrc.js',
3
3
  rules: {
4
4
  '@typescript-eslint/no-unsafe-argument': 'off',
@@ -0,0 +1,10 @@
1
+ import fs from 'fs/promises';
2
+ export default async function directoryExists(dir) {
3
+ try {
4
+ const stats = await fs.stat(dir);
5
+ return stats.isDirectory();
6
+ }
7
+ catch (error) {
8
+ return false;
9
+ }
10
+ }
@@ -1,4 +1,4 @@
1
- import type { VovkConfig } from '../types';
1
+ import type { VovkConfig } from '../types.mjs';
2
2
  export default function getConfig({ clientOutDir }: {
3
3
  clientOutDir?: string;
4
4
  }): Promise<{
@@ -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
+ }
@@ -1,3 +1,3 @@
1
- import type { VovkConfig } from '../types';
1
+ import type { VovkConfig } from '../types.mjs';
2
2
  declare function readConfig(): Promise<VovkConfig>;
3
3
  export default readConfig;
@@ -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;
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { VovkConfig, VovkEnv } from './types';
2
+ import { VovkConfig, VovkEnv } from './types.mjs';
3
3
  export type { VovkConfig, VovkEnv };
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
- Object.defineProperty(exports, "__esModule", { value: true });
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 (0, prompts_1.confirm)({ message: 'Continue?' }).then(console.info);
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
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
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
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
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 (!(0, lodash_1.isEqual)(newHandler, oldHandlers[handler])) {
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
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
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
- ? path_1.default.join(projectInfo.cwd, projectInfo.config.validateOnClient)
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 = path_1.default.relative(projectInfo.config.clientOutDir, routeFilePath);
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 = path_1.default.join(outDir, 'client.js');
83
- const localDtsPath = path_1.default.join(outDir, 'client.d.ts');
84
- const localTsPath = path_1.default.join(outDir, 'index.ts');
85
- const existingJs = await promises_1.default.readFile(localJsPath, 'utf-8').catch(() => '');
86
- const existingDts = await promises_1.default.readFile(localDtsPath, 'utf-8').catch(() => '');
87
- const existingTs = await promises_1.default.readFile(localTsPath, 'utf-8').catch(() => '');
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 promises_1.default.mkdir(outDir, { recursive: true });
93
- await promises_1.default.writeFile(localJsPath, js);
94
- await promises_1.default.writeFile(localDtsPath, dts);
95
- await promises_1.default.writeFile(localTsPath, ts);
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
  }