zudojs-cli 1.2.0 → 2.0.0

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 (109) hide show
  1. package/README.md +35 -1
  2. package/dist/src/adapters/frontend/flutter.adapter.d.ts +9 -0
  3. package/dist/src/adapters/frontend/flutter.adapter.js +16 -1
  4. package/dist/src/bin/zudojs.js +16 -5
  5. package/dist/src/cliApplication/cliApplication.builtins.d.ts +4 -0
  6. package/dist/src/cliApplication/cliApplication.builtins.js +28 -9
  7. package/dist/src/cliApplication/cliApplication.core.d.ts +8 -0
  8. package/dist/src/cliApplication/cliApplication.core.js +45 -5
  9. package/dist/src/cliApplication/cliApplication.help.d.ts +17 -0
  10. package/dist/src/cliApplication/cliApplication.help.js +94 -0
  11. package/dist/src/cliApplication/cliApplication.logger.js +4 -4
  12. package/dist/src/cliCommand/cliCommand.validator.js +22 -0
  13. package/dist/src/cliConstant/cliConstant.value.d.ts +18 -12
  14. package/dist/src/cliConstant/cliConstant.value.js +25 -19
  15. package/dist/src/cliError/cliError.argument.js +2 -2
  16. package/dist/src/cliError/cliError.base.js +28 -5
  17. package/dist/src/cliError/cliError.command.js +5 -2
  18. package/dist/src/cliError/cliError.execution.js +3 -3
  19. package/dist/src/cliParser/cliParser.core.js +15 -6
  20. package/dist/src/cliParser/cliParser.helper.d.ts +11 -0
  21. package/dist/src/cliParser/cliParser.helper.js +19 -0
  22. package/dist/src/cliParser/cliParser.longOption.js +4 -4
  23. package/dist/src/cliParser/cliParser.shortOption.js +7 -4
  24. package/dist/src/cliVersion/cliVersion.update.js +5 -3
  25. package/dist/src/commands/add.command.js +17 -2
  26. package/dist/src/commands/commandError.helper.d.ts +15 -0
  27. package/dist/src/commands/commandError.helper.js +28 -0
  28. package/dist/src/commands/create.command.js +284 -46
  29. package/dist/src/commands/doctor.command.d.ts +8 -0
  30. package/dist/src/commands/doctor.command.js +38 -5
  31. package/dist/src/commands/generate.command.js +71 -32
  32. package/dist/src/constants/index.d.ts +40 -68
  33. package/dist/src/constants/index.js +73 -23
  34. package/dist/src/generators/frontend/frontendPipeline.js +16 -4
  35. package/dist/src/generators/integration/integrationGenerator.core.js +10 -6
  36. package/dist/src/generators/project/project.generator.js +5 -1
  37. package/dist/src/index.d.ts +4 -4
  38. package/dist/src/index.js +3 -3
  39. package/dist/src/manifest/index.d.ts +1 -0
  40. package/dist/src/manifest/index.js +1 -0
  41. package/dist/src/manifest/manifestFile.helper.d.ts +42 -0
  42. package/dist/src/manifest/manifestFile.helper.js +141 -0
  43. package/dist/src/manifest/manifestManager.core.d.ts +16 -0
  44. package/dist/src/manifest/manifestManager.core.js +65 -35
  45. package/dist/src/prompts/backend/api-style.prompt.js +2 -5
  46. package/dist/src/prompts/backend/backend-architecture.prompt.js +5 -22
  47. package/dist/src/prompts/backend/database.prompt.js +5 -23
  48. package/dist/src/prompts/backend/services.prompt.js +12 -10
  49. package/dist/src/prompts/cancel.prompt.d.ts +18 -0
  50. package/dist/src/prompts/cancel.prompt.js +26 -0
  51. package/dist/src/prompts/capabilities/capabilities.prompt.js +2 -5
  52. package/dist/src/prompts/frontend/framework.prompt.js +2 -5
  53. package/dist/src/prompts/frontend/frontend-architecture.prompt.js +2 -5
  54. package/dist/src/prompts/index.d.ts +1 -0
  55. package/dist/src/prompts/index.js +1 -0
  56. package/dist/src/prompts/project/confirmation.prompt.js +2 -5
  57. package/dist/src/prompts/project/project-name.prompt.js +6 -7
  58. package/dist/src/prompts/project/project-type.prompt.js +2 -5
  59. package/dist/src/prompts/workspace/package-manager.prompt.js +5 -12
  60. package/dist/src/registries/adapter/frontendAdapterRegistry.core.d.ts +6 -0
  61. package/dist/src/registries/adapter/frontendAdapterRegistry.core.js +12 -0
  62. package/dist/src/registries/adapter/packageManagerRegistry.core.d.ts +5 -0
  63. package/dist/src/registries/adapter/packageManagerRegistry.core.js +11 -0
  64. package/dist/src/registries/dependency/dependencyRegistry.core.d.ts +9 -0
  65. package/dist/src/registries/dependency/dependencyRegistry.core.js +16 -0
  66. package/dist/src/registries/generator/generatorRegistry.core.d.ts +8 -0
  67. package/dist/src/registries/generator/generatorRegistry.core.js +14 -0
  68. package/dist/src/resolvers/capability/capabilityResolver.core.d.ts +1 -1
  69. package/dist/src/resolvers/capability/capabilityResolver.core.js +0 -2
  70. package/dist/src/resolvers/layout/projectLayout.core.js +1 -6
  71. package/dist/src/resolvers/project.resolver.d.ts +9 -3
  72. package/dist/src/resolvers/project.resolver.js +11 -7
  73. package/dist/src/rollback/index.d.ts +1 -1
  74. package/dist/src/rollback/index.js +1 -1
  75. package/dist/src/rollback/rollbackManager.core.d.ts +19 -1
  76. package/dist/src/rollback/rollbackManager.core.js +21 -2
  77. package/dist/src/runners/package-manager/index.d.ts +1 -1
  78. package/dist/src/runners/package-manager/index.js +1 -1
  79. package/dist/src/runners/package-manager/packageManagerRunner.core.d.ts +13 -1
  80. package/dist/src/runners/package-manager/packageManagerRunner.core.js +19 -1
  81. package/dist/src/runners/process/processRunner.core.d.ts +11 -0
  82. package/dist/src/runners/process/processRunner.core.js +41 -6
  83. package/dist/src/runners/task/index.d.ts +1 -1
  84. package/dist/src/runners/task/taskRunner.core.d.ts +22 -1
  85. package/dist/src/runners/task/taskRunner.core.js +35 -6
  86. package/dist/src/scaffolders/framework.scaffolder.js +3 -0
  87. package/dist/src/scaffolders/scaffolder.helper.d.ts +10 -0
  88. package/dist/src/scaffolders/scaffolder.helper.js +25 -2
  89. package/dist/src/templates/microservice/index.d.ts +1 -1
  90. package/dist/src/templates/microservice/index.js +1 -1
  91. package/dist/src/templates/microservice/microservice.template.d.ts +8 -4
  92. package/dist/src/templates/microservice/microservice.template.js +30 -19
  93. package/dist/src/templates/modular-monolith/modularMonolith.template.js +27 -8
  94. package/dist/src/templates/monolith/monolith.template.js +11 -17
  95. package/dist/src/templates/shared/capability.template.d.ts +25 -0
  96. package/dist/src/templates/shared/capability.template.js +46 -0
  97. package/dist/src/templates/shared/index.d.ts +1 -0
  98. package/dist/src/templates/shared/index.js +1 -0
  99. package/dist/src/utils/utils.exec.d.ts +22 -0
  100. package/dist/src/utils/utils.exec.js +51 -4
  101. package/dist/src/utils/utils.fileSystem.js +46 -2
  102. package/dist/src/utils/utils.name.d.ts +9 -0
  103. package/dist/src/utils/utils.name.js +29 -3
  104. package/dist/src/validators/environment/environmentValidator.core.d.ts +11 -1
  105. package/dist/src/validators/environment/environmentValidator.core.js +40 -73
  106. package/dist/src/validators/project/index.d.ts +1 -1
  107. package/dist/src/validators/project/projectValidator.core.d.ts +11 -1
  108. package/dist/src/validators/project/projectValidator.core.js +20 -4
  109. package/package.json +6 -6
