vovk-cli 0.0.1-draft.3 → 0.0.1-draft.5

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.
@@ -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
+ dirName?: string;
26
+ fileName?: string;
27
+ overwrite?: 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', 'Override config template')
100
+ .option('--dir-name', 'Override dirName in template file')
101
+ .option('--file-name', 'Override fileName in template file')
100
102
  .option('--dry-run', 'Do not write files to disk')
101
103
  .action((components, options) => newComponents(components, options));
102
104
  program
@@ -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, fileName, dirName, template, overwrite }: 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, fileName, dirName, template, overwrite }) {
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
+ fileName,
30
+ dirName,
31
+ template,
32
+ overwrite,
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, fileName: fileNameFlag, dirName: dirNameFlag, template: templateFlag, overwrite, }: {
2
2
  what: string[];
3
3
  moduleNameWithOptionalSegment: string;
4
- dryRun: boolean;
4
+ dryRun?: boolean;
5
+ fileName?: string;
6
+ dirName?: string;
7
+ template?: string;
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, fileName: fileNameFlag, dirName: dirNameFlag, template: templateFlag, overwrite, }) {
22
22
  const { config, log, apiDir, cwd } = await getProjectInfo();
23
23
  const templates = config.templates;
24
24
  const [segmentName, moduleName] = splitByLast(moduleNameWithOptionalSegment);
@@ -47,33 +47,42 @@ export default async function newModule({ what, moduleNameWithOptionalSegment, d
47
47
  throw new Error(`Segment ${segmentName} not found`);
48
48
  }
49
49
  for (const type of what) {
50
- const templatePath = templates[type];
50
+ const templatePath = templateFlag ?? templates[type];
51
51
  const templateAbsolutePath = templatePath.startsWith('/') || templatePath.startsWith('.')
52
52
  ? path.resolve(cwd, templatePath)
53
53
  : path.resolve(cwd, './node_modules', templatePath);
54
54
  const templateCode = await fs.readFile(templateAbsolutePath, 'utf-8');
55
- const { filePath, sourceName, compiledName, code } = await render(templateCode, {
55
+ const { dirName: renderedDirName, fileName: renderedFileName, sourceName, compiledName, code, } = await render(templateCode, {
56
+ cwd,
56
57
  config,
57
58
  withService: what.includes('service'),
58
59
  segmentName,
59
60
  moduleName,
60
61
  });
61
- const absoluteModulePath = path.join(cwd, config.modulesDir, filePath);
62
- const dirName = path.dirname(absoluteModulePath);
62
+ const fileName = fileNameFlag || renderedFileName;
63
+ const dirName = dirNameFlag || renderedDirName;
64
+ const absoluteModuleDir = path.join(cwd, config.modulesDir, dirName);
65
+ const absoluteModulePath = path.join(absoluteModuleDir, fileName);
63
66
  const prettiedCode = await prettify(code, absoluteModulePath);
64
67
  if (!dryRun) {
65
- if (await getFileSystemEntryType(absoluteModulePath)) {
68
+ if (!overwrite && (await getFileSystemEntryType(absoluteModulePath))) {
66
69
  log.warn(`File ${chalkHighlightThing(absoluteModulePath)} already exists, skipping this "${type}"`);
67
70
  }
68
71
  else {
69
- await fs.mkdir(dirName, { recursive: true });
72
+ await fs.mkdir(absoluteModuleDir, { recursive: true });
70
73
  await fs.writeFile(absoluteModulePath, prettiedCode);
71
74
  }
72
75
  }
73
76
  if (type === 'controller' || type === 'worker') {
77
+ if (!sourceName) {
78
+ throw new Error('sourceName is required');
79
+ }
80
+ if (!compiledName) {
81
+ throw new Error('compiledName is required');
82
+ }
74
83
  const { routeFilePath } = segment;
75
84
  const segmentSourceCode = await fs.readFile(routeFilePath, 'utf-8');
76
- const importPath = path.relative(dirName, absoluteModulePath).replace(/\.(ts|tsx)$/, '');
85
+ const importPath = path.relative(absoluteModuleDir, absoluteModulePath).replace(/\.(ts|tsx)$/, '');
77
86
  const newSegmentCode = addClassToSegmentCode(segmentSourceCode, {
78
87
  sourceName,
79
88
  compiledName,
@@ -85,6 +94,6 @@ export default async function newModule({ what, moduleNameWithOptionalSegment, d
85
94
  }
86
95
  log.info(`Added ${chalkHighlightThing(sourceName)} ${type} to ${formatLoggedSegmentName(segmentName)} as ${chalkHighlightThing(compiledName)}`);
87
96
  }
88
- log.info(`Created ${chalkHighlightThing(sourceName)} with ${chalkHighlightThing(type)} template for ${formatLoggedSegmentName(segmentName)}`);
97
+ log.info(`Created ${chalkHighlightThing(fileName)} using "${chalkHighlightThing(type)}" template for ${formatLoggedSegmentName(segmentName)}`);
89
98
  }
90
99
  }
@@ -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';
@@ -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
  }
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
+ };
@@ -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) {
@@ -137,7 +136,6 @@ export class VovkCLIWatcher {
137
136
  this.#watchModules();
138
137
  this.#watchSegments();
139
138
  }, 1000);
140
- let ready = false;
141
139
  chokidar
142
140
  .watch(['vovk.config.{js,mjs,cjs}', '.config/vovk.config.{js,mjs,cjs}'], {
143
141
  persistent: true,
@@ -149,15 +147,13 @@ export class VovkCLIWatcher {
149
147
  .on('change', () => void handle())
150
148
  .on('unlink', () => void handle())
151
149
  .on('ready', () => {
152
- if (ready)
150
+ if (isReady)
153
151
  return;
154
152
  // for some reason this watcher triggers ready event twice
155
153
  log.debug('Config files watcher is ready');
156
- ready = true;
154
+ isReady = true;
157
155
  })
158
- .on('error', (error) => {
159
- log.error(`Error watching config files: ${error.message}`);
160
- });
156
+ .on('error', (error) => log.error(`Error watching config files: ${error?.message ?? 'Unknown error'}`));
161
157
  void handle();
162
158
  };
163
159
  #watch() {
@@ -280,13 +276,13 @@ export class VovkCLIWatcher {
280
276
  const schemaOutAbsolutePath = path.join(cwd, config.schemaOutDir);
281
277
  this.#segments = await locateSegments(apiDirAbsolutePath);
282
278
  await debouncedEnsureSchemaFiles(this.#projectInfo, schemaOutAbsolutePath, this.#segments.map((s) => s.segmentName));
283
- // 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
284
280
  setTimeout(() => {
285
281
  for (const { segmentName } of this.#segments) {
286
282
  void this.#requestSchema(segmentName);
287
283
  }
288
284
  this.#watch();
289
- }, 3000);
285
+ }, 5000);
290
286
  }
291
287
  }
292
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.3",
3
+ "version": "0.0.1-draft.5",
4
4
  "bin": {
5
5
  "vovk": "./dist/index.mjs"
6
6
  },
@@ -33,10 +33,10 @@
33
33
  },
34
34
  "homepage": "https://vovk.dev",
35
35
  "peerDependencies": {
36
- "vovk": "^3.0.0-draft.3"
36
+ "vovk": "^3.0.0-draft.4"
37
37
  },
38
38
  "dependencies": {
39
- "@inquirer/prompts": "^7.0.0",
39
+ "@inquirer/prompts": "^7.0.1",
40
40
  "@npmcli/package-json": "^6.0.1",
41
41
  "chalk": "^5.3.0",
42
42
  "chokidar": "^4.0.1",
@@ -1,38 +1,39 @@
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) %> # Relative to the root dir
8
+ fileName: <%= controllerName + '.ts' %>
9
+ sourceName: <%= controllerName %> # Used to define import declaration in a segment route file for the given class
10
+ compiledName: <%= compiledName %> # Used to define a compiled object name
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
  <% } %>
@@ -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) %> # Relative to the root dir
7
+ fileName: <%= serviceName + '.ts' %>
5
8
  ---
6
9
 
7
- // TO DO: Implement <%= ServiceName %>
10
+ import type { VovkControllerBody, VovkControllerQuery } from 'vovk';
11
+ import <%= 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<%= modulePascalNamePlural %>>['q'],
21
+ body: VovkControllerBody<typeof <%= controllerName %>.get<%= modulePascalNamePlural %>>
22
+ ) => {
23
+ return { id, q, body };
24
+ };
25
+
26
+ // ...
27
+ }
@@ -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
- }