vovk-cli 0.0.1-beta.15 → 0.0.1-beta.17

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.
@@ -0,0 +1 @@
1
+ export default function addCommonTerms(): void;
@@ -0,0 +1,90 @@
1
+ import pluralize from 'pluralize';
2
+ const terms = [
3
+ ['entity', 'entities'],
4
+ ['regex', 'regexes'],
5
+ ['index', 'indices'],
6
+ ['matrix', 'matrices'],
7
+ ['axis', 'axes'],
8
+ ['schema', 'schemas'],
9
+ ['criterion', 'criteria'],
10
+ ['datum', 'data'],
11
+ ['formula', 'formulas'],
12
+ ['appendix', 'appendices'],
13
+ ['vertex', 'vertices'],
14
+ ['leaf', 'leaves'],
15
+ ['analysis', 'analyses'],
16
+ ['thesis', 'theses'],
17
+ ['hypothesis', 'hypotheses'],
18
+ ['phenomenon', 'phenomena'],
19
+ ['alumnus', 'alumni'],
20
+ ['focus', 'foci'],
21
+ ['medium', 'media'],
22
+ ['bacterium', 'bacteria'],
23
+ ['radius', 'radii'],
24
+ ['genus', 'genera'],
25
+ ['corpus', 'corpora'],
26
+ ['locus', 'loci'],
27
+ ['nucleus', 'nuclei'],
28
+ ['fungus', 'fungi'],
29
+ ['syllabus', 'syllabi'],
30
+ ['cactus', 'cacti'],
31
+ ['automaton', 'automata'],
32
+ ['stimulus', 'stimuli'],
33
+ ['parenthesis', 'parentheses'],
34
+ ['ellipsis', 'ellipses'],
35
+ ['diagnosis', 'diagnoses'],
36
+ ['oasis', 'oases'],
37
+ ['crisis', 'crises'],
38
+ ['antenna', 'antennae'],
39
+ ['chateau', 'chateaux'],
40
+ ['tableau', 'tableaux'],
41
+ ['penny', 'pence'],
42
+ ['memorandum', 'memoranda'],
43
+ ['erratum', 'errata'],
44
+ ['curriculum', 'curricula'],
45
+ ['millennium', 'millennia'],
46
+ ['stratum', 'strata'],
47
+ ['spectrum', 'spectra'],
48
+ ['apex', 'apices'],
49
+ ['vortex', 'vortices'],
50
+ ['calf', 'calves'],
51
+ ['loaf', 'loaves'],
52
+ ['foot', 'feet'],
53
+ ['tooth', 'teeth'],
54
+ ['mouse', 'mice'],
55
+ ['goose', 'geese'],
56
+ ['child', 'children'],
57
+ ['person', 'people'],
58
+ ['man', 'men'],
59
+ ['woman', 'women'],
60
+ ['ox', 'oxen'],
61
+ ['die', 'dice'],
62
+ ['brother', 'brethren'],
63
+ ['stimulus', 'stimuli'],
64
+ ['larva', 'larvae'],
65
+ ['nebula', 'nebulae'],
66
+ ['formula', 'formulae'],
67
+ ['antenna', 'antennas'],
68
+ ['vertebra', 'vertebrae'],
69
+ ['index', 'indexes'],
70
+ ['addendum', 'addenda'],
71
+ ['aquarium', 'aquaria'],
72
+ ['stadium', 'stadia'],
73
+ ['maximum', 'maxima'],
74
+ ['minimum', 'minima'],
75
+ ['referendum', 'referenda'],
76
+ ['forum', 'fora'],
77
+ ['schema', 'schemata'],
78
+ ['leaf', 'leaves'],
79
+ ['knife', 'knives'],
80
+ ['wolf', 'wolves'],
81
+ ['elf', 'elves'],
82
+ ['half', 'halves'],
83
+ ['scarf', 'scarves'],
84
+ ];
85
+ export default function addCommonTerms() {
86
+ terms.forEach(([singular, plural]) => {
87
+ const regex = new RegExp(`${singular}$`, 'i');
88
+ pluralize.addPluralRule(regex, plural);
89
+ });
90
+ }
@@ -0,0 +1 @@
1
+ export default function replaceOccurrences(code: string, replacementSingular: string): string;
@@ -0,0 +1,49 @@
1
+ import camelCase from 'lodash/camelCase.js';
2
+ import kebabCase from 'lodash/kebabCase.js';
3
+ import snakeCase from 'lodash/snakeCase.js';
4
+ import upperFirst from 'lodash/upperFirst.js';
5
+ import pluralize from 'pluralize';
6
+ import addCommonTerms from './addCommonTerms';
7
+ addCommonTerms();
8
+ console.log('WoRD', pluralize('entity'));
9
+ export default function replaceOccurrences(code, replacementSingular) {
10
+ const replacementPlural = pluralize(replacementSingular);
11
+ // Different cases of the replacement string
12
+ const replacements = {
13
+ camelPlural: camelCase(replacementPlural),
14
+ camel: camelCase(replacementSingular),
15
+ pascalPlural: upperFirst(camelCase(replacementPlural)),
16
+ pascal: upperFirst(camelCase(replacementSingular)),
17
+ kebabPlural: kebabCase(replacementPlural),
18
+ kebab: kebabCase(replacementSingular),
19
+ snakePlural: snakeCase(replacementPlural),
20
+ snake: snakeCase(replacementSingular),
21
+ screamingSnakePlural: snakeCase(replacementPlural).toUpperCase(),
22
+ screamingSnake: snakeCase(replacementSingular).toUpperCase(),
23
+ screamingKebabPlural: kebabCase(replacementPlural).toUpperCase(),
24
+ screamingKebab: kebabCase(replacementSingular).toUpperCase(),
25
+ };
26
+ // Create a map of original patterns to their replacements
27
+ const originalPatterns = {
28
+ camelPlural: 'myThings',
29
+ camel: 'myThing',
30
+ pascalPlural: 'MyThings',
31
+ pascal: 'MyThing',
32
+ kebabPlural: 'my-things',
33
+ kebab: 'my-thing',
34
+ snakePlural: 'my_things',
35
+ snake: 'my_thing',
36
+ screamingSnakePlural: 'MY_THINGS',
37
+ screamingSnake: 'MY_THING',
38
+ screamingKebabPlural: 'MY-THINGS',
39
+ screamingKebab: 'MY-THING',
40
+ };
41
+ // Replace all occurrences in the code
42
+ Object.keys(originalPatterns).forEach((key) => {
43
+ const pattern = originalPatterns[key];
44
+ const replacementValue = replacements[key];
45
+ const regex = new RegExp(pattern, 'g');
46
+ code = code.replace(regex, replacementValue);
47
+ });
48
+ return code;
49
+ }
@@ -81,7 +81,7 @@ const prefix = '${projectInfo.apiEntryPoint}';
81
81
  const existingDts = await fs.readFile(localDtsPath, 'utf-8').catch(() => '');