@@ -9,18 +9,32 @@ export function normalizeName(name) {
9
9
  .replace(/^-+/, "")
10
10
  .replace(/(?<!-)-+$/, "");
11
11
  }
12
+ /**
13
+ * Prefixes an underscore when the first character cannot start a TypeScript
14
+ * identifier.
15
+ *
16
+ * `normalizeName` keeps digits, so `2fa` came out of the converters as `2fa`
17
+ * and was emitted verbatim as a class name — a syntax error in the generated
18
+ * file, and in every barrel and `app.ts` that imports it. Generators reject
19
+ * such a name up front (see {@link assertGeneratableName}); this is the
20
+ * last-resort guarantee for the callers that do not, such as the project
21
+ * templates rendering `--services 2fa`.
22
+ */
23
+ function toIdentifier(value) {
24
+ return /^[0-9]/.test(value) ? `_${value}` : value;
25
+ }
12
26
  /** Converts an arbitrary name to PascalCase (safe as a TS identifier). */
13
27
  export function toPascalCase(name) {
14
- return normalizeName(name)
28
+ return toIdentifier(normalizeName(name)
15
29
  .split("-")
16
30
  .filter(Boolean)
17
31
  .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
18
- .join("");
32
+ .join(""));
19
33
  }
20
34
  /** Converts an arbitrary name to camelCase (safe as a TS identifier). */
