vovk-cli 0.0.1-draft.1 → 0.0.1-draft.10

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 (41) hide show
  1. package/README.md +1 -1
  2. package/dist/getProjectInfo/getRelativeSrcRoot.mjs +3 -3
  3. package/dist/getProjectInfo/getUserConfig.mjs +1 -1
  4. package/dist/index.d.mts +5 -1
  5. package/dist/index.mjs +6 -4
  6. package/dist/init/index.mjs +29 -19
  7. package/dist/init/installDependencies.d.mts +3 -0
  8. package/dist/init/installDependencies.mjs +1 -1
  9. package/dist/init/logUpdateDependenciesError.d.mts +11 -0
  10. package/dist/init/logUpdateDependenciesError.mjs +45 -0
  11. package/dist/init/updateDependenciesWithoutInstalling.mjs +9 -2
  12. package/dist/new/addClassToSegmentCode.mjs +6 -2
  13. package/dist/new/index.d.mts +1 -1
  14. package/dist/new/index.mjs +13 -3
  15. package/dist/new/newModule.d.mts +6 -2
  16. package/dist/new/newModule.mjs +39 -21
  17. package/dist/new/newSegment.d.mts +3 -2
  18. package/dist/new/newSegment.mjs +3 -3
  19. package/dist/new/render.d.mts +3 -7
  20. package/dist/new/render.mjs +13 -6
  21. package/dist/postinstall.mjs +3 -1
  22. package/dist/types.d.mts +7 -0
  23. package/dist/utils/getAvailablePort.mjs +1 -1
  24. package/dist/watcher/ensureSchemaFiles.mjs +18 -6
  25. package/dist/watcher/index.mjs +11 -9
  26. package/dist/watcher/writeOneSchemaFile.mjs +2 -1
  27. package/package.json +6 -5
  28. package/templates/controller.ejs +15 -14
  29. package/templates/service.ejs +24 -4
  30. package/templates/worker.ejs +24 -1
  31. package/dist/getProjectInfo/directoryExists.d.mts +0 -1
  32. package/dist/getProjectInfo/directoryExists.mjs +0 -10
  33. package/templates_old/MyThingController.c.only.template.ts +0 -32
  34. package/templates_old/MyThingController.c.template.ts +0 -34
  35. package/templates_old/MyThingService.s.template.ts +0 -18
  36. package/templates_old/controller.ejs +0 -85
  37. package/templates_old/service.ejs +0 -9
  38. package/templates_old/worker.ejs +0 -9
  39. package/templates_old/zod/MyThingController.c.only.template.ts +0 -32
  40. package/templates_old/zod/MyThingController.c.template.ts +0 -39
  41. package/templates_old/zod/MyThingService.s.template.ts +0 -18
package/README.md CHANGED
@@ -1 +1 @@
1
- Description is coming soon.
1
+ Description is coming soon.
@@ -1,11 +1,11 @@
1
1
  import path from 'path';