82
82
  const existingTs = await fs.readFile(localTsPath, 'utf-8').catch(() => '');
83
83
  if (existingJs === js && existingDts === dts && existingTs === ts) {
84
- projectInfo.log.info(`Client is up to date and doesn't need to be regenerated (${Date.now() - now}ms).`);
84
+ projectInfo.log.info(`Client is up to date and doesn't need to be regenerated (${Date.now() - now}ms)`);
85
85
  return { written: false, path: clientoOutDirFullPath };
86
86
  }
87
87
  await fs.mkdir(clientoOutDirFullPath, { recursive: true });
@@ -10,7 +10,7 @@ export default async function getConfig({ clientOutDir }) {
10
10
  validationLibrary: env.VOVK_VALIDATION_LIBRARY ?? userConfig.validationLibrary ?? null,
11
11
  fetcher: env.VOVK_FETCHER ?? userConfig.fetcher ?? 'vovk/client/defaultFetcher',
12
12
  schemaOutDir: env.VOVK_SCHEMA_OUT_DIR ?? userConfig.schemaOutDir ?? './.vovk-schema',
13
- clientOutDir: clientOutDir ?? env.VOVK_CLIENT_OUT_DIR ?? userConfig.clientOutDir ?? './node_modules/.vovk',
13
+ clientOutDir: clientOutDir ?? env.VOVK_CLIENT_OUT_DIR ?? userConfig.clientOutDir ?? './node_modules/.vovk-client',
14
14
  origin: (env.VOVK_ORIGIN ?? userConfig.origin ?? '').replace(/\/$/, ''), // Remove trailing slash
15
15
  rootEntry: env.VOVK_ROOT_ENTRY ?? userConfig.rootEntry ?? 'api',
16
16
  rootSegmentModulesDirName: env.VOVK_ROOT_SEGMENT_MODULES_DIR_NAME ?? userConfig.rootSegmentModulesDirName ?? '',
package/dist/index.mjs CHANGED
@@ -51,8 +51,7 @@ program
51
51
  await result;
52
52
  }
