zudojs-cli 2.1.1 → 2.1.3

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 (49) hide show
  1. package/README.md +1 -1
  2. package/dist/src/bin/zudojs.js +1 -1
  3. package/dist/src/commands/generate.command.js +11 -16
  4. package/dist/src/constants/index.js +5 -1
  5. package/dist/src/constants/zudojsVersions.generated.js +37 -37
  6. package/dist/src/generators/command/command.generator.d.ts +7 -1
  7. package/dist/src/generators/command/command.generator.js +18 -44
  8. package/dist/src/generators/command/command.template.d.ts +24 -0
  9. package/dist/src/generators/command/command.template.js +78 -0
  10. package/dist/src/generators/command/index.d.ts +6 -0
  11. package/dist/src/generators/command/index.js +6 -0
  12. package/dist/src/generators/index.d.ts +0 -1
  13. package/dist/src/generators/index.js +0 -1
  14. package/dist/src/generators/infrastructure/infrastructure.generator.js +1 -1
  15. package/dist/src/generators/middleware/index.d.ts +2 -1
  16. package/dist/src/generators/middleware/index.js +2 -1
  17. package/dist/src/generators/middleware/middleware.generator.d.ts +28 -1
  18. package/dist/src/generators/middleware/middleware.generator.js +60 -15
  19. package/dist/src/generators/middleware/middleware.template.d.ts +21 -0
  20. package/dist/src/generators/middleware/middleware.template.js +35 -0
  21. package/dist/src/generators/query/index.d.ts +6 -0
  22. package/dist/src/generators/query/index.js +6 -0
  23. package/dist/src/generators/query/query.generator.d.ts +7 -1
  24. package/dist/src/generators/query/query.generator.js +19 -44
  25. package/dist/src/generators/query/query.template.d.ts +14 -0
  26. package/dist/src/generators/query/query.template.js +72 -0
  27. package/dist/src/generators/resource/resource.plan.d.ts +8 -2
  28. package/dist/src/generators/resource/resource.plan.js +10 -1
  29. package/dist/src/recipes/docker/docker.recipe.js +1 -1
  30. package/dist/src/recipes/docker/docker.refresh.js +1 -1
  31. package/dist/src/templates/resource/layers/resourceData.template.d.ts +1 -1
  32. package/dist/src/templates/resource/layers/resourceData.template.js +2 -1
  33. package/dist/src/templates/resource/layers/resourceRoutes.template.d.ts +1 -1
  34. package/dist/src/templates/resource/layers/resourceRoutes.template.js +6 -5
  35. package/dist/src/templates/resource/resource.names.d.ts +6 -0
  36. package/dist/src/templates/resource/resource.names.js +15 -0
  37. package/dist/src/templates/shared/dockerfile/dockerfile.install.d.ts +35 -0
  38. package/dist/src/templates/shared/dockerfile/dockerfile.install.js +89 -0
  39. package/dist/src/templates/shared/{dockerfile.template.d.ts → dockerfile/dockerfile.template.d.ts} +4 -2
  40. package/dist/src/templates/shared/{dockerfile.template.js → dockerfile/dockerfile.template.js} +17 -30
  41. package/dist/src/templates/shared/dockerfile/index.d.ts +6 -0
  42. package/dist/src/templates/shared/dockerfile/index.js +6 -0
  43. package/dist/src/templates/shared/index.d.ts +1 -1
  44. package/dist/src/templates/shared/index.js +1 -1
  45. package/dist/src/templates/shared/server.template.d.ts +7 -0
  46. package/dist/src/templates/shared/server.template.js +15 -3
  47. package/package.json +6 -6
  48. package/dist/src/generators/service/service.generator.d.ts +0 -12
  49. package/dist/src/generators/service/service.generator.js +0 -41
@@ -8,30 +8,7 @@
8
8
  * unprivileged `node` user and declares a /health HEALTHCHECK. The images
9
9
  * used to run as root and ship the whole dev toolchain.
10
10
  */
