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.
Files changed (111) hide show
  1. package/LICENSE +23 -0
  2. package/dist/generateClient.d.mts +7 -0
  3. package/dist/generateClient.mjs +97 -0
  4. package/dist/getProjectInfo/getConfig.d.mts +1 -1
  5. package/dist/getProjectInfo/getConfig.mjs +16 -11
  6. package/dist/getProjectInfo/getConfigPath.d.mts +1 -0
  7. package/dist/getProjectInfo/getConfigPath.mjs +18 -0
  8. package/dist/getProjectInfo/getRelativeSrcRoot.d.mts +1 -0
  9. package/dist/getProjectInfo/{getSrcRoot.mjs → getRelativeSrcRoot.mjs} +3 -3
  10. package/dist/getProjectInfo/index.d.mts +4 -8
  11. package/dist/getProjectInfo/index.mjs +9 -22
  12. package/dist/getProjectInfo/readConfig.mjs +4 -23
  13. package/dist/index.d.mts +9 -1
  14. package/dist/index.mjs +39 -17
  15. package/dist/init/index.d.mts +20 -0
  16. package/dist/init/index.mjs +244 -0
  17. package/dist/init/installDependencies.d.mts +1 -0
  18. package/dist/init/installDependencies.mjs +28 -0
  19. package/dist/new/addClassToSegmentCode.d.mts +6 -0
  20. package/dist/new/addClassToSegmentCode.mjs +32 -0
  21. package/dist/new/addCommonTerms.d.mts +1 -0
  22. package/dist/new/addCommonTerms.mjs +91 -0
  23. package/dist/new/index.d.mts +2 -0
  24. package/dist/new/index.mjs +24 -0
  25. package/dist/new/newModule.d.mts +5 -0
  26. package/dist/new/newModule.mjs +89 -0
  27. package/dist/new/newSegment.d.mts +4 -0
  28. package/dist/new/newSegment.mjs +33 -0
  29. package/dist/new/render.d.mts +12 -0
  30. package/dist/new/render.mjs +28 -0
  31. package/dist/types.d.mts +10 -4
  32. package/dist/utils/chalkHighlightThing.d.mts +1 -0
  33. package/dist/utils/chalkHighlightThing.mjs +4 -0
  34. package/dist/utils/formatLoggedSegmentName.d.mts +4 -0
  35. package/dist/utils/formatLoggedSegmentName.mjs +7 -0
  36. package/dist/utils/getLogger.d.mts +8 -0
  37. package/dist/utils/getLogger.mjs +13 -0
  38. package/dist/utils/prettify.d.mts +1 -0
  39. package/dist/utils/prettify.mjs +10 -0
  40. package/dist/{server/diffMetadata.d.mts → watcher/diffSchema.d.mts} +5 -5
  41. package/dist/{server/diffMetadata.mjs → watcher/diffSchema.mjs} +1 -1
  42. package/dist/watcher/ensureSchemaFiles.d.mts +3 -0
  43. package/dist/{server/ensureMetadataFiles.mjs → watcher/ensureSchemaFiles.mjs} +26 -25
  44. package/dist/watcher/index.d.mts +6 -0
  45. package/dist/{server → watcher}/index.mjs +109 -96
  46. package/dist/watcher/isMetadataEmpty.d.mts +2 -0
  47. package/dist/watcher/isMetadataEmpty.mjs +4 -0
  48. package/dist/{server → watcher}/logDiffResult.d.mts +1 -1
  49. package/dist/{server → watcher}/logDiffResult.mjs +11 -10
  50. package/dist/watcher/writeOneSchemaFile.d.mts +11 -0
  51. package/dist/{server/writeOneMetadataFile.mjs → watcher/writeOneSchemaFile.mjs} +7 -7
  52. package/package.json +22 -6
  53. package/templates/MyThingController.c.only.template.ts +32 -0
  54. package/templates/MyThingController.c.template.ts +34 -0
  55. package/templates/MyThingService.s.template.ts +18 -0
  56. package/templates/controller.ejs +85 -0
  57. package/templates/service.ejs +9 -0
  58. package/templates/worker.ejs +9 -0
  59. package/templates/zod/MyThingController.c.only.template.ts +32 -0
  60. package/templates/zod/MyThingController.c.template.ts +39 -0
  61. package/templates/zod/MyThingService.s.template.ts +18 -0
  62. package/.eslintrc.mjs +0 -20
  63. package/dist/getProjectInfo/getCwdPath.d.mts +0 -1
  64. package/dist/getProjectInfo/getCwdPath.mjs +0 -13
  65. package/dist/getProjectInfo/getSrcRoot.d.mts +0 -1
  66. package/dist/init.d.mts +0 -2
  67. package/dist/init.mjs +0 -171
  68. package/dist/server/createMetadataServer.d.mts +0 -5
  69. package/dist/server/createMetadataServer.mjs +0 -25
  70. package/dist/server/ensureMetadataFiles.d.mts +0 -3
  71. package/dist/server/generateClient.d.mts +0 -7
  72. package/dist/server/generateClient.mjs +0 -92
  73. package/dist/server/index.d.mts +0 -6
  74. package/dist/server/isMetadataEmpty.d.mts +0 -2
  75. package/dist/server/isMetadataEmpty.mjs +0 -4
  76. package/dist/server/writeOneMetadataFile.d.mts +0 -11
  77. package/src/getProjectInfo/directoryExists.mts +0 -10
  78. package/src/getProjectInfo/getConfig.mts +0 -29
  79. package/src/getProjectInfo/getCwdPath.mts +0 -15
  80. package/src/getProjectInfo/getSrcRoot.mts +0 -14
  81. package/src/getProjectInfo/index.mts +0 -59
  82. package/src/getProjectInfo/readConfig.mts +0 -44
  83. package/src/index.mts +0 -113
  84. package/src/init.mts +0 -174
  85. package/src/locateSegments.mts +0 -40
  86. package/src/postinstall.mts +0 -27
  87. package/src/server/createMetadataServer.mts +0 -30
  88. package/src/server/diffMetadata.mts +0 -110
  89. package/src/server/ensureMetadataFiles.mts +0 -92
  90. package/src/server/generateClient.mts +0 -108
  91. package/src/server/index.mts +0 -307
  92. package/src/server/isMetadataEmpty.mts +0 -6
  93. package/src/server/logDiffResult.mts +0 -114
  94. package/src/server/writeOneMetadataFile.mts +0 -44
  95. package/src/types.mts +0 -58
  96. package/src/utils/debounceWithArgs.mts +0 -22
  97. package/src/utils/fileExists.mts +0 -10
  98. package/src/utils/getAvailablePort.mts +0 -50
  99. package/test/data/segments/[[...vovk]]/route.ts +0 -0
  100. package/test/data/segments/bar/[[...custom]]/route.ts +0 -0
  101. package/test/data/segments/baz/[[...vovk]]/noroute.ts +0 -0
  102. package/test/data/segments/foo/[[...vovk]]/route.ts +0 -0
  103. package/test/data/segments/garply/waldo/route.ts +0 -0
  104. package/test/data/segments/grault/xxxx/[[...vovk]]/noroute.ts +0 -0
  105. package/test/data/segments/quux/corge/[[...vovk]]/route.ts +0 -0
  106. package/test/index.ts +0 -3
  107. package/test/metadata-diff.test.mts +0 -300
  108. package/test/metadata-write.test.mts +0 -82
  109. package/test/utils.test.mts +0 -49
  110. package/tsconfig.json +0 -17
  111. 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