53
53
  finally {
54
- // eslint-disable-next-line no-console
55
- console.log('🐺 Exiting...');
54
+ // do nothing, all processes are killed
56
55
  }
57
56
  }
58
57
  else {
@@ -0,0 +1 @@
1
+ export default function chalkHighlightThing(str: string): string;
@@ -0,0 +1,4 @@
1
+ import chalk from 'chalk';
2
+ export default function chalkHighlightThing(str) {
3
+ return chalk.cyan.bold(str);
4
+ }
@@ -1,5 +1,5 @@
1
- import chalk from 'chalk';
1
+ import chalkHighlightThing from './chalkHighlightThing.mjs';
2
2
  export default function formatLoggedSegmentName(segmentName, withChalk = false) {
3
3
  const text = segmentName ? `segment "${segmentName}"` : 'the root segment';
4
- return withChalk ? chalk.white.bold(text) : text;
4
+ return withChalk ? chalkHighlightThing(text) : text;
5
5
  }
@@ -2,7 +2,7 @@ import chalk from 'chalk';
2
2
  import loglevel from 'loglevel';
3
3
  export default function getLogger(level) {
4
4
  const log = {
5
- info: (msg) => loglevel.info(chalk.cyanBright(`🐺 ${msg}`)),
5
+ info: (msg) => loglevel.info(chalk.white(`🐺 ${msg}`)),
6
6
  warn: (msg) => loglevel.warn(chalk.yellowBright(`🐺 ${msg}`)),
7
7
  error: (msg) => loglevel.error(chalk.redBright(`🐺 ${msg}`)),
8
8
  debug: (msg) => loglevel.debug(chalk.gray(`🐺 ${msg}`)),
@@ -11,6 +11,8 @@ import debounceWithArgs from '../utils/debounceWithArgs.mjs';
11
11
  import debounce from 'lodash/debounce.js';
12
12
  import isEmpty from 'lodash/isEmpty.js';
13
13
  import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
14
+ import keyBy from 'lodash/keyBy.js';
15
+ import { capitalize } from 'lodash';
14
16
  export class VovkCLIWatcher {
15
17
  #projectInfo;
16
18
  #segments = [];
@@ -37,7 +39,7 @@ export class VovkCLIWatcher {
37
39
  this.#segments = this.#segments.find((s) => s.segmentName === segmentName)
38
40
  ? this.#segments
39
41
  : [...this.#segments, { routeFilePath: filePath, segmentName }];
40
- log.info(`Segment "${segmentName}" has been added`);
42
+ log.info(`${capitalize(formatLoggedSegmentName(segmentName, true))} has been added`);
41
43
  log.debug(`Full list of segments: ${this.#segments.map((s) => s.segmentName).join(', ')}`);
42
44
  void debouncedEnsureSchemaFiles(schemaOutFullPath, this.#segments.map((s) => s.segmentName), this.#projectInfo // TODO refactor
43
45
  );
@@ -70,7 +72,7 @@ export class VovkCLIWatcher {
70
72
  if (segmentReg.test(filePath)) {
71
73
  const segmentName = getSegmentName(filePath);
72
74
  this.#segments = this.#segments.filter((s) => s.segmentName !== segmentName);
73
- log.info(`Segment "${segmentName}" has been removed`);
75
+ log.info(`${capitalize(formatLoggedSegmentName(segmentName, true))} has been removed`);
74
76
  log.debug(`Full list of segments: ${this.#segments.map((s) => s.segmentName).join(', ')}`);
75
77
  void debouncedEnsureSchemaFiles(schemaOutFullPath, this.#segments.map((s) => s.segmentName), this.#projectInfo // TODO refactor
76
78
  );
@@ -167,13 +169,18 @@ export class VovkCLIWatcher {
167
169
  return;
168
170
  const nameOfClasReg = /\bclass\s+([A-Za-z_]\w*)(?:\s*<[^>]*>)?\s*\{/g;
169
171
  const namesOfClasses = [...code.matchAll(nameOfClasReg)].map((match) => match[1]);
170
- const importRegex = /import\s*{[^}]*\b(initVovk|get|post|put|del|head|options|worker)\b[^}]*}\s*from\s*['"]vovk['"]/;
172
+ const importRegex = /import\s*{[^}]*\b(get|post|put|del|head|options|worker)\b[^}]*}\s*from\s*['"]vovk['"]/;
171
173
  if (importRegex.test(code) && namesOfClasses.length) {
172
174
  const affectedSegments = this.#segments.filter((s) => {
173
175
  const schema = this.#schemas[s.segmentName];
174
176
  if (!schema)
175
177
  return false;
176
- return namesOfClasses.some((name) => schema.controllers[name] || schema.workers[name]);
178
+ const controllersByOriginalName = keyBy(schema.controllers, '_originalControllerName');
179
+ const workersByOriginalName = keyBy(schema.workers, '_originalWorkerName');
180
+ return namesOfClasses.some((name) => schema.controllers[name] ||
181
+ schema.workers[name] ||
182
+ controllersByOriginalName[name] ||
183
+ workersByOriginalName[name]);
177
184
  });
178
185
  if (affectedSegments.length) {
179
186
  log.debug(`A file with controller or worker ${namesOfClasses.join(', ')} have been modified at path "${filePath}". Segment(s) affected: ${affectedSegments.map((s) => s.segmentName).join(', ')}`);
@@ -232,7 +239,7 @@ export class VovkCLIWatcher {
232
239
  log.error(`Non-empty schema provided for ${formatLoggedSegmentName(segment.segmentName, true)} but emitSchema is false`);
233
240
  }
234
241
  if (this.#segments.every((s) => this.#schemas[s.segmentName])) {
235
- log.debug(`All segments with emitSchema=true have schema.`);
242
+ log.debug(`All segments with "emitSchema" have schema.`);
236
243
  await generateClient(this.#projectInfo, this.#segments, this.#schemas);
237
244
  }
238
245
  }
@@ -1,5 +1,5 @@
1
- import chalk from 'chalk';
2
1
  import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
2
+ import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
3
3
  export default function logDiffResult(segmentName, diffResult, projectInfo) {
4
4
  const diffNormalized = [];
5
5
  diffResult.workers.added.forEach((name) => {
@@ -31,43 +31,43 @@ export default function logDiffResult(segmentName, diffResult, projectInfo) {
31
31
  case 'worker':
32
32
  switch (diffNormalizedItem.type) {
33
33
  case 'added':
34
- projectInfo.log.info(`Schema for worker ${chalk.white.bold(diffNormalizedItem.name)} has been added at ${formatLoggedSegmentName(segmentName, true)}`);
34
+ projectInfo.log.info(`Schema for worker ${chalkHighlightThing(diffNormalizedItem.name)} has been added at ${formatLoggedSegmentName(segmentName, true)}`);
35
35
  break;
36
36
  case 'removed':
37
- projectInfo.log.info(`Schema for worker ${chalk.white.bold(diffNormalizedItem.name)} has been removed from ${formatLoggedSegmentName(segmentName, true)}`);
37
+ projectInfo.log.info(`Schema for worker ${chalkHighlightThing(diffNormalizedItem.name)} has been removed from ${formatLoggedSegmentName(segmentName, true)}`);
38
38
  break;
39
39
  }
40
40
  break;
41
41
  case 'controller':
42
42
  switch (diffNormalizedItem.type) {
43
43
  case 'added':
44
- projectInfo.log.info(`Schema for controller ${chalk.white.bold(diffNormalizedItem.name)} has been added at ${formatLoggedSegmentName(segmentName, true)}`);
44
+ projectInfo.log.info(`Schema for controller ${chalkHighlightThing(diffNormalizedItem.name)} has been added at ${formatLoggedSegmentName(segmentName, true)}`);
45
45
  break;
46
46
  case 'removed':
47
- projectInfo.log.info(`Schema for controller ${chalk.white.bold(diffNormalizedItem.name)} has been removed from ${formatLoggedSegmentName(segmentName, true)}`);
47
+ projectInfo.log.info(`Schema for controller ${chalkHighlightThing(diffNormalizedItem.name)} has been removed from ${formatLoggedSegmentName(segmentName, true)}`);
48
48
  break;
49
49
  }
50
50
  break;
51
51
  case 'workerHandler':
52
52
  switch (diffNormalizedItem.type) {
53
53
  case 'added':
54
- projectInfo.log.info(`Schema for worker method ${chalk.white.bold(diffNormalizedItem.name)} has been added at ${formatLoggedSegmentName(segmentName, true)}`);
54
+ projectInfo.log.info(`Schema for worker method ${chalkHighlightThing(diffNormalizedItem.name)} has been added at ${formatLoggedSegmentName(segmentName, true)}`);
55
55
  break;
56
56
  case 'removed':
57
- projectInfo.log.info(`Schema for worker method ${chalk.white.bold(diffNormalizedItem.name)} has been removed from ${formatLoggedSegmentName(segmentName, true)}`);
57
+ projectInfo.log.info(`Schema for worker method ${chalkHighlightThing(diffNormalizedItem.name)} has been removed from ${formatLoggedSegmentName(segmentName, true)}`);
58
58
  break;
59
59
  }
60
60
  break;
61
61
  case 'controllerHandler':
62
62
  switch (diffNormalizedItem.type) {
63
63
  case 'added':
64
- projectInfo.log.info(`Schema for controller method ${chalk.white.bold(diffNormalizedItem.name)} has been added at ${formatLoggedSegmentName(segmentName, true)}`);
64
+ projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been added at ${formatLoggedSegmentName(segmentName, true)}`);
65
65
  break;
66
66
  case 'removed':
67
- projectInfo.log.info(`Schema for controller method ${chalk.white.bold(diffNormalizedItem.name)} has been removed from ${formatLoggedSegmentName(segmentName, true)}`);
67
+ projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been removed from ${formatLoggedSegmentName(segmentName, true)}`);
68
68
  break;
69
69
  case 'changed':
70
- projectInfo.log.info(`Schema for controller method ${chalk.white.bold(diffNormalizedItem.name)} has been changed at ${formatLoggedSegmentName(segmentName, true)}`);
70
+ projectInfo.log.info(`Schema for controller method ${chalkHighlightThing(diffNormalizedItem.name)} has been changed at ${formatLoggedSegmentName(segmentName, true)}`);
71
71
  break;
72
72
  }
73
73
  break;
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "vovk-cli",
3
- "version": "0.0.1-beta.15",
3
+ "version": "0.0.1-beta.17",
4
4
  "bin": {
5
5
  "vovk": "./dist/index.mjs"
6
6
  },
7
+ "main": "./dist/index.mjs",
7
8
  "type": "module",
8
9
  "types": "./dist/index.d.mts",
9
10
  "prefix": ".",
10
11
  "scripts": {
11
- "build": "rm -rf dist tsconfig.tsbuildinfo && tsc",
12
- "test": "ts-node test/index.mts",
12
+ "build": "rm -rf dist tsconfig.build.tsbuildinfo && tsc -P tsconfig.build.json",
13
+ "test": "NODE_OPTIONS=\"--loader ts-node/esm\" TS_NODE_PROJECT=./tsconfig.test.json node test/index.mts",
13
14
  "ncu": "npm-check-updates -u",
14
15
  "npm-publish": "npm publish"
15
16
  },
@@ -30,21 +31,24 @@
30
31
  },
31
32
  "homepage": "https://vovk.dev",
32
33
  "peerDependencies": {
33
- "vovk": "^3.0.0-beta.22"
34
+ "vovk": "^3.0.0-beta.29"
34
35
  },
35
36
  "dependencies": {
36
- "@inquirer/prompts": "^5.3.8",
37
+ "@inquirer/prompts": "^5.5.0",
37
38
  "@npmcli/package-json": "^5.2.0",
38
39
  "chalk": "^5.3.0",
39
40
  "chokidar": "^3.6.0",
40
41
  "commander": "^12.1.0",
41
42
  "concurrently": "^8.2.2",
43
+ "inflection": "^3.0.0",
42
44
  "jsonc-parser": "^3.3.1",
43
45
  "lodash": "^4.17.21",
44
- "loglevel": "^1.9.1"
46
+ "loglevel": "^1.9.2",
47
+ "pluralize": "^8.0.0"
45
48
  },
46
49
  "devDependencies": {
47
50
  "@types/npmcli__package-json": "^4.0.4",
51
+ "@types/pluralize": "^0.0.33",
48
52
  "type-fest": "^4.26.0"
49
53
  }
50
54
  }
@@ -0,0 +1,32 @@
1
+ import { prefix, get, put, post, del, type VovkRequest } from 'vovk';
2
+
3
+ @prefix('my-things')
4
+ export default class MyThingController {
5
+ @get()
6
+ static getMyThingsExample = (req: VovkRequest<null, { q: string }>) => {
7
+ const q = req.nextUrl.searchParams.get('q');
8
+ return { q };
9
+ };
10
+
11
+ @put(':id')
12
+ static updateMyThingExample = async (
13
+ req: VovkRequest<{ foo: 'bar' | 'baz' }, { q: string }>,
14
+ params: { id: string }
15
+ ) => {
16
+ const { id } = params;
17
+ const body = await req.json();
18
+ const q = req.nextUrl.searchParams.get('q');
19
+
20
+ return { id, q, body };
21
+ };
22
+
23
+ @post()
24
+ static createMyThingExample = () => {
25
+ // ...
26
+ };
27
+
28
+ @del(':id')
29
+ static deleteMyThingExample = () => {
30
+ // ...
31
+ };
32
+ }
@@ -0,0 +1,34 @@
1
+ import { prefix, get, put, post, del, type VovkRequest } from 'vovk';
2
+ import MyThingService from './MyThingService.s.template';
3
+
4
+ @prefix('my-things')
5
+ export default class MyThingController {
6
+ @get()
7
+ static getMyThingsExample(req: VovkRequest<null, { q: string }>) {
8
+ const q = req.nextUrl.searchParams.get('q');
9
+
10
+ return MyThingService.getMyThingsExample(q);
11
+ }
12
+
13
+ @put(':id')
14
+ static updateMyThingExample = async (
15
+ req: VovkRequest<{ foo: 'bar' | 'baz' }, { q: string }>,
16
+ params: { id: string }
17
+ ) => {
18
+ const { id } = params;
19
+ const body = await req.json();
20
+ const q = req.nextUrl.searchParams.get('q');
21
+
22
+ return MyThingService.updateMyThingExample(id, q, body);
23
+ };
24
+
25
+ @post()
26
+ static createMyThingExample = () => {
27
+ // ...
28
+ };
29
+
30
+ @del(':id')
31
+ static deleteMyThingExample = () => {
32
+ // ...
33
+ };
34
+ }
@@ -0,0 +1,18 @@
1
+ import type { VovkControllerBody, VovkControllerQuery } from 'vovk';
2
+ import type MyThingController from './MyThingController.c.template';
3
+
4
+ export default class MyThingService {
5
+ static getMyThingsExample = (q: VovkControllerQuery<typeof MyThingController.getMyThingsExample>['q']) => {
6
+ return { q };
7
+ };
8
+
9
+ static updateMyThingExample = (
10
+ id: string,
11
+ q: VovkControllerQuery<typeof MyThingController.updateMyThingExample>['q'],
12
+ body: VovkControllerBody<typeof MyThingController.updateMyThingExample>
13
+ ) => {
14
+ return { id, q, body };
15
+ };
16
+
17
+ // ...
18
+ }