21
35
  export function toCamelCase(name) {
22
36
  const pascal = toPascalCase(name);
23
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
37
+ return toIdentifier(pascal.charAt(0).toLowerCase() + pascal.slice(1));
24
38
  }
25
39
  /**
26
40
  * Pattern for names that may be used verbatim as a path segment
@@ -34,12 +48,24 @@ export const SAFE_PATH_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
34
48
  * `normalizeName` strips everything outside `[a-z0-9-]`, so a name such as
35
49
  * `"..."` or `"!!!"` collapses to the empty string and the generator writes
36
50
  * `src//.event.ts` with an anonymous exported class. Reject it up front.
51
+ *
52
+ * A name whose normalized form starts with a digit (`2fa`, `401-handler`) is
53
+ * rejected for the same reason: it becomes a class name, and `2faModule` is
54
+ * not a TypeScript identifier. The generated file, the barrel it is appended
55
+ * to and the `app.ts` it is registered in would all stop compiling, and the
56
+ * appends mean the overwrite guard cannot undo it. The converters would
57
+ * rather rename it to `_2faModule`, but a class whose name no longer contains
58
+ * the name that was asked for — and disagrees with its own file name — is a
59
+ * worse surprise than a one-line failure the author can act on.
37
60
  */
38
61
  export function assertGeneratableName(name, kind = "name") {
39
62
  const normalized = normalizeName(name);
40
63
  if (normalized === "") {
41
64
  throw new CLIValidationError(`Invalid ${kind}: "${name}". It must contain at least one letter or digit.`);
42
65
  }
66
+ if (/^[0-9]/.test(normalized)) {
67
+ throw new CLIValidationError(`Invalid ${kind}: "${name}". It must start with a letter: the name becomes a TypeScript class name, and "${normalized}" is not a valid identifier. Try "two-factor-auth" instead of "2fa".`);
68
+ }
43
69
  return normalized;
44
70
  }
45
71
  /**
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * @module validators/environment
5
5
  */
6
+ import type { PackageManager } from "../../types/index.js";
6
7
  /**
7
8
  * Environment check result.
8
9
  */