11
- /** The dependency install command for a package manager inside a Dockerfile. */
12
- export function dockerInstallCommand(packageManager) {
13
- // No lockfile is generated, so never `npm ci`; bare node images need
14
- // corepack for pnpm/yarn; bun is not available there, so npm is used.
15
- switch (packageManager) {
16
- case "pnpm":
17
- return "corepack enable && pnpm install";
18
- case "yarn":
19
- return "corepack enable && yarn install";
20
- default:
21
- return "npm install";
22
- }
23
- }
24
- /** Removes dev dependencies after the build (scripts off: prisma is a dev tool). */
25
- function dockerPruneCommand(packageManager) {
26
- switch (packageManager) {
27
- case "pnpm":
28
- return "pnpm prune --prod --ignore-scripts";
29
- case "yarn":
30
- return undefined;
31
- default:
32
- return "npm prune --omit=dev --ignore-scripts";
33
- }
34
- }
11
+ import { lockedInstallSteps, workspaceMemberInstallSteps, } from "./dockerfile.install.js";
35
12
  function runnerFor(packageManager) {
36
13
  return packageManager === "npm" || packageManager === "bun" ? "npm" : packageManager;
37
14
  }
@@ -54,24 +31,31 @@ CMD ["node", "${dist}/server.js"]
54
31
  * project root; `appPath` is the app's directory (`.` for a single-app
55
32
  * project). pnpm's `pnpm-workspace.yaml` is copied for its build-script
56
33
  * allow-list, without which pnpm 11 refuses to install.
34
+ *
35
+ * An app at the project root copies its lockfile and installs frozen (see
36
+ * `dockerfile.install.ts`); an app inside a workspace cannot use the
37
+ * workspace lockfile on its own, and its Dockerfile says so.
57
38
  */
