nest-combo 1.0.2 → 1.1.1

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.
package/bin/cli.js CHANGED
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import { execSync } from "child_process";
3
+ import fs from "fs";
4
+ import path from "path";
2
5
  import chalk from "chalk";
6
+ import yaml from "js-yaml";
3
7
  import {
4
8
  generateModule,
5
9
  generateController,
@@ -7,9 +11,13 @@ import {
7
11
  generateInterceptor,
8
12
  generateGateway,
9
13
  generateMiddlware,
14
+ generateProject,
10
15
  } from "../lib/generate.js";
16
+ import { installDependencies } from "../lib/install.js";
11
17
  import { createRequire } from "module";
18
+ import { validateYml } from "../lib/utils.js";
12
19
  const require = createRequire(import.meta.url);
20
+ const { name, version } = require("../package.json");
13
21
 
14
22
  const args = process.argv.slice(2);
15
23
  const resources = [
@@ -52,20 +60,40 @@ const resources = [
52
60
  ];
53
61
 
54
62
  function main() {
55
- const { askForHelp, askForVersion, moduleName, hasDryRun, hasNoSpec } =
56
- getArgs();
63
+ printBanner();
64
+ const {
65
+ askForHelp,
66
+ askForVersion,
67
+ moduleName,
68
+ hasDryRun,
69
+ hasNoSpec,
70
+ hasYmlFile,
71
+ } = getArgs();
57
72
 
58
- if (askForVersion) {
59
- showVersion();
60
- process.exit(0);
61
- }
62
- if (askForHelp) {
63
- showUsage();
64
- process.exit(0);
73
+ switch (true) {
74
+ case askForVersion:
75
+ showVersion();
76
+ process.exit(0);
77
+ break;
78
+
79
+ case askForHelp:
80
+ showUsage();
81
+ process.exit(0);
82
+ break;
83
+
84
+ case hasYmlFile:
85
+ const file = args[1];
86
+ loadFromYml(file);
87
+ process.exit(0);
88
+ break;
89
+
90
+ default:
91
+ execute(moduleName, hasNoSpec, hasDryRun);
65
92
  }
93
+ }
66
94
 
95
+ function execute(moduleName, hasNoSpec, hasDryRun) {
67
96
  validate(moduleName);
68
-
69
97
  resources.forEach((component) => {
70
98
  if (args.includes(component.flag) || args.includes(component.longFlag)) {
71
99
  console.log(
@@ -95,6 +123,9 @@ function getArgs() {
95
123
  const askForHelp = args.includes("-h") || args.includes("--help");
96
124
  const hasNoSpec = args.includes("-ns") || args.includes("--no-spec");
97
125
  const hasDryRun = args.includes("-dr") || args.includes("--dry-run");
126
+ const hasYmlFile = args.includes("-f") || args.includes("--file");
127
+ const validateYmlFile =
128
+ args.includes("-vf") || args.includes("--validate-yml");
98
129
 
99
130
  return {
100
131
  moduleName,
@@ -102,6 +133,8 @@ function getArgs() {
102
133
  askForHelp,
103
134
  hasNoSpec,
104
135
  hasDryRun,
136
+ hasYmlFile,
137
+ validateYmlFile,
105
138
  };
106
139
  }
107
140
 
@@ -110,12 +143,14 @@ function showUsage() {
110
143
  ${chalk.yellow("Usage:")} nest-combo <module-name> [options]
111
144
 
112
145
  ${chalk.yellow("Options:")}
113
- ${chalk.cyan("-m, --module")} Generate a Module
114
- ${chalk.cyan("-c, --controller")} Generate a Controller
115
- ${chalk.cyan("-s, --service")} Generate a Service
116
- ${chalk.cyan("-g, --gateway")} Generate a Gateway
117
- ${chalk.cyan("-mw, --middleware")} Generate Middleware
118
- ${chalk.cyan("-itc, --interceptor")} Generate an Interceptor
146
+ ${chalk.cyan("-m, --module")} Generate a Module
147
+ ${chalk.cyan("-c, --controller")} Generate a Controller
148
+ ${chalk.cyan("-s, --service")} Generate a Service
149
+ ${chalk.cyan("-g, --gateway")} Generate a Gateway
150
+ ${chalk.cyan("-mw, --middleware")} Generate Middleware
151
+ ${chalk.cyan("-itc, --interceptor")} Generate an Interceptor
152
+ ${chalk.cyan("-f, --file")} Generate project from yml file
153
+ ${chalk.cyan("-vf, --validate-yml")} Validate Yml File
119
154
 
120
155
  ${chalk.bgMagenta("Optional:")}
121
156
  ${chalk.cyan("-ns, --no-spec")} Do not generate spec (test) files
@@ -125,11 +160,11 @@ ${chalk.bgMagenta("Optional:")}
125
160
 
126
161
  ${chalk.yellow("Example:")}
127
162
  nest-combo users -m -c -s
163
+ nest-combo -f project.yml
128
164
  `);
129
165
  }
130
166
 
131
167
  function showVersion() {
132
- const { name, version } = require("../package.json");
133
168
  console.log(chalk.green(`${name} - version: ${version}`));
134
169
  }
135
170
 
@@ -156,4 +191,118 @@ function validate(moduleName) {
156
191
  }
157
192
  }
158
193
 
194
+ function loadFromYml(file) {
195
+ try {
196
+ const fileContent = fs.readFileSync(file, "utf-8");
197
+ const ymlData = yaml.load(fileContent);
198
+
199
+ validateYml(ymlData);
200
+
201
+ const content = ymlData["nest-combo"];
202
+ const projectName = content["project-name"];
203
+ const packageManager = content["package-manager"] || "npm";
204
+ const dependencies = Array.isArray(content["dependencies"])
205
+ ? content["dependencies"]
206
+ : [];
207
+ const modules = Array.isArray(content["modules"]) ? content["modules"] : [];
208
+
209
+ const openVsCode = content["open-vscode"];
210
+
211
+ console.log(chalk.green(`Generating project: ${projectName}`));
212
+ generateProject(projectName, [`-p ${packageManager}`]);
213
+
214
+ if (dependencies.length > 0) {
215
+ console.log(chalk.blue("Installing dependencies..."));
216
+ installDependencies(projectName, dependencies);
217
+ } else {
218
+ console.log(chalk.yellow("No dependencies to install."));
219
+ }
220
+
221
+ const recursiveAddModule = (parentModule, modules) => {
222
+ for (const genModule of modules) {
223
+ const {
224
+ name,
225
+ resources: moduleResources,
226
+ options,
227
+ modules: subModules,
228
+ } = genModule;
229
+
230
+ const pathModule = parentModule ? `${parentModule}/${name}` : name;
231
+
232
+ console.log(chalk.cyan(`Processing module: ${pathModule}`));
233
+
234
+ if (!Array.isArray(moduleResources)) {
235
+ console.warn(
236
+ chalk.yellow(
237
+ `Invalid resources for module: ${pathModule}. Skipping.`
238
+ )
239
+ );
240
+ continue;
241
+ }
242
+
243
+ moduleResources.forEach((resourceName) => {
244
+ const resource = resources.find((r) => r.name === resourceName);
245
+
246
+ if (!resource) {
247
+ console.warn(
248
+ chalk.yellow(`Unknown resource type: ${resourceName}. Skipping.`)
249
+ );
250
+ return;
251
+ }
252
+
253
+ console.log(
254
+ chalk.magenta(`Generating ${resourceName} for ${pathModule}`)
255
+ );
256
+ resource.generator(pathModule, options, projectName);
257
+ console.log(
258
+ chalk.green("---------------------------------------------------")
259
+ );
260
+ });
261
+
262
+ if (subModules && subModules.length > 0) {
263
+ recursiveAddModule(pathModule, subModules);
264
+ }
265
+ }
266
+ };
267
+
268
+ recursiveAddModule(null, modules);
269
+
270
+ if (openVsCode) {
271
+ const workingDirectory = path.join(process.cwd(), projectName);
272
+ const command = "code .";
273
+ execSync(command, { cwd: workingDirectory });
274
+ }
275
+
276
+ console.log(
277
+ chalk.green(
278
+ "**************** succefully finished ************************** "
279
+ )
280
+ );
281
+ showVersion();
282
+ } catch (error) {
283
+ console.error(chalk.red(`Error: ${error.message}`));
284
+ process.exit(1);
285
+ }
286
+ }
287
+
288
+ function printBanner() {
289
+ console.log(
290
+ chalk.magenta(`
291
+ ███╗ ██╗███████╗███████╗████████╗ ██████╗ ██████╗ ███╗ ███╗██████╗ ██████╗
292
+ ████╗ ██║██╔════╝██╔════╝╚══██╔══╝ ██╔════╝██╔═══██╗████╗ ████║██╔══██╗██╔═══██╗
293
+ ██╔██╗ ██║█████╗ ███████╗ ██║ █████╗██║ ██║ ██║██╔████╔██║██████╔╝██║ ██║
294
+ ██║╚██╗██║██╔══╝ ╚════██║ ██║ ╚════╝██║ ██║ ██║██║╚██╔╝██║██╔══██╗██║ ██║
295
+ ██║ ╚████║███████╗███████║ ██║ ╚██████╗╚██████╔╝██║ ╚═╝ ██║██████╔╝╚██████╔╝
296
+ ╚═╝ ╚═══╝╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═════╝
297
+ `)
298
+ );
299
+ console.log(
300
+ chalk.bgWhite(
301
+ chalk.bold(
302
+ `********************************************************************** version: ${version} `
303
+ )
304
+ )
305
+ );
306
+ }
307
+
159
308
  main();
package/lib/generate.js CHANGED
@@ -21,34 +21,42 @@ const getNestBinary = () => {
21
21
  }
22
22
  };
23
23
 
24
+ const nest = getNestBinary();
25
+
24
26
  /**
25
27
  * Generates a NestJS component (module, controller, service, etc.).
26
28
  * @param {string} type - The type of component to generate (e.g., "mo", "co", "s").
27
- * @param {string} moduleName - The name of the module to generate.
29
+ * @param {string} resourceName - The name of the resource to generate.
28
30
  * @param {Object} options - Additional options for the command.
29
31
  */
30
- const generateComponent = (type, moduleName, options) => {
31
- const command = `${nest} g ${type} ${moduleName} ${options.join(" ")}`;
32
- // console.log(`Running command: ${command}`);
33
- execSync(command, { stdio: "inherit" });
34
- };
32
+ const generateComponent = (type, resourceName, options, projectName) => {
33
+ const workingDirectory = projectName
34
+ ? path.join(process.cwd(), projectName)
35
+ : process.cwd();
35
36
 
36
- export const generateModule = (moduleName, options) =>
37
- generateComponent("mo", moduleName, options);
37
+ const command = `${nest} ${type} ${resourceName} ${
38
+ Array.isArray(options) ? options.join(" ") : ""
39
+ }`;
38
40
 
39
- export const generateController = (moduleName, options) =>
40
- generateComponent("co", moduleName, options);
41
+ execSync(command, { stdio: "inherit", cwd: workingDirectory });
42
+ };
43
+ export const generateProject = (projectName, options) =>
44
+ generateComponent("new", projectName, options);
41
45
 
42
- export const generateService = (moduleName, options) =>
43
- generateComponent("s", moduleName, options);
46
+ export const generateModule = (resourceName, options, projectName) =>
47
+ generateComponent("g mo", resourceName, options, projectName);
44
48
 
45
- export const generateGateway = (moduleName, options) =>
46
- generateComponent("ga", moduleName, options);
49
+ export const generateController = (resourceName, options, projectName) =>
50
+ generateComponent("g co", resourceName, options, projectName);
47
51
 
48
- export const generateInterceptor = (moduleName, options) =>
49
- generateComponent("itc", moduleName, options);
52
+ export const generateService = (resourceName, options, projectName) =>
53
+ generateComponent("g s", resourceName, options, projectName);
50
54
 
51
- export const generateMiddlware = (moduleName, options) =>
52
- generateComponent("mi", moduleName, options);
55
+ export const generateGateway = (resourceName, options, projectName) =>
56
+ generateComponent("g ga", resourceName, options, projectName);
53
57
 
54
- const nest = getNestBinary();
58
+ export const generateInterceptor = (resourceName, options, projectName) =>
59
+ generateComponent("g itc", resourceName, options, projectName);
60
+
61
+ export const generateMiddlware = (resourceName, options, projectName) =>
62
+ generateComponent("g mi", resourceName, options, projectName);
package/lib/install.js ADDED
@@ -0,0 +1,16 @@
1
+ import { execSync } from "child_process";
2
+ import path from "path";
3
+ function installDependencies(projectName, dependencies) {
4
+ const cdCommand = `cd ${projectName}`;
5
+ const workingDirectory = path.join(process.cwd(), projectName);
6
+
7
+ execSync(cdCommand, { stdio: "inherit" });
8
+ if (Array.isArray(dependencies)) {
9
+ const command = `npm i ${dependencies
10
+ .map((dependency) => dependency.name)
11
+ .join(" ")}`;
12
+ execSync(command, { stdio: "inherit", cwd: workingDirectory });
13
+ }
14
+ }
15
+
16
+ export { installDependencies };
package/lib/utils.js ADDED
@@ -0,0 +1,107 @@
1
+ import chalk from "chalk";
2
+ /**
3
+ * Validate Yml File
4
+ * @param {Object} ymlData
5
+ */
6
+ export function validateYml(ymlData) {
7
+ try {
8
+ if (!ymlData || typeof ymlData !== "object" || !ymlData["nest-combo"]) {
9
+ throw new Error("Invalid YAML file: Missing 'nest-combo' key.");
10
+ }
11
+
12
+ const nestCombo = ymlData["nest-combo"];
13
+
14
+ if (
15
+ !nestCombo["project-name"] ||
16
+ typeof nestCombo["project-name"] !== "string"
17
+ ) {
18
+ throw new Error("Missing or invalid 'project-name'. Must be a string.");
19
+ }
20
+
21
+ if (
22
+ nestCombo["package-manager"] &&
23
+ typeof nestCombo["package-manager"] !== "string"
24
+ ) {
25
+ throw new Error("Invalid 'package-manager'. Must be a string.");
26
+ }
27
+
28
+ if (nestCombo.dependencies && !Array.isArray(nestCombo.dependencies)) {
29
+ throw new Error("Invalid 'dependencies'. Must be an array.");
30
+ }
31
+ if (nestCombo.dependencies) {
32
+ nestCombo.dependencies.forEach((dep, index) => {
33
+ if (!dep.name || typeof dep.name !== "string") {
34
+ throw new Error(
35
+ `Invalid dependency at index ${index}. Each dependency must have a 'name' field as a string.`
36
+ );
37
+ }
38
+ });
39
+ }
40
+
41
+ if (nestCombo.modules && !Array.isArray(nestCombo.modules)) {
42
+ throw new Error("Invalid 'modules'. Must be an array.");
43
+ }
44
+ if (nestCombo.modules) {
45
+ validateModules(nestCombo.modules);
46
+ }
47
+
48
+ console.log(chalk.green("YAML file is valid."));
49
+ } catch (error) {
50
+ console.error(chalk.red(`Validation Error: ${error.message}`));
51
+ process.exit(1);
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Recursively validate modules and sub-modules.
57
+ * @param {Array} modules - The list of modules to validate.
58
+ */
59
+ function validateModules(modules) {
60
+ modules.forEach((module, index) => {
61
+ if (!module.name || typeof module.name !== "string") {
62
+ throw new Error(
63
+ `Invalid module at index ${index}. Each module must have a 'name' field as a string.`
64
+ );
65
+ }
66
+
67
+ if (module.resources && !Array.isArray(module.resources)) {
68
+ throw new Error(
69
+ `Invalid 'resources' for module '${module.name}'. Must be an array.`
70
+ );
71
+ }
72
+ if (module.resources) {
73
+ const validResources = [
74
+ "module",
75
+ "controller",
76
+ "service",
77
+ "gateway",
78
+ "middleware",
79
+ "interceptor",
80
+ ];
81
+ module.resources.forEach((resource, resIndex) => {
82
+ if (!validResources.includes(resource)) {
83
+ throw new Error(
84
+ `Invalid resource '${resource}' at index ${resIndex} for module '${
85
+ module.name
86
+ }'. Valid resources are: ${validResources.join(", ")}.`
87
+ );
88
+ }
89
+ });
90
+ }
91
+
92
+ if (module.options && !Array.isArray(module.options)) {
93
+ throw new Error(
94
+ `Invalid 'options' for module '${module.name}'. Must be an array.`
95
+ );
96
+ }
97
+
98
+ if (module.modules && !Array.isArray(module.modules)) {
99
+ throw new Error(
100
+ `Invalid 'modules' for module '${module.name}'. Must be an array.`
101
+ );
102
+ }
103
+ if (module.modules) {
104
+ validateModules(module.modules);
105
+ }
106
+ });
107
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nest-combo",
3
- "version": "1.0.2",
3
+ "version": "1.1.1",
4
4
  "main": "lib/generate.js",
5
5
  "description": "A CLI tool to generate NestJS modules, controllers, and services, etc with single line commands.",
6
6
  "type": "module",
@@ -25,6 +25,11 @@
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
27
  "@nestjs/cli": "^11.0.5",
28
- "chalk": "^5.4.1"
28
+ "chalk": "^5.4.1",
29
+ "js-yaml": "^4.1.0"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/vinisalves/nest-combo.git"
29
34
  }
30
35
  }
@@ -0,0 +1,48 @@
1
+ #YML example for loading a full project from scracth
2
+ nest-combo:
3
+ project-name: my-new-project
4
+ open-vscode: true # open vscode when process is finished
5
+ package-manager: npm # npm | yarn | pnmp
6
+ dependencies:
7
+ - name: "@nestjs/config"
8
+ - name: "@nestjs/bull"
9
+ - name: "class-transformer"
10
+ - name: "class-validator"
11
+ - name: "nestjs-twilio"
12
+ modules:
13
+ - name: core
14
+ resources:
15
+ - module
16
+ modules:
17
+ - name: user
18
+ resources:
19
+ - module
20
+ - controller
21
+ - service
22
+ options:
23
+ - --no-spec
24
+ modules:
25
+ - name: subUsers
26
+ resources:
27
+ - module
28
+ - controller
29
+ - service
30
+ options:
31
+ - --no-spec
32
+
33
+ - name: auth
34
+ resources:
35
+ - module
36
+ - controller
37
+ - service
38
+ - interceptor
39
+ - name: product
40
+ resources:
41
+ - module
42
+ - controller
43
+ - service
44
+ - name: payment
45
+ resources:
46
+ - module
47
+ - controller
48
+ - service