@@ -26,10 +27,19 @@ export interface EnvironmentValidationResult {
26
27
  export declare class EnvironmentValidator {
27
28
  /**
28
29
  * Validates the environment for a given project type.
30
+ *
31
+ * `packageManager` is the one the project will actually use. Without it
32
+ * the check probed pnpm, yarn, bun and npm in turn and reported the first
33
+ * one found, so it said "valid" while the chosen manager was missing.
29
34
  */
30
- validate(projectType: "backend" | "frontend" | "fullstack"): Promise<EnvironmentValidationResult>;
35
+ validate(projectType: "backend" | "frontend" | "fullstack", packageManager?: PackageManager): Promise<EnvironmentValidationResult>;
31
36
  private checkNode;
32
37
  private checkGit;
38
+ /**
39
+ * Checks the selected package manager, or — when none was selected — the
40
+ * first one that is installed.
41
+ */
33
42
  private checkPackageManager;
43
+ private checkBinary;
34
44
  }
35
45
  //# sourceMappingURL=environmentValidator.core.d.ts.map
@@ -4,20 +4,30 @@
4
4
  * @module validators/environment
5
5
  */
6
6
  import { execCommand } from "../../utils/utils.exec.js";
7
+ const PACKAGE_MANAGER_CANDIDATES = [
8
+ "pnpm",
9
+ "yarn",
10
+ "bun",
11
+ "npm",
12
+ ];
7
13
  /**
8
14
  * Validates the development environment before project generation.
9
15
  */
10
16
  export class EnvironmentValidator {
11
17
  /**
12
18
  * Validates the environment for a given project type.
19
+ *
20
+ * `packageManager` is the one the project will actually use. Without it
21
+ * the check probed pnpm, yarn, bun and npm in turn and reported the first
22
+ * one found, so it said "valid" while the chosen manager was missing.
13
23
  */
14
- async validate(projectType) {
24
+ async validate(projectType, packageManager) {
15
25
  const checks = [];
16
26
  checks.push(await this.checkNode());
17
27
  checks.push(await this.checkGit());
18
- if (projectType === "frontend" || projectType === "fullstack") {
19
- checks.push(await this.checkPackageManager());
20
- }
28
+ // A backend project installs dependencies too, so the package manager
29
+ // is checked for every project type, not just frontend and fullstack.
30
+ checks.push(await this.checkPackageManager(packageManager));
21
31
  const missing = checks
22
32
  .filter((c) => c.required && !c.installed)
23
33
  .map((c) => c.name);
@@ -28,28 +38,36 @@ export class EnvironmentValidator {
28
38
  };
29
39
  }
30
40
  async checkNode() {
31
- try {
32
- const result = await execCommand("node", ["--version"], ".");
33
- return {
34
- name: "Node.js",
35
- installed: true,
36
- version: result.stdout.trim(),
37
- required: true,
38
- };
41
+ return this.checkBinary("Node.js", "node");
42
+ }
43
+ async checkGit() {
44
+ return this.checkBinary("Git", "git");
45
+ }
46
+ /**
47
+ * Checks the selected package manager, or — when none was selected — the
48
+ * first one that is installed.
49
+ */
50
+ async checkPackageManager(packageManager) {
51
+ if (packageManager !== undefined) {
52
+ return this.checkBinary(packageManager, packageManager);
39
53
  }
40
- catch {
41
- return {
42
- name: "Node.js",
43
- installed: false,
44
- required: true,
45
- };
54
+ for (const candidate of PACKAGE_MANAGER_CANDIDATES) {
55
+ const check = await this.checkBinary(candidate, candidate);
56
+ if (check.installed) {
57
+ return check;
58
+ }
46
59
  }
60
+ return {
61
+ name: "Package Manager",
62
+ installed: false,
63
+ required: true,
64
+ };
47
65
  }
48
- async checkGit() {
66
+ async checkBinary(name, binary) {
49
67
  try {
50
- const result = await execCommand("git", ["--version"], ".");
68
+ const result = await execCommand(binary, ["--version"], ".");
51
69
  return {
52
- name: "Git",
70
+ name,
53
71
  installed: true,
54
72
  version: result.stdout.trim(),
55
73
  required: true,
@@ -57,62 +75,11 @@ export class EnvironmentValidator {
57
75
  }
58
76
  catch {
59
77
  return {
60
- name: "Git",
78
+ name,
61
79
  installed: false,
62
80
  required: true,
63
81
  };
64
82
  }
65
83
  }
66
- async checkPackageManager() {
67
- try {
68
- const result = await execCommand("pnpm", ["--version"], ".");
69
- return {
70
- name: "pnpm",
71
- installed: true,
72
- version: result.stdout.trim(),
73
- required: true,
74
- };
75
- }
76
- catch {
77
- try {
78
- const result = await execCommand("yarn", ["--version"], ".");
79
- return {
80
- name: "yarn",
81
- installed: true,
82
- version: result.stdout.trim(),
83
- required: true,
84
- };
85
- }
86
- catch {
87
- try {
88
- const result = await execCommand("bun", ["--version"], ".");
89
- return {
90
- name: "bun",
91
- installed: true,
92
- version: result.stdout.trim(),
93
- required: true,
94
- };
95
- }
96
- catch {
97
- try {
98
- const result = await execCommand("npm", ["--version"], ".");
99
- return {
100
- name: "npm",
101
- installed: true,
102
- version: result.stdout.trim(),
103
- required: true,
104
- };
105
- }
106
- catch {
107
- return {
108
- name: "Package Manager",
109
- installed: false,
110
- required: true,
111
- };
112
- }
113
- }
114
- }
115
- }
116
- }
117
84
  }
118
85
  //# sourceMappingURL=environmentValidator.core.js.map
@@ -3,6 +3,6 @@
3
3
  *
4
4
  * @module validators/project
5
5
  */
6
- export type { ProjectCheck, ProjectValidationResult, } from "./projectValidator.core.js";
6
+ export type { ProjectCheck, ProjectValidationResult, ProjectValidationOptions, } from "./projectValidator.core.js";
7
7
  export { ProjectValidator } from "./projectValidator.core.js";
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -13,8 +13,18 @@ export interface ProjectCheck {
13
13
  readonly passed: boolean;
14
14
  readonly message?: string;
15
15
  }
16
+ /** What a validation run should look at. */
17
+ export interface ProjectValidationOptions {
18
+ /**
19
+ * Whether dependencies are expected to be installed. A project created
20
+ * with `--no-install` has no node_modules on purpose.
21
+ */
22
+ readonly expectInstalled?: boolean;
23
+ /** Whether to run the project's own TypeScript compiler. */
24
+ readonly typecheck?: boolean;
25
+ }
16
26
  export declare class ProjectValidator {
17
- validate(projectPath: string): Promise<ProjectValidationResult>;
27
+ validate(projectPath: string, options?: ProjectValidationOptions): Promise<ProjectValidationResult>;
18
28
  private checkPackageJson;
19
29
  private checkTsConfig;
20
30
  private checkNodeModules;
@@ -9,14 +9,19 @@ import { stat } from "node:fs/promises";
9
9
  import { join } from "node:path";
10
10
  import { execCommand } from "../../utils/utils.exec.js";
11
11
  export class ProjectValidator {
12
- async validate(projectPath) {
12
+ async validate(projectPath, options = {}) {
13
+ const { expectInstalled = true, typecheck = true } = options;
13
14
  const checks = [];
14
15
  const errors = [];
15
16
  checks.push(await this.checkPackageJson(projectPath));
16
17
  checks.push(await this.checkTsConfig(projectPath));
17
- checks.push(await this.checkNodeModules(projectPath));
18
+ if (expectInstalled) {
19
+ checks.push(await this.checkNodeModules(projectPath));
20
+ }
18
21
  checks.push(await this.checkSourceFiles(projectPath));
19
- checks.push(await this.checkTypeScript(projectPath));
22
+ if (typecheck) {
23
+ checks.push(await this.checkTypeScript(projectPath));
24
+ }
20
25
  for (const check of checks) {
21
26
  if (!check.passed && check.message) {
22
27
  errors.push(check.message);
@@ -96,8 +101,19 @@ export class ProjectValidator {
96
101
  message: "Skipped (no tsconfig)",
97
102
  };
98
103
  }
104
+ // `npx tsc` downloads TypeScript from the registry when the project has
105
+ // no local copy — a network fetch nobody asked for. Only the project's
106
+ // own compiler is used, and the check is skipped when it is absent.
107
+ const tsc = join(projectPath, "node_modules", ".bin", process.platform === "win32" ? "tsc.cmd" : "tsc");
108
+ if (!existsSync(tsc)) {
109
+ return {
110
+ name: "typescript",
111
+ passed: true,
112
+ message: "Skipped (typescript is not installed in this project)",
113
+ };
114
+ }
99
115
  try {
100
- await execCommand("npx", ["tsc", "--noEmit"], projectPath);
116
+ await execCommand(tsc, ["--noEmit"], projectPath);
101
117
  return { name: "typescript", passed: true };
102
118
  }
103
119
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudojs-cli",
3
- "version": "1.2.0",
3
+ "version": "2.0.0",
4
4
  "description": "Command-line interface for scaffolding, generating, and managing Zudojs framework projects.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -28,14 +28,14 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "@clack/prompts": "^1.7.0",
31
- "@zudojs/config": "1.1.0",
32
- "@zudojs/core": "1.2.0",
33
- "@zudojs/errors": "1.1.0",
34
- "@zudojs/logger": "1.2.0"
31
+ "@zudojs/config": "1.2.0",
32
+ "@zudojs/core": "1.2.1",
33
+ "@zudojs/errors": "1.2.0",
34
+ "@zudojs/logger": "1.3.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^26.4.1",
38
- "@zudojs/constants": "1.1.0",
38
+ "@zudojs/constants": "1.1.1",
39
39
  "tsx": "^4.23.12",
40
40
  "typescript": "7.0.2",
41
41
  "vitest": "^4.1.11"