+ }
@@ -2,6 +2,6 @@ import type { VovkConfig } from '../types.mjs';
2
2
  export default function getConfig({ clientOutDir }: {
3
3
  clientOutDir?: string;
4
4
  }): Promise<{
5
- config: Required<VovkConfig>;
5
+ config: Required<Omit<VovkConfig, "_devForceAppDir">>;
6
6
  srcRoot: string;
7
7
  }>;
@@ -1,23 +1,28 @@
1
1
  import readConfig from './readConfig.mjs';
2
- import getCwdPath from './getCwdPath.mjs';
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 getSrcRoot();
9
- const cwd = process.cwd();
6
+ const srcRoot = await getRelativeSrcRoot();
10
7
  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',
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 getSrcRoot() {
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 cwd;
7
+ return '.';
8
8
  }
9
9
  else if (await directoryExists(path.join(cwd, 'src/app'))) {
10
- return path.join(cwd, 'src');
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
- metadataOutFullPath: string;
15
- metadataOutImportPath: string;
16
- clientOutFullPath: string;
11
+ schemaOutImportPath: string;
17
12
  fetcherClientImportPath: string;
18
- config: Required<import("../types.mjs").VovkConfig>;
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 vovkPort = env.VOVK_PORT || (parseInt(port) + 6969).toString();
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 metadataOutFullPath = path.join(cwd, config.metadataOutDir);
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 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);
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
- metadataOutFullPath,
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 { 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
- }
1
+ import getConfigPath from './getConfigPath.mjs';
19
2
  async function readConfig() {
20
- const configPath = await findConfigPath();
3
+ const configPath = await getConfigPath();
21
4
  let config = {};
22
5
  if (!configPath) {
23
6
  return config;
24
7
  }
25
8
  try {
26
- if (configPath.endsWith('.cjs') || configPath.endsWith('.js')) {
27
- const cacheBuster = Date.now();
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 { VovkConfig, VovkEnv } from './types.mjs';
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 './server/generateClient.mjs';
8
+ import generateClient from './generateClient.mjs';
8
9
  import locateSegments from './locateSegments.mjs';
9
- import path from 'path';
10
- import { readFileSync } from 'fs';
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 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
+ .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 Vovk Server and Next.js with automatic port allocation', false)
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}/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 } : {}),
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: `cd ${options.project} && npx next dev ${command.args.join(' ')}`,
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
- // eslint-disable-next-line no-console
55
- console.log('🐺 Exiting...');
55
+ // do nothing, all processes are killed
56
56
  }
57
57
  }
58
58
  else {
59
- void new VovkCLIServer().startServer({ clientOutDir: options.clientOut });
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 segments = await locateSegments(projectInfo.apiDir);
69
- const metadata = (await import(path.join(projectInfo.metadataOutFullPath, 'index.js')));
70
- await generateClient(projectInfo, segments, metadata.default);
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 {};