58
39
  export function renderAppPackageDockerfile(options) {
59
40
  const app = options.appPath.replace(/^\.\/?/, "").replace(/\/$/, "");
60
41
  const at = (file) => (app === "" ? file : `${app}/${file}`);
61
42
  const pm = options.packageManager;
62
- const prune = dockerPruneCommand(pm);
43
+ const steps = app === "" ? lockedInstallSteps(pm) : workspaceMemberInstallSteps(pm);
44
+ const manifests = app === ""
45
+ ? [`COPY ${["package.json", ...steps.files].join(" ")} ./`]
46
+ : [`COPY ${at("package.json")} ./`, ...steps.files.map((file) => `COPY ${file} ./`)];
63
47
  const lines = [
64
48
  "# syntax=docker/dockerfile:1",
65
49
  "FROM node:24-alpine AS build",
66
50
  "WORKDIR /app",
67
- `COPY ${at("package.json")} ./`,
68
- ...(pm === "pnpm" ? ["COPY pnpm-workspace.yaml ./"] : []),
51
+ ...steps.comment,
52
+ ...manifests,
69
53
  ...(options.prisma ? [`COPY ${at("prisma.config.ts")} ./`, `COPY ${at("prisma")} ./prisma`] : []),
70
- `RUN ${dockerInstallCommand(pm)}`,
54
+ `RUN ${steps.install}`,
71
55
  `COPY ${at("tsconfig.json")} ./`,
72
56
  `COPY ${at("src")} ./src`,
73
57
  `RUN ${runnerFor(pm)} run build`,
74
- ...(prune === undefined ? [] : [`RUN ${prune}`]),
58
+ ...(steps.prune === undefined ? [] : [`RUN ${steps.prune}`]),
75
59
  "",
76
60
  ];
77
61
  return `${lines.join("\n")}\n${runtimeStage(options.port, "dist", "/app")}`;
@@ -86,13 +70,16 @@ export function renderWorkspaceAppDockerfile(options) {
86
70
  const runner = runnerFor(options.packageManager);
87
71
  const build = dir === "" ? `${runner} run build` : `cd ${dir} && ${runner} run build`;
88
72
  const dist = dir === "" ? "dist" : `${dir}/dist`;
73
+ // The whole workspace is copied, lockfile included, so it installs frozen.
74
+ const steps = lockedInstallSteps(options.packageManager);
89
75
  // pnpm links workspace dependencies through the root node_modules, so
90
76
  // the runtime stage takes the whole built workspace.
91
77
  return `# syntax=docker/dockerfile:1
92
78
  FROM node:24-alpine AS build
93
79
  WORKDIR /app
80
+ ${steps.comment.join("\n")}
94
81
  COPY . .
95
- RUN ${dockerInstallCommand(options.packageManager)}
82
+ RUN ${steps.install}
96
83
  RUN ${build}
97
84
 
98
85
  FROM node:24-alpine AS runtime
@@ -0,0 +1,6 @@
1
+ /**
2
+ * zudojs-cli — Generated Dockerfiles and how they install dependencies.
3
+ */
4
+ export * from "./dockerfile.template.js";
5
+ export * from "./dockerfile.install.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * zudojs-cli — Generated Dockerfiles and how they install dependencies.
3
+ */
4
+ export * from "./dockerfile.template.js";
5
+ export * from "./dockerfile.install.js";
6
+ //# sourceMappingURL=index.js.map
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export * from "./appRuntime.template.js";
5
5
  export * from "./capability.template.js";
6
- export * from "./dockerfile.template.js";
6
+ export * from "./dockerfile/index.js";
7
7
  export * from "./pnpm.template.js";
8
8
  export * from "./server.template.js";
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export * from "./appRuntime.template.js";
5
5
  export * from "./capability.template.js";
6
- export * from "./dockerfile.template.js";
6
+ export * from "./dockerfile/index.js";
7
7
  export * from "./pnpm.template.js";
8
8
  export * from "./server.template.js";
9
9
  //# sourceMappingURL=index.js.map
@@ -14,6 +14,13 @@
14
14
  * 500. On SIGINT/SIGTERM integrations drain, HTTP stops, then the runtime
15
15
  * stops. `tests/generatedProject.typecheck.test.ts` type-checks the output
16
16
  * against the current `@zudojs/*` sources.
17
+ *
18
+ * The signal listeners stay registered (`process.on`, not `once`) for the
19
+ * whole shutdown. Under `tsx watch`, Ctrl+C delivers SIGINT twice (from the
20
+ * terminal and forwarded by the watcher); with `once` the second signal
21
+ * found no listener and Node's default action killed the process before
22
+ * the runtime had stopped or logged anything. "Listening on" is logged once
23
+ * the listeners are in place, so a signal sent after it is always handled.
17
24
  */
18
25
  /** Options for {@link renderServerFile}. */
19
26
  export interface ServerFileOptions {
@@ -14,6 +14,13 @@
14
14
  * 500. On SIGINT/SIGTERM integrations drain, HTTP stops, then the runtime
15
15
  * stops. `tests/generatedProject.typecheck.test.ts` type-checks the output
16
16
  * against the current `@zudojs/*` sources.
17
+ *
18
+ * The signal listeners stay registered (`process.on`, not `once`) for the
19
+ * whole shutdown. Under `tsx watch`, Ctrl+C delivers SIGINT twice (from the
20
+ * terminal and forwarded by the watcher); with `once` the second signal
21
+ * found no listener and Node's default action killed the process before
22
+ * the runtime had stopped or logged anything. "Listening on" is logged once
23
+ * the listeners are in place, so a signal sent after it is always handled.
17
24
  */
18
25
  import { MARKERS, renderMarkerBlock } from "../../wiring/index.js";
19
26
  /** Emits a string as a TypeScript string literal that cannot break out. */
@@ -98,13 +105,16 @@ const server = createHttpServer({
98
105
  });
99
106
 
100
107
  await server.start();
101
- console.log(\`Listening on http://\${config.host}:\${server.address?.port ?? config.port}\`);
102
108
 
103
109
  let stopping = false;
104
110
  for (const signal of ["SIGINT", "SIGTERM"] as const) {
105
- process.once(signal, () => {
106
- if (stopping) return;
111
+ process.on(signal, () => {
112
+ if (stopping) {
113
+ console.log(\`Received \${signal} again: already shutting down.\`);
114
+ return;
115
+ }
107
116
  stopping = true;
117
+ console.log(\`Received \${signal}: shutting down.\`);
108
118
  void drainIntegrations(integrations)
109
119
  .then(() => server.stop())
110
120
  .then(() => runtime.stop())
@@ -117,6 +127,8 @@ for (const signal of ["SIGINT", "SIGTERM"] as const) {
117
127
  });
118
128
  });
119
129
  }
130
+
131
+ console.log(\`Listening on http://\${config.host}:\${server.address?.port ?? config.port}\`);
120
132
  `;
121
133
  }
122
134
  //# sourceMappingURL=server.template.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudojs-cli",
3
- "version": "2.1.1",
3
+ "version": "2.1.3",
4
4
  "description": "Command-line interface for scaffolding, generating, and managing Zudojs framework projects.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -32,14 +32,14 @@
32
32
  ],
33
33
  "dependencies": {
34
34
  "@clack/prompts": "^1.8.1",
35
- "@zudojs/config": "1.3.1",
36
- "@zudojs/core": "1.2.2",
37
- "@zudojs/errors": "1.3.0",
38
- "@zudojs/logger": "1.4.1"
35
+ "@zudojs/config": "1.3.3",
36
+ "@zudojs/core": "1.2.4",
37
+ "@zudojs/errors": "1.3.2",
38
+ "@zudojs/logger": "1.4.3"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^26.6.2",
42
- "@zudojs/constants": "1.1.2",
42
+ "@zudojs/constants": "1.1.4",
43
43
  "tsx": "^4.23.15",
44
44
  "typescript": "7.0.2",
45
45
  "vitest": "^5.0.1"
@@ -1,12 +0,0 @@
1
- /**
2
- * zudojs-cli — Service Generator
3
- *
4
- * Generates a new service with CQRS structure.
5
- */
6
- export interface GenerateServiceOptions {
7
- readonly name: string;
8
- readonly basePath?: string;
9
- readonly dryRun?: boolean;
10
- }
11
- export declare function generateService(options: GenerateServiceOptions, cwd: string): Promise<string[]>;
12
- //# sourceMappingURL=service.generator.d.ts.map
@@ -1,41 +0,0 @@
1
- /**
2
- * zudojs-cli — Service Generator
3
- *
4
- * Generates a new service with CQRS structure.
5
- */
6
- import { writeFileTree, mergeBarrelExport, } from "../../utils/utils.fileSystem.js";
7
- import { CLIGenerationError } from "../../errors/index.js";
8
- import { assertGeneratableName, toPascalCase } from "../../utils/utils.name.js";
9
- export async function generateService(options, cwd) {
10
- const basePath = options.basePath ?? "services";
11
- const name = assertGeneratableName(options.name, "service name");
12
- const namePascal = toPascalCase(name);
13
- const files = {
14
- [`${basePath}/${name}/${name}.service.ts`]: `import { createLogger } from "@zudojs/logger";
15
-
16
- export class ${namePascal}Service {
17
- private readonly logger = createLogger({ name: "${name}-service" });
18
-
19
- async initialize(): Promise<void> {
20
- this.logger.info("${name} service initialized");
21
- }
22
- }
23
- `,
24
- // Merge with any existing barrel content instead of overwriting it.
25
- [`${basePath}/${name}/index.ts`]: mergeBarrelExport(cwd, `${basePath}/${name}/index.ts`, `export { ${namePascal}Service } from "./${name}.service.js";`),
26
- // Preserve any existing content in the CQRS barrels.
27
- [`${basePath}/${name}/commands/index.ts`]: mergeBarrelExport(cwd, `${basePath}/${name}/commands/index.ts`, ""),
28
- [`${basePath}/${name}/queries/index.ts`]: mergeBarrelExport(cwd, `${basePath}/${name}/queries/index.ts`, ""),
29
- };
30
- if (options.dryRun) {
31
- return Object.keys(files);
32
- }
33
- try {
34
- await writeFileTree(cwd, files);
35
- return Object.keys(files);
36
- }
37
- catch (error) {
38
- throw new CLIGenerationError(`Failed to generate service: ${name}`, error);
39
- }
40
- }
41
- //# sourceMappingURL=service.generator.js.map