2
- import directoryExists from './directoryExists.mjs';
2
+ import getFileSystemEntryType, { FileSystemEntryType } from '../utils/getFileSystemEntryType.mjs';
3
3
  export default async function getRelativeSrcRoot({ cwd }) {
4
4
  // Next.js Docs: src/app or src/pages will be ignored if app or pages are present in the root directory.
5
- if (await directoryExists(path.join(cwd, 'app'))) {
5
+ if ((await getFileSystemEntryType(path.join(cwd, 'app'))) === FileSystemEntryType.DIRECTORY) {
6
6
  return '.';
7
7
  }
8
- else if (await directoryExists(path.join(cwd, 'src/app'))) {
8
+ else if ((await getFileSystemEntryType(path.join(cwd, 'src/app'))) === FileSystemEntryType.DIRECTORY) {
9
9
  return './src';
10
10
  }
11
11
  throw new Error(`${cwd} Could not find app router directory. Check Next.js docs for more info.`);
@@ -11,7 +11,7 @@ async function getUserConfig({ cwd, }) {
11
11
  try {
12
12
  userConfig = await importUncachedModule(configPath);
13
13
  }
14
- catch (e) {
14
+ catch {
15
15
  try {
16
16
  const cacheBuster = Date.now();
17
17
  ({ default: userConfig } = (await import(`${configPath}?cache=${cacheBuster}`)));
package/dist/index.d.mts CHANGED
@@ -20,7 +20,11 @@ export interface InitOptions {
20
20
  channel?: 'latest' | 'beta' | 'dev';
21
21
  }
22
22
  export interface NewOptions {
23
- dryRun: boolean;
23
+ dryRun?: boolean;
24
+ template?: string;
25
+ dir?: string;
26
+ overwrite?: boolean;
27
+ noSegmentUpdate?: boolean;
24
28
  }
25
29
  declare const program: Command;
26
30
  export declare function initProgram(p: typeof program, command: string): Command;
package/dist/index.mjs CHANGED
@@ -25,9 +25,7 @@ program
25
25
  const PORT = !options.nextDev
26
26
  ? process.env.PORT
27
27
  : process.env.PORT ||
28
- (await getAvailablePort(3000, portAttempts, 0, (failedPort, tryingPort) =>
29
- // eslint-disable-next-line no-console
30
- console.warn(`🐺 Next.js Port ${failedPort} is in use, trying ${tryingPort} instead.`)).catch(() => {
28
+ (await getAvailablePort(3000, portAttempts, 0, (failedPort, tryingPort) => console.warn(`🐺 Next.js Port ${failedPort} is in use, trying ${tryingPort} instead.`)).catch(() => {
31
29
  throw new Error(`🐺 ❌ Failed to find available Next port after ${portAttempts} attempts`);
32
30
  }));
33
31
  if (!PORT) {
@@ -88,8 +86,8 @@ export function initProgram(p, command) {
88
86
  .option('--update-scripts <mode>', 'Update package.json scripts (implicit or explicit)')
89
87
  .option('--validation-library <library>', 'Validation library to use ("vovk-zod", "vovk-yup", "vovk-dto" or another). Set to "none" to skip validation')
90
88
  .option('--validate-on-client', 'Validate on client')
91
- .option('--dry-run', 'Do not write files to disk')
92
89
  .option('--channel <channel>', 'Channel to use for fetching packages', 'latest')
90
+ .option('--dry-run', 'Do not write files to disk')
93
91
  .action((prefix = '.', options) => new Init().main(prefix, options));
94
92
  }
95
93
  initProgram(program, 'init ');
@@ -97,6 +95,10 @@ program
97
95
  .command('new [components...]')
98
96
  .alias('n')
99
97
  .description('Create new components. "vovk new [...components] [segmentName/]moduleName" to create a new module or "vovk new segment [segmentName]" to create a new segment')
98
+ .option('-O, --overwrite', 'Overwrite existing files')
99
+ .option('--template <template>', 'Override config template')
100
+ .option('--dir <dirname>', 'Override dirName in template file. Relative to the root of the project')
101
+ .option('--no-segment-update', 'Do not update segment files when creating a new module')
100
102
  .option('--dry-run', 'Do not write files to disk')
101
103
  .action((components, options) => newComponents(components, options));
102
104
  program
@@ -62,13 +62,15 @@ import fs from 'fs/promises';
62
62
  import getConfigPaths from '../getProjectInfo/getConfigAbsolutePaths.mjs';
63
63
  import chalk from 'chalk';
64
64
  import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
65
- import installDependencies from './installDependencies.mjs';
65
+ import installDependencies, { getPackageManager } from './installDependencies.mjs';
66
66
  import getLogger from '../utils/getLogger.mjs';
67
67
  import createConfig from './createConfig.mjs';
68
68
  import updateNPMScripts from './updateNPMScripts.mjs';
69
69
  import checkTSConfigForExperimentalDecorators from './checkTSConfigForExperimentalDecorators.mjs';
70
70
  import updateTypeScriptConfig from './updateTypeScriptConfig.mjs';
71
71
  import updateDependenciesWithoutInstalling from './updateDependenciesWithoutInstalling.mjs';
72
+ import logUpdateDependenciesError from './logUpdateDependenciesError.mjs';
73
+ import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
72
74
  export class Init {
73
75
  root;
74
76
  log;
@@ -113,6 +115,7 @@ export class Init {
113
115
  }
114
116
  }
115
117
  if (!dryRun) {
118
+ let depsUpdated = false;
116
119
  try {
117
120
  await updateDependenciesWithoutInstalling({
118
121
  log,
@@ -121,27 +124,34 @@ export class Init {
121
124
  devDependencyNames: devDependencies,
122
125
  channel: channel ?? 'latest',
123
126
  });
124
- log.info('Updated dependencies and devDependencies in package.json');
127
+ depsUpdated = true;
125
128
  }
126
- catch (error) {
127
- log.error(`Failed to update dependencies: ${error.message}. Please `);
129
+ catch (e) {
130
+ const error = e;
131
+ logUpdateDependenciesError({ log, error, useNpm, useYarn, usePnpm, useBun, dependencies, devDependencies });
128
132
  }
129
- if (!skipInstall) {
130
- try {
131
- await installDependencies({
132
- log,
133
- cwd: root,
134
- options: {
135
- useNpm,
136
- useYarn,
137
- usePnpm,
138
- useBun,
139
- },
140
- });
141
- log.info('Dependencies installed successfully');
133
+ if (depsUpdated) {
134
+ const packageManager = getPackageManager({ useNpm, useYarn, usePnpm, useBun });
135
+ if (skipInstall) {
136
+ log.info(`Installation skipped. Please, install them manually with ${chalkHighlightThing(packageManager + ' install')}`);
142
137
  }
143
- catch (error) {
144
- log.error(`Failed to install dependencies: ${error.message}`);
138
+ else {
139
+ try {
140
+ await installDependencies({
141
+ log,
142
+ cwd: root,
143
+ options: {
144
+ useNpm,
145
+ useYarn,
146
+ usePnpm,
147
+ useBun,
148
+ },
149
+ });
150
+ log.info('Dependencies installed successfully');
151
+ }
152
+ catch (error) {
153
+ log.warn(`Failed to install dependencies: ${error.message}. Please, install them manually with ${chalkHighlightThing(packageManager + ' install')}`);
154
+ }
145
155
  }
146
156
  }
147
157
  }
@@ -1,7 +1,10 @@
1
1
  import { InitOptions } from '../index.mjs';
2
2
  import getLogger from '../utils/getLogger.mjs';
3
+ type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';
4
+ export declare function getPackageManager(options: Pick<InitOptions, 'useNpm' | 'useYarn' | 'usePnpm' | 'useBun'>): PackageManager;
3
5
  export default function installDependencies({ log, cwd, options, }: {
4
6
  log: ReturnType<typeof getLogger>;
5
7
  cwd: string;
6
8
  options: Pick<InitOptions, 'useNpm' | 'useYarn' | 'usePnpm' | 'useBun'>;
7
9
  }): Promise<void>;
10
+ export {};
@@ -1,5 +1,5 @@
1
1
  import { spawn } from 'child_process';
2
- function getPackageManager(options) {
2
+ export function getPackageManager(options) {
3
3
  if (options.useNpm)
4
4
  return 'npm';
5
5
  if (options.useYarn)
@@ -0,0 +1,11 @@
1
+ import getLogger from "../utils/getLogger.mjs";
2
+ export default function logUpdateDependenciesError({ useNpm, useYarn, usePnpm, useBun, log, dependencies, devDependencies, error }: {
3
+ useNpm?: boolean;
4
+ useYarn?: boolean;
5
+ usePnpm?: boolean;
6
+ useBun?: boolean;
7
+ log: ReturnType<typeof getLogger>;
8
+ dependencies: string[];
9
+ devDependencies: string[];
10
+ error: Error;
11
+ }): void;
@@ -0,0 +1,45 @@
1
+ import chalkHighlightThing from "../utils/chalkHighlightThing.mjs";
2
+ import { getPackageManager } from "./installDependencies.mjs";
3
+ export default function logUpdateDependenciesError({ useNpm, useYarn, usePnpm, useBun, log, dependencies, devDependencies, error }) {
4
+ const packageManager = getPackageManager({ useNpm, useYarn, usePnpm, useBun });
5
+ const installCommands = [];
6
+ if (dependencies.length > 0) {
7
+ let depInstallCmd = '';
8
+ switch (packageManager) {
9
+ case 'npm':
10
+ depInstallCmd = `npm install ${dependencies.join(' ')}`;
11
+ break;
12
+ case 'yarn':
13
+ depInstallCmd = `yarn add ${dependencies.join(' ')}`;
14
+ break;
15
+ case 'pnpm':
16
+ depInstallCmd = `pnpm add ${dependencies.join(' ')}`;
17
+ break;
18
+ case 'bun':
19
+ depInstallCmd = `bun add ${dependencies.join(' ')}`;
20
+ break;
21
+ }
22
+ installCommands.push(depInstallCmd);
23
+ }
24
+ if (devDependencies.length > 0) {
25
+ let devDepInstallCmd = '';
26
+ switch (packageManager) {
27
+ case 'npm':
28
+ devDepInstallCmd = `npm install -D ${devDependencies.join(' ')}`;
29
+ break;
30
+ case 'yarn':
31
+ devDepInstallCmd = `yarn add --dev ${devDependencies.join(' ')}`;
32
+ break;
33
+ case 'pnpm':
34
+ devDepInstallCmd = `pnpm add -D ${devDependencies.join(' ')}`;
35
+ break;
36
+ case 'bun':
37
+ devDepInstallCmd = `bun add -d ${devDependencies.join(' ')}`;
38
+ break;
39
+ }
40
+ installCommands.push(devDepInstallCmd);
41
+ }
42
+ const installCmd = installCommands.join(' && ');
43
+ // Log the error with the appropriate manual installation instructions
44
+ log.warn(`Failed to update dependencies: ${error.message}. Please, install them manually with ${chalkHighlightThing(installCmd)}`);
45
+ }
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs/promises';
2
2
  import getNPMPackageMetadata from '../utils/getNPMPackageMetadata.mjs';
3
3
  import path from 'path';
4
+ import chalk from 'chalk';
4
5
  async function updateDeps({ packageJson, packageNames, channel, key, }) {
5
6
  return Promise.all(packageNames.map(async (packageName) => {
6
7
  if (packageJson[key]?.[packageName])
@@ -18,8 +19,14 @@ export default async function updateDependenciesWithoutInstalling({ log, dir, de
18
19
  const packageJsonPath = path.join(dir, 'package.json');
19
20
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
20
21
  await updateDeps({ packageJson, packageNames: dependencyNames, channel, key: 'dependencies' });
21
- log.debug('Updated dependencies in package.json');
22
22
  await updateDeps({ packageJson, packageNames: devDependencyNames, channel, key: 'devDependencies' });
23
- log.debug('Updated devDependencies in package.json');
24
23
  await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
24
+ log.info('Added dependencies to package.json:');
25
+ for (const dependency of dependencyNames) {
26
+ log.raw.log(` - ${chalk.cyan(dependency)}`);
27
+ }
28
+ log.info('Added devDependencies to package.json:');
29
+ for (const dependency of devDependencyNames) {
30
+ log.raw.log(` - ${chalk.cyan(dependency)}`);
31
+ }
25
32
  }
@@ -1,6 +1,10 @@
1
- import { Project, SyntaxKind } from 'ts-morph';
1
+ import { Project, QuoteKind, SyntaxKind } from 'ts-morph';
2
2
  export default function addClassToSegmentCode(segmentSourceCode, { sourceName, compiledName, type, importPath, }) {
3
- const project = new Project();
3
+ const project = new Project({
4
+ manipulationSettings: {
5
+ quoteKind: QuoteKind.Single
6
+ }
7
+ });
4
8
  const sourceFile = project.createSourceFile('route.ts', segmentSourceCode, { overwrite: true });
5
9
  // Add the import if it doesn't exist
6
10
  let importDeclaration = sourceFile.getImportDeclaration((imp) => {
@@ -1,2 +1,2 @@
1
1
  import type { NewOptions } from '../index.mjs';
2
- export default function newComponents(components: string[], options: NewOptions): Promise<void>;
2
+ export default function newComponents(components: string[], { dryRun, dir, template, overwrite, noSegmentUpdate }: NewOptions): Promise<void>;
@@ -1,7 +1,8 @@
1
1
  import newModule from './newModule.mjs';
2
2
  import newSegment from './newSegment.mjs';
3
- export default async function newComponents(components, options) {
3
+ export default async function newComponents(components, { dryRun, dir, template, overwrite, noSegmentUpdate }) {
4
4
  if (components[0] === 'segment' || components[0] === 'segments') {
5
+ // vovk new segment [segmentName]
5
6
  let segmentNames = components
6
7
  .slice(1)
7
8
  .map((segmentName) => (segmentName === '""' || segmentName === "''" ? '' : segmentName));
@@ -9,10 +10,11 @@ export default async function newComponents(components, options) {
9
10
  segmentNames = [''];
10
11
  }
11
12
  for (const segmentName of segmentNames) {
12
- await newSegment({ segmentName, dryRun: options.dryRun });
13
+ await newSegment({ segmentName, overwrite, dryRun });
13
14
  }
14
15
  }
15
16
  else {
17
+ // vovk new [what...] [moduleNameWithOptionalSegment]
16
18
  if (components.length < 2) {
17
19
  throw new Error('Invalid command invocation. Please provide at least two arguments.');
18
20
  }
@@ -21,6 +23,14 @@ export default async function newComponents(components, options) {
21
23
  if (!moduleNameWithOptionalSegment) {
22
24
  throw new Error('A module name with an optional segment cannot be empty');
23
25
  }
24
- await newModule({ what, moduleNameWithOptionalSegment, dryRun: options.dryRun });
26
+ await newModule({
27
+ what,
28
+ moduleNameWithOptionalSegment,
29
+ dir,
30
+ template,
31
+ overwrite,
32
+ noSegmentUpdate,
33
+ dryRun,
34
+ });
25
35
  }
26
36
  }
@@ -1,5 +1,9 @@
1
- export default function newModule({ what, moduleNameWithOptionalSegment, dryRun, }: {
1
+ export default function newModule({ what, moduleNameWithOptionalSegment, dryRun, dir: dirNameFlag, template: templateFlag, noSegmentUpdate, overwrite, }: {
2
2
  what: string[];
3
3
  moduleNameWithOptionalSegment: string;
4
- dryRun: boolean;
4
+ dryRun?: boolean;
5
+ dir?: string;
6
+ template?: string;
7
+ noSegmentUpdate?: boolean;
8
+ overwrite?: boolean;
5
9
  }): Promise<void>;
@@ -18,7 +18,7 @@ function splitByLast(str, delimiter = '/') {
18
18
  const after = str.substring(index + delimiter.length);
19
19
  return [before, after];
20
20
  }
21
- export default async function newModule({ what, moduleNameWithOptionalSegment, dryRun, }) {
21
+ export default async function newModule({ what, moduleNameWithOptionalSegment, dryRun, dir: dirNameFlag, template: templateFlag, noSegmentUpdate, overwrite, }) {
22
22
  const { config, log, apiDir, cwd } = await getProjectInfo();
23
23
  const templates = config.templates;
24
24
  const [segmentName, moduleName] = splitByLast(moduleNameWithOptionalSegment);
@@ -35,10 +35,19 @@ export default async function newModule({ what, moduleNameWithOptionalSegment, d
35
35
  return s;
36
36
  }
37
37
  });
38
- // check if template exists
39
- for (const type of what) {
40
- if (!templates[type]) {
41
- throw new Error(`Template for ${type} not found in config`);
38
+ console.log('moduleNameWithOptionalSegment', moduleNameWithOptionalSegment);
39
+ console.log('what', what);
40
+ if (templateFlag) {
41
+ if (what.length > 1) {
42
+ throw new Error('Cannot use --template flag with multiple types');
43
+ }
44
+ }
45
+ else {
46
+ // check if template exists
47
+ for (const type of what) {
48
+ if (!templates[type]) {
49
+ throw new Error(`Template for ${type} not found in config`);
50
+ }
42
51
  }
43
52
  }
44
53
  const segments = await locateSegments(apiDir);
@@ -47,44 +56,53 @@ export default async function newModule({ what, moduleNameWithOptionalSegment, d
47
56
  throw new Error(`Segment ${segmentName} not found`);
48
57
  }
49
58
  for (const type of what) {
50
- const templatePath = templates[type];
59
+ const templatePath = templateFlag ?? templates[type];
51
60
  const templateAbsolutePath = templatePath.startsWith('/') || templatePath.startsWith('.')
52
61
  ? path.resolve(cwd, templatePath)
53
62
  : path.resolve(cwd, './node_modules', templatePath);
54
63
  const templateCode = await fs.readFile(templateAbsolutePath, 'utf-8');
55
- const { filePath, sourceName, compiledName, code } = await render(templateCode, {
64
+ const { dirName: renderedDirName, fileName, sourceName, compiledName, code, } = await render(templateCode, {
65
+ cwd,
56
66
  config,
57
67
  withService: what.includes('service'),
58
68
  segmentName,
59
69
  moduleName,
60
70
  });
61
- const absoluteModulePath = path.join(cwd, config.modulesDir, filePath);
62
- const dirName = path.dirname(absoluteModulePath);
71
+ const absoluteModuleDir = path.join(cwd, dirNameFlag || renderedDirName);
72
+ const absoluteModulePath = path.join(absoluteModuleDir, fileName);
63
73
  const prettiedCode = await prettify(code, absoluteModulePath);
64
74
  if (!dryRun) {
65
- if (await getFileSystemEntryType(absoluteModulePath)) {
75
+ if (!overwrite && (await getFileSystemEntryType(absoluteModulePath))) {
66
76
  log.warn(`File ${chalkHighlightThing(absoluteModulePath)} already exists, skipping this "${type}"`);
67
77
  }
68
78
  else {
69
- await fs.mkdir(dirName, { recursive: true });
79
+ await fs.mkdir(absoluteModuleDir, { recursive: true });
70
80
  await fs.writeFile(absoluteModulePath, prettiedCode);
71
81
  }
72
82
  }
73
83
  if (type === 'controller' || type === 'worker') {
84
+ if (!sourceName) {
85
+ throw new Error('sourceName is required');
86
+ }
87
+ if (!compiledName) {
88
+ throw new Error('compiledName is required');
89
+ }
74
90
  const { routeFilePath } = segment;
75
91
  const segmentSourceCode = await fs.readFile(routeFilePath, 'utf-8');
76
- const importPath = path.relative(dirName, absoluteModulePath).replace(/\.(ts|tsx)$/, '');
77
- const newSegmentCode = addClassToSegmentCode(segmentSourceCode, {
78
- sourceName,
79
- compiledName,
80
- type,
81
- importPath,
82
- });
83
- if (!dryRun) {
84
- await fs.writeFile(routeFilePath, newSegmentCode);
92
+ const importPath = path.relative(path.dirname(routeFilePath), absoluteModulePath).replace(/\.(ts|tsx)$/, '');
93
+ if (!noSegmentUpdate) {
94
+ const newSegmentCode = addClassToSegmentCode(segmentSourceCode, {
95
+ sourceName,
96
+ compiledName,
97
+ type,
98
+ importPath,
99
+ });
100
+ if (!dryRun) {
101
+ await fs.writeFile(routeFilePath, newSegmentCode);
102
+ }
85
103
  }
86
104
  log.info(`Added ${chalkHighlightThing(sourceName)} ${type} to ${formatLoggedSegmentName(segmentName)} as ${chalkHighlightThing(compiledName)}`);
87
105
  }
88
- log.info(`Created ${chalkHighlightThing(sourceName)} with ${chalkHighlightThing(type)} template for ${formatLoggedSegmentName(segmentName)}`);
106
+ log.info(`Created ${chalkHighlightThing(fileName)} using "${chalkHighlightThing(type)}" template for ${formatLoggedSegmentName(segmentName)}`);
89
107
  }
90
108
  }
@@ -1,4 +1,5 @@
1
- export default function newSegment({ segmentName, dryRun }: {
1
+ export default function newSegment({ segmentName, overwrite, dryRun, }: {
2
2
  segmentName: string;
3
- dryRun: boolean;
3
+ overwrite?: boolean;
4
+ dryRun?: boolean;
4
5
  }): Promise<void>;
@@ -5,10 +5,10 @@ import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
5
5
  import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
6
6
  import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
7
7
  import prettify from '../utils/prettify.mjs';
8
- export default async function newSegment({ segmentName, dryRun }) {
8
+ export default async function newSegment({ segmentName, overwrite, dryRun, }) {
9
9
  const { apiDir, cwd, log } = await getProjectInfo();
10
10
  const absoluteSegmentRoutePath = path.join(cwd, apiDir, segmentName, '[[...vovk]]/route.ts');
11
- if (await getFileSystemEntryType(absoluteSegmentRoutePath)) {
11
+ if (!overwrite && (await getFileSystemEntryType(absoluteSegmentRoutePath))) {
12
12
  throw new Error(`Unable to create new segment. ${formatLoggedSegmentName(segmentName, { upperFirst: true })} already exists.`);
13
13
  }
14
14
  const code = await prettify(`import { initVovk } from 'vovk';
@@ -29,5 +29,5 @@ ${segmentName ? ` segmentName: '${segmentName}',\n` : ''} emitSchema: true,
29
29
  await fs.mkdir(path.dirname(absoluteSegmentRoutePath), { recursive: true });
30
30
  await fs.writeFile(absoluteSegmentRoutePath, code);
31
31
  }
32
- log.info(`${formatLoggedSegmentName(segmentName, { upperFirst: true })} created at ${absoluteSegmentRoutePath}. Run ${chalkHighlightThing(`vovk new controller ${segmentName}/someName`)} to create a controller or modify the segment file manually`);
32
+ log.info(`${formatLoggedSegmentName(segmentName, { upperFirst: true })} created at ${absoluteSegmentRoutePath}. Run ${chalkHighlightThing(`vovk new controller ${[segmentName, 'someName'].filter(Boolean).join('/')}`)} to create a controller.`);
33
33
  }
@@ -1,12 +1,8 @@
1
- import type { VovkConfig } from '../types.mjs';
1
+ import type { VovkConfig, VovkModuleRenderResult } from '../types.mjs';
2
2
  export default function render(codeTemplate: string, { config, withService, segmentName, moduleName, }: {
3
+ cwd: string;
3
4
  config: VovkConfig;
4
5
  withService: boolean;
5
6
  segmentName: string;
6
7
  moduleName: string;
7
- }): Promise<{
8
- filePath: string;
9
- sourceName: string;
10
- compiledName: string;
11
- code: string;
12
- }>;
8
+ }): Promise<VovkModuleRenderResult>;
@@ -5,7 +5,7 @@ import pluralize from 'pluralize';
5
5
  import addCommonTerms from './addCommonTerms.mjs';
6
6
  addCommonTerms();
7
7
  export default async function render(codeTemplate, { config, withService, segmentName, moduleName, }) {
8
- const getModulePath = (givenSegmentName, givenModuleName, fileName) => [givenSegmentName || config.rootSegmentModulesDirName, _.camelCase(givenModuleName), fileName]
8
+ const getModuleDirName = (givenSegmentName, givenModuleName) => [config.modulesDir, givenSegmentName || config.rootSegmentModulesDirName, _.camelCase(givenModuleName)]
9
9
  .filter(Boolean)
10
10
  .join('/');
11
11
  const templateVars = {
@@ -15,15 +15,22 @@ export default async function render(codeTemplate, { config, withService, segmen
15
15
  segmentName,
16
16
  moduleName,
17
17
  // utils
18
- getModulePath,
18
+ getModuleDirName,
19
19
  // libraries
20
20
  _, // lodash
21
21
  pluralize,
22
22
  };
23
23
  // first, render the front matter because it can use ejs variables
24
24
  const parsed = matter((await ejs.render(codeTemplate, templateVars, { async: true })).trim());
25
- const { filePath, sourceName, compiledName } = parsed.data;
26
- const templateContent = parsed.content;
27
- const code = await ejs.render(templateContent, templateVars, { async: true });
28
- return { filePath, sourceName, compiledName, code };
25
+ const { dirName, fileName, sourceName, compiledName } = parsed.data;
26
+ const code = parsed.content;
27
+ // const templateContent = parsed.content; TODO
28
+ // const code = await ejs.render(templateContent, templateVars, { async: true });
29
+ return {
30
+ dirName,
31
+ fileName,
32
+ sourceName,
33
+ compiledName,
34
+ code,
35
+ };
29
36
  }
@@ -11,7 +11,9 @@ async function postinstall() {
11
11
  const js = path.join(vovk, 'client.js');
12
12
  const ts = path.join(vovk, 'client.d.ts');
13
13
  const index = path.join(vovk, 'index.ts');
14
- if ((await getFileSystemEntryType(js)) || (await getFileSystemEntryType(ts)) || (await getFileSystemEntryType(index))) {
14
+ if ((await getFileSystemEntryType(js)) ||
15
+ (await getFileSystemEntryType(ts)) ||
16
+ (await getFileSystemEntryType(index))) {
15
17
  return;
16
18
  }
17
19
  await fs.mkdir(vovk, { recursive: true });
package/dist/types.d.mts CHANGED
@@ -37,3 +37,10 @@ export type VovkConfig = {
37
37
  [key: string]: string | undefined;
38
38
  };
39
39
  };
40
+ export type VovkModuleRenderResult = {
41
+ fileName: string;
42
+ dirName: string;
43
+ sourceName?: string;
44
+ compiledName?: string;
45
+ code: string;
46
+ };
@@ -34,7 +34,7 @@ function getAvailablePort(startPort, maxAttempts, attempt, onWarning) {
34
34
  getAvailablePort(startPort + 1, maxAttempts, attempt + 1, onWarning).then(resolve, reject);
35
35
  }
36
36
  else {
37
- reject('No available ports found');
37
+ reject(new Error('No available ports found'));
38
38
  }
39
39
  });
40
40
  });
@@ -7,17 +7,29 @@ export default async function ensureSchemaFiles(projectInfo, schemaOutAbsolutePa
7
7
  const now = Date.now();
8
8
  let hasChanged = false;
9
9
  // Create index.js file
10
- const indexContent = segmentNames
10
+ const indexContent = `// auto-generated
11
+ ${segmentNames
11
12
  .map((segmentName) => {
12
13
  return `module.exports['${segmentName}'] = require('./${segmentName || ROOT_SEGMENT_SCHEMA_NAME}.json');`;
13
14
  })
14
- .join('\n');
15
- const dTsContent = `import type { VovkSchema } from 'vovk';
16
- declare const segmentSchema: Record<string, VovkSchema>;
15
+ .join('\n')}`;
16
+ const dTsContent = `// auto-generated
17
+ import type { VovkSchema } from 'vovk';
18
+ declare const segmentSchema: {
19
+ ${segmentNames.map((segmentName) => ` '${segmentName}': VovkSchema;`).join('\n')}
20
+ };
17
21
  export default segmentSchema;`;
22
+ const jsAbsolutePath = path.join(schemaOutAbsolutePath, 'index.js');
23
+ const dTsAbsolutePath = path.join(schemaOutAbsolutePath, 'index.d.ts');
24
+ const existingJs = await fs.readFile(jsAbsolutePath, 'utf-8').catch(() => null);
25
+ const existingDTs = await fs.readFile(dTsAbsolutePath, 'utf-8').catch(() => null);
18
26
  await fs.mkdir(schemaOutAbsolutePath, { recursive: true });
19
- await fs.writeFile(path.join(schemaOutAbsolutePath, 'index.js'), indexContent);
20
- await fs.writeFile(path.join(schemaOutAbsolutePath, 'index.d.ts'), dTsContent);
27
+ if (existingJs !== indexContent) {
28
+ await fs.writeFile(jsAbsolutePath, indexContent);
29
+ }
30
+ if (existingDTs !== dTsContent) {
31
+ await fs.writeFile(dTsAbsolutePath, dTsContent);
32
+ }
21
33
  // Create JSON files (if not exist) with name [segmentName].json (where segmentName can include /, which means the folder structure can be nested)
22
34
  await Promise.all(segmentNames.map(async (segmentName) => {
23
35
  const { isCreated } = await writeOneSchemaFile({
@@ -51,7 +51,6 @@ export class VovkCLIWatcher {
51
51
  void this.#requestSchema(getSegmentName(filePath));
52
52
  }
53
53
  })
54
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
55
54
  .on('addDir', async (dirPath) => {
56
55
  log.debug(`Directory ${dirPath} has been added to segments folder`);
57
56
  this.#segments = await locateSegments(apiDirAbsolutePath);
@@ -59,7 +58,6 @@ export class VovkCLIWatcher {
59
58
  void this.#requestSchema(segmentName);
60
59
  }
61
60
  })
62
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
63
61
  .on('unlinkDir', async (dirPath) => {
64
62
  log.debug(`Directory ${dirPath} has been removed from segments folder`);
65
63
  this.#segments = await locateSegments(apiDirAbsolutePath);
@@ -81,7 +79,7 @@ export class VovkCLIWatcher {
81
79
  log.debug('Segments watcher is ready');
82
80
  })
83
81
  .on('error', (error) => {
84
- log.error(`Error watching segments folder: ${error.message}`);
82
+ log.error(`Error watching segments folder: ${error?.message ?? 'Unknown error'}`);
85
83
  });
86
84
  };
87
85
  #watchModules = () => {
@@ -119,13 +117,14 @@ export class VovkCLIWatcher {
119
117
  log.debug('Modules watcher is ready');
120
118
  })
121
119
  .on('error', (error) => {
122
- log.error(`Error watching modules folder: ${error.message}`);
120
+ log.error(`Error watching modules folder: ${error?.message ?? 'Unknown error'}`);
123
121
  });
124
122
  };
125
123
  #watchConfig = () => {
126
124
  const { log, cwd } = this.#projectInfo;
127
125
  log.debug(`Watching config files`);
128
126
  let isInitial = true;
127
+ let isReady = false;
129
128
  const handle = debounce(async () => {
130
129
  this.#projectInfo = await getProjectInfo();
131
130
  if (!isInitial) {
@@ -148,11 +147,14 @@ export class VovkCLIWatcher {
148
147
  .on('change', () => void handle())
149
148
  .on('unlink', () => void handle())
150
149
  .on('ready', () => {
150
+ if (isReady)
151
+ return;
152
+ // for some reason this watcher triggers ready event twice
151
153
  log.debug('Config files watcher is ready');
154
+ isReady = true;
152
155
  })
153
- .on('error', (error) => {
154
- log.error(`Error watching config files: ${error.message}`);
155
- });
156
+ .on('error', (error) => log.error(`Error watching config files: ${error?.message ?? 'Unknown error'}`));
157
+ void handle();
156
158
  };
157
159
  #watch() {
158
160
  if (this.#isWatching)
@@ -274,13 +276,13 @@ export class VovkCLIWatcher {
274
276
  const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
275
277
  this.#segments = await locateSegments(apiDirAbsolutePath);
276
278
  await debouncedEnsureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, this.#segments.map((s) => s.segmentName));
277
- // Request schema every segment in 3 seconds in order to update schema and start watching
279
+ // Request schema every segment in 5 seconds in order to update schema and start watching
278
280
  setTimeout(() => {
279
281
  for (const { segmentName } of this.#segments) {
280
282
  void this.#requestSchema(segmentName);
281
283
  }
282
284
  this.#watch();
283
- }, 3000);
285
+ }, 5000);
284
286
  }
285
287
  }
286
288
  const env = process.env;
@@ -1,10 +1,11 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs/promises';
3
3
  import diffSchema from './diffSchema.mjs';
4
+ import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
4
5
  export const ROOT_SEGMENT_SCHEMA_NAME = '_root';
5
6
  export default async function writeOneSchemaFile({ schemaOutAbsolutePath, schema, skipIfExists = false, }) {
6
7
  const segmentPath = path.join(schemaOutAbsolutePath, `${schema.segmentName || ROOT_SEGMENT_SCHEMA_NAME}.json`);
7
- if (skipIfExists) {
8
+ if (skipIfExists && (await getFileSystemEntryType(segmentPath))) {
8
9
  try {
9
10
  await fs.stat(segmentPath);
10
11
  return { isCreated: false, diffResult: null };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vovk-cli",
3
- "version": "0.0.1-draft.1",
3
+ "version": "0.0.1-draft.10",
4
4
  "bin": {
5
5
  "vovk": "./dist/index.mjs"
6
6
  },
@@ -11,8 +11,9 @@
11
11
  "scripts": {
12
12
  "build": "rm -rf dist tsconfig.build.tsbuildinfo && tsc -P tsconfig.build.json",
13
13
  "build-test": "rm -rf test_dist && tsc -P tsconfig.test.json",
14
- "test": "npm run build && chmod +x ./dist/index.mjs && npm run build-test && node --test --test-only test_dist/test/**/*.mjs",
15
- "test_": "npm run build && chmod +x ./dist/index.mjs && NODE_OPTIONS=\"--loader ts-node/esm\" TS_NODE_PROJECT=./tsconfig.test.json node --test --test-only test/**.mts",
14
+ "pre-test": "npm run build && chmod +x ./dist/index.mjs && npm run build-test",
15
+ "test-only": "npm run pre-test && node --test --test-only test_dist/test/**/*.mjs",
16
+ "test": "npm run pre-test && node --test --test-concurrency=1 test_dist/test/**/*.mjs",
16
17
  "ncu": "npm-check-updates -u",
17
18
  "npm-publish": "npm publish"
18
19
  },
@@ -33,10 +34,10 @@
33
34
  },
34
35
  "homepage": "https://vovk.dev",
35
36
  "peerDependencies": {
36
- "vovk": "^3.0.0-draft.1"
37
+ "vovk": "^3.0.0-draft.12"
37
38
  },
38
39
  "dependencies": {
39
- "@inquirer/prompts": "^7.0.0",
40
+ "@inquirer/prompts": "^7.0.1",
40
41
  "@npmcli/package-json": "^6.0.1",
41
42
  "chalk": "^5.3.0",
42
43
  "chokidar": "^4.0.1",
@@ -1,50 +1,51 @@
1
1
  <% var modulePascalName = _.upperFirst(_.camelCase(moduleName)); %>
2
2
  <% var modulePascalNamePlural = pluralize(modulePascalName); %>
3
- <% var ControllerName = modulePascalName + 'Controller'; %>
4
- <% var RPCName = modulePascalName + 'RPC'; %>
5
- <% var ServiceName = modulePascalName + 'Service'; %>
3
+ <% var controllerName = modulePascalName + 'Controller'; %>
4
+ <% var compiledName = modulePascalName + 'RPC'; %>
5
+ <% var serviceName = modulePascalName + 'Service'; %>
6
6
  ---
7
- filePath: <%= getModulePath(segmentName, moduleName, ControllerName + '.ts') %> # Relative to "modules" dir
8
- sourceName: <%= ControllerName %> # Used to define import declaration in a segment route file for the given class
9
- compiledName: <%= RPCName %> # Used to define a compiled object name
7
+ dirName: <%= getModuleDirName(segmentName, moduleName) %>
8
+ fileName: <%= controllerName + '.ts' %>
9
+ sourceName: <%= controllerName %>
10
+ compiledName: <%= compiledName %>
10
11
  ---
11
12
 
12
- import { prefix, get, put, post, del<%= !config.validationLibrary ? ', type VovkRequest' : '' %> } from 'vovk';
13
+ import { prefix, get, put, post, del, type VovkRequest } from 'vovk';
13
14
  <% if(withService) { %>
14
- import <%= ServiceName %> from './<%= ServiceName %>';
15
+ import <%= serviceName %> from './<%= serviceName %>';
15
16
  <% } %>
16
17
 
17
18
  @prefix('<%= _.kebabCase(moduleName).toLowerCase() %>')
18
- export default class <%= ControllerName %> {
19
+ export default class <%= controllerName %> {
19
20
  @get()
20
21
  static get<%= modulePascalNamePlural %> = async (req: VovkRequest<null, { q: string }>) => {
21
22
  const q = req.nextUrl.searchParams.get('q');
22
23
  <% if(withService) { %>
23
- return <%= ServiceName %>.getMyThingsExample(q);
24
+ return <%= serviceName %>.get<%= modulePascalNamePlural %>(q);
24
25
  <% } else { %>
25
26
  return { q };
26
27
  <% } %>
27
28
  }
28
29
 
29
30
  @put(':id')
30
- static update<%= modulePascalNamePlural %> = async (req: VovkRequest<{ foo: 'bar' | 'baz' }, { q: string }>, params: { id: string }) => {
31
+ static update<%= modulePascalName %> = async (req: VovkRequest<{ foo: 'bar' | 'baz' }, { q: string }>, params: { id: string }) => {
31
32
  const { id } = params;
32
33
  const body = await req.json();
33
34
  const q = req.nextUrl.searchParams.get('q');
34
35
  <% if(withService) { %>
35
- return MyThingService.updateMyThingExample(id, q, body);
36
+ return MyThingService.update<%= modulePascalName %>(id, q, body);
36
37
  <% } else { %>
37
38
  return { id, body, q };
38
39
  <% } %>
39
40
  };
40
41
 
41
42
  @post()
42
- static create<%= modulePascalNamePlural %> = () => {
43
+ static create<%= modulePascalName %> = () => {
43
44
  // ...
44
45
  };
45
46
 
46
47
  @del(':id')
47
- static delete<%= modulePascalNamePlural %> = () => {
48
+ static delete<%= modulePascalName %> = () => {
48
49
  // ...
49
50
  };
50
51
  }
@@ -1,7 +1,27 @@
1
- <% var ServiceName = _.upperFirst(_.camelCase(moduleName) + 'Service'; %>
2
-
1
+ <% var modulePascalName = _.upperFirst(_.camelCase(moduleName)); %>
2
+ <% var modulePascalNamePlural = pluralize(modulePascalName); %>
3
+ <% var serviceName = modulePascalName + 'Service'; %>
4
+ <% var controllerName = modulePascalName + 'Controller'; %>
3
5
  ---
4
- filePath: <%= getModulePath(segmentName, moduleName, ServiceName + '.ts') %> # Relative to "modules" dir
6
+ dirName: <%= getModuleDirName(segmentName, moduleName) %>
7
+ fileName: <%= serviceName + '.ts' %>
5
8
  ---
6
9
 
7
- // TO DO: Implement <%= ServiceName %>
10
+ import type { VovkControllerBody, VovkControllerQuery } from 'vovk';
11
+ import type <%= controllerName %> from './<%= controllerName %>';
12
+
13
+ export default class <%= serviceName %> {
14
+ static get<%= modulePascalNamePlural %> = (q: VovkControllerQuery<typeof <%= controllerName %>.get<%= modulePascalNamePlural %>>['q']) => {
15
+ return [];
16
+ };
17
+
18
+ static update<%= modulePascalName %> = (
19
+ id: string,
20
+ q: VovkControllerQuery<typeof <%= controllerName %>.get<%= modulePascalName %>>['q'],
21
+ body: VovkControllerBody<typeof <%= controllerName %>.get<%= modulePascalName %>>
22
+ ) => {
23
+ return { id, q, body };
24
+ };
25
+
26
+ // ...
27
+ }
@@ -1 +1,24 @@
1
- // TODO
1
+ <% var modulePascalName = _.upperFirst(_.camelCase(moduleName)); %>
2
+ <% var modulePascalNamePlural = pluralize(modulePascalName); %>
3
+ <% var workerName = modulePascalName + 'Worker'; %>
4
+ <% var compiledName = modulePascalName + 'WPC'; %>
5
+ ---
6
+ dirName: <%= getModuleDirName(segmentName, moduleName) %>
7
+ fileName: <%= controllerName + '.ts' %>
8
+ sourceName: <%= workerName %>
9
+ compiledName: <%= compiledName %>
10
+ ---
11
+ import { worker } from 'vovk';
12
+
13
+ @worker()
14
+ export default class <%= workerName %> {
15
+ static heavyComputation = () => {
16
+ return 'Hello, World';
17
+ };
18
+
19
+ static heavyComputationYield = function* () {
20
+ for(const str of ['Hello', 'World'] as const) {
21
+ yield str;
22
+ }
23
+ };
24
+ }
@@ -1 +0,0 @@
1
- export default function directoryExists(dir: string): Promise<boolean>;
@@ -1,10 +0,0 @@
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,32 +0,0 @@
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
- }
@@ -1,34 +0,0 @@
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
- }
@@ -1,18 +0,0 @@
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
- }
@@ -1,85 +0,0 @@
1
- <% var modulePascalName = _.upperFirst(_.camelCase(moduleName)); %>
2
- <% var modulePascalNamePlural = pluralize(modulePascalName); %>
3
- <% var ControllerName = modulePascalName + 'Controller'; %>
4
- <% var RPCName = modulePascalName + 'RPC'; %>
5
- <% var ServiceName = modulePascalName + 'Service'; %>
6
- ---
7
- # Relative to modules dir
8
- fileName: <%= getFileDir(segmentName, moduleName) + ControllerName + '.ts' %>
9
- className: <%= ControllerName %> # Used to define a controller in a segment
10
- rpcName: <%= RPCName %> # Used to define an exported RPC class in a segment
11
- ---
12
-
13
- import { prefix, get, put, post, del<%= !config.validationLibrary ? ', type VovkRequest' : '' %> } from 'vovk';
14
- <% if(withService) { %>
15
- import <%= ServiceName %> from './<%= ServiceName %>';
16
- <% } %>
17
- <% if(config.validationLibrary === 'vovk-zod') { %>
18
- import { withZod } from 'vovk-zod';
19
- import { z } from 'zod';
20
- <% } %>
21
-
22
- @prefix('<%= _.kebabCase(moduleName).toLowerCase() %>')
23
- export default class <%= ControllerName %> {
24
- @get()
25
- <% if(config.validationLibrary === 'vovk-zod') { %>
26
- async get<%= modulePascalNamePlural %> = withZod(null, z.object({ q: z.string() }), (req) => {
27
- const q = req.nextUrl.searchParams.get('q');
28
- <% if(withService) { %>
29
- return <%= ServiceName %>.getMyThingsExample(q);
30
- <% } else { %>
31
- return { q };
32
- <% } %>
33
- });
34
- <% } else { %>
35
- static get<%= modulePascalNamePlural %> = async (req: VovkRequest<null, { q: string }>) => {
36
- const q = req.nextUrl.searchParams.get('q');
37
- <% if(withService) { %>
38
- return <%= ServiceName %>.getMyThingsExample(q);
39
- <% } else { %>
40
- return { q };
41
- <% } %>
42
- }
43
- <% } %>
44
-
45
- @put(':id')
46
- <% if(config.validationLibrary === 'vovk-zod') { %>
47
- static update<%= modulePascalNamePlural %> = withZod(
48
- z.object({
49
- foo: z.union([z.literal('bar'), z.literal('baz')]),
50
- }),
51
- z.object({ q: z.string() }),
52
- async (req, params: { id: string }) => {
53
- const { id } = params;
54
- const body = await req.json();
55
- const q = req.nextUrl.searchParams.get('q');
56
- <% if(withService) { %>
57
- return MyThingService.updateMyThingExample(id, q, body);
58
- <% } else { %>
59
- return { id, body, q };
60
- <% } %>
61
- }
62
- );
63
- <% } else { %>
64
- static update<%= modulePascalNamePlural %> = async (req: VovkRequest<{ foo: 'bar' | 'baz' }, { q: string }>, params: { id: string }) => {
65
- const { id } = params;
66
- const body = await req.json();
67
- const q = req.nextUrl.searchParams.get('q');
68
- <% if(withService) { %>
69
- return MyThingService.updateMyThingExample(id, q, body);
70
- <% } else { %>
71
- return { id, body, q };
72
- <% } %>
73
- };
74
- <% } %>
75
-
76
- @post()
77
- static create<%= modulePascalNamePlural %> = () => {
78
- // ...
79
- };
80
-
81
- @del(':id')
82
- static delete<%= modulePascalNamePlural %> = () => {
83
- // ...
84
- };
85
- }
@@ -1,9 +0,0 @@
1
- <% var modulePascalName = _.upperFirst(_.camelCase(moduleName); %>
2
- <% var ServiceName = modulePascalName + 'Service'; %>
3
-
4
- ---
5
- # Relative to modules dir
6
- fileName: <%= getFileDir(segmentName, moduleName) + ServiceName + '.ts' %>
7
- ---
8
-
9
- // TO DO: Implement <%= ServiceName %>
@@ -1,9 +0,0 @@
1
- <% var modulePascalName = _.upperFirst(_.camelCase(moduleName); %>
2
- <% var WorkerName = modulePascalName + 'Worker'; %>
3
-
4
- ---
5
- # Relative to modules dir
6
- fileName: <%= getFileDir(segmentName, moduleName) + WorkerName + '.ts' %>
7
- ---
8
-
9
- // TO DO: Implement <%= WorkerName %>
@@ -1,32 +0,0 @@
1
- import { prefix, get, put, post, del, type VovkRequest } from 'vovk';
2
-
3
- @prefix('my-thing')
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
- }
@@ -1,39 +0,0 @@
1
- import { prefix, get, put, post, del } from 'vovk';
2
- import { z } from 'zod';
3
- import { withZod } from 'vovk-zod';
4
- import MyThingService from './MyThingService.s.template';
5
-
6
- @prefix('my-thing')
7
- export default class MyThingController {
8
- @get()
9
- static getMyThingsExample = withZod(null, z.object({ q: z.string() }), (req) => {
10
- const q = req.nextUrl.searchParams.get('q');
11
-
12
- return MyThingService.getMyThingsExample(q);
13
- });
14
-
15
- @put(':id')
16
- static updateMyThingExample = withZod(
17
- z.object({
18
- foo: z.union([z.literal('bar'), z.literal('baz')]),
19
- }),
20
- z.object({ q: z.string() }),
21
- async (req, params: { id: string }) => {
22
- const { id } = params;
23
- const body = await req.json();
24
- const q = req.nextUrl.searchParams.get('q');
25
-
26
- return MyThingService.updateMyThingExample(id, q, body);
27
- }
28
- );
29
-
30
- @post()
31
- static async createMyThingExample() {
32
- // ...
33
- }
34
-
35
- @del(':id')
36
- static deleteMyThingExample() {
37
- // ...
38
- }
39
- }
@@ -1,18 +0,0 @@
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
- }