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
package/README.md CHANGED
@@ -178,7 +178,7 @@ current directory.
178
178
  | Option | Applies to | Description |
179
179
  | ----------------- | ----------------------------- | ------------------------------------------------------------------ |
180
180
  | `--module <name>` | every schematic but `module` | Generates inside `<root>/modules/<name>` instead of the schematic's default directory |
181
- | `--service <name>`| microservice projects, CQRS | Selects the app a schematic belongs to; also names the CQRS service for `command`/`query` |
181
+ | `--service <name>`| microservice projects | Selects the microservice app a schematic (including `command`/`query`) is written into |
182
182
  | `--dry-run` | all | Lists the files without writing anything |
183
183
  | `--force` | all | Overwrites files that already exist |
184
184
 
@@ -178,7 +178,7 @@ app.register(createCommand({
178
178
  {
179
179
  name: "service",
180
180
  short: "s",
181
- description: "The service name (for CQRS commands/queries)",
181
+ description: "Microservice projects: the app to generate into (gateway or a service name). Elsewhere: a folder grouping CQRS commands/queries",
182
182
  type: "string",
183
183
  },
184
184
  {
@@ -6,11 +6,10 @@
6
6
  * backend lives, then places the schematic accordingly.
7
7
  */
8
8
  import { join } from "node:path";
9
- import { generateService } from "../generators/service/service.generator.js";
10
9
  import { generateModule } from "../generators/module/module.generator.js";
11
10
  import { generateCommand } from "../generators/command/command.generator.js";
12
11
  import { generateQuery } from "../generators/query/query.generator.js";
13
- import { generateMiddleware } from "../generators/middleware/middleware.generator.js";
12
+ import { generateMiddleware, } from "../generators/middleware/middleware.generator.js";
14
13
  import { generateEvent } from "../generators/event/event.generator.js";
15
14
  import { generateJob } from "../generators/job/job.generator.js";
16
15
  import { generateModel } from "../generators/model/model.generator.js";
@@ -53,10 +52,6 @@ function getArchitectureRoot(architecture, schematic, serviceName) {
53
52
  }
54
53
  return "src";
55
54
  case "microservice":
56
- if (schematic === "service") {
57
- // A new service is a new app in the workspace.
58
- return "apps/services";
59
- }
60
55
  // `--service <name>` selects which app the schematic belongs to.
61
56
  // Without it the gateway app — the one app always generated — is used.
62
57
  // Modules go to that app's src/modules, next to the scaffolded ones,
@@ -72,13 +67,6 @@ function getArchitectureRoot(architecture, schematic, serviceName) {
72
67
  if (schematic === "module") {
73
68
  return "src/modules";
74
69
  }
75
- if (schematic === "service") {
76
- // The monolith template scaffolds `src/services/app.service.ts` and a
77
- // `src/services/index.ts` barrel. Generating into `src/<name>/`
78
- // instead left two conventions in one project, and the generated
79
- // service was never exported from the barrel the template owns.
80
- return "src/services";
81
- }
82
70
  return "src";
83
71
  }
84
72
  }
@@ -221,6 +209,15 @@ export async function runGenerateCommand(context) {
221
209
  .map((step) => ` ${step}`)
222
210
  .join("\n")}`);
223
211
  },
212
+ onMiddlewareRegistered: (registration) => {
213
+ if (registration.registered) {
214
+ context.logger.info(`Registered in the middleware pipeline of ${registration.serverFile}.`);
215
+ return;
216
+ }
217
+ context.logger.warn(`Could not register the middleware automatically (the zudojs:server-imports or zudojs:server-middleware markers are missing). Add:\n${registration.manualSteps
218
+ .map((step) => ` ${step}`)
219
+ .join("\n")}`);
220
+ },
224
221
  }, cwd);
225
222
  if (dryRun) {
226
223
  context.logger.info(`Dry run: ${fileCount(result.length)} would be generated (nothing written):`);
@@ -254,8 +251,6 @@ async function runSchematic(schematic, name, options, cwd) {
254
251
  // writing to `<basePath>/commands/<name>`.
255
252
  const cqrsService = options.architecture === "microservice" ? undefined : options.service;
256
253
  switch (schematic) {
257
- case "service":
258
- return await generateService({ name, basePath, dryRun }, cwd);
259
254
  case "module":
260
255
  return await generateModule({ name, feature: true, basePath, dryRun, onRegistered: options.onModuleRegistered }, cwd);
261
256
  case "command":
@@ -263,7 +258,7 @@ async function runSchematic(schematic, name, options, cwd) {
263
258
  case "query":
264
259
  return await generateQuery({ name, ...(cqrsService ? { service: cqrsService } : {}), basePath, dryRun }, cwd);
265
260
  case "middleware":
266
- return await generateMiddleware({ name, basePath, dryRun }, cwd);
261
+ return await generateMiddleware({ name, basePath, dryRun, onRegistered: options.onMiddlewareRegistered }, cwd);
267
262
  case "event":
268
263
  return await generateEvent({ name, basePath, dryRun }, cwd);
269
264
  case "job":
@@ -133,7 +133,11 @@ export const SCHEMA_CHOICES = Object.freeze([
133
133
  label: "Resource",
134
134
  hint: "DTO, repository, service, controller, CRUD routes and a test, registered",
135
135
  },
136
- { value: "service", label: "Service (CQRS)" },
136
+ {
137
+ value: "service",
138
+ label: "Service",
139
+ hint: "src/services/<name>.service.ts, plus its DTO and repository when missing",
140
+ },
137
141
  { value: "module", label: "Module" },
138
142
  { value: "command", label: "Command" },
139
143
  { value: "query", label: "Query" },
@@ -6,43 +6,43 @@
6
6
  * Do not edit by hand; run `node scripts/generateZudojsVersions.mjs`.
7
7
  */
8
8
  export const ZUDOJS_PACKAGE_VERSIONS = Object.freeze({
9
- "@zudojs/adapters": "1.2.1",
10
- "@zudojs/api": "1.2.1",
11
- "@zudojs/auth": "1.3.1",
12
- "@zudojs/auth-oauth": "1.2.3",
13
- "@zudojs/cache": "1.2.1",
14
- "@zudojs/config": "1.3.1",
15
- "@zudojs/constants": "1.1.2",
16
- "@zudojs/container": "1.2.1",
17
- "@zudojs/core": "1.2.2",
18
- "@zudojs/cqrs": "1.2.1",
19
- "@zudojs/crypto": "1.3.1",
20
- "@zudojs/database": "1.3.1",
21
- "@zudojs/docs": "1.0.4",
22
- "@zudojs/errors": "1.3.0",
23
- "@zudojs/events": "1.3.1",
24
- "@zudojs/feature-flags": "1.4.0",
25
- "@zudojs/http": "1.4.2",
26
- "@zudojs/lifecycle": "1.2.1",
27
- "@zudojs/logger": "1.4.1",
28
- "@zudojs/messaging": "1.2.1",
29
- "@zudojs/middleware": "1.1.0",
30
- "@zudojs/observability": "1.2.1",
31
- "@zudojs/openapi": "1.5.0",
32
- "@zudojs/permissions": "1.4.1",
33
- "@zudojs/plugins": "1.3.1",
34
- "@zudojs/queue": "1.4.1",
35
- "@zudojs/rpc": "1.4.1",
36
- "@zudojs/runtime": "1.3.1",
37
- "@zudojs/scheduler": "1.2.0",
38
- "@zudojs/schema": "1.2.1",
39
- "@zudojs/security": "1.3.1",
40
- "@zudojs/serialization": "1.2.1",
41
- "@zudojs/storage": "1.2.1",
42
- "@zudojs/tenancy": "1.3.1",
43
- "@zudojs/testing": "1.2.2",
44
- "@zudojs/transactions": "1.2.0",
9
+ "@zudojs/adapters": "1.2.3",
10
+ "@zudojs/api": "1.2.3",
11
+ "@zudojs/auth": "1.3.3",
12
+ "@zudojs/auth-oauth": "1.2.5",
13
+ "@zudojs/cache": "1.2.3",
14
+ "@zudojs/config": "1.3.3",
15
+ "@zudojs/constants": "1.1.4",
16
+ "@zudojs/container": "1.2.3",
17
+ "@zudojs/core": "1.2.4",
18
+ "@zudojs/cqrs": "1.2.3",
19
+ "@zudojs/crypto": "1.3.3",
20
+ "@zudojs/database": "1.4.0",
21
+ "@zudojs/docs": "1.0.6",
22
+ "@zudojs/errors": "1.3.2",
23
+ "@zudojs/events": "1.3.3",
24
+ "@zudojs/feature-flags": "1.4.2",
25
+ "@zudojs/http": "1.4.4",
26
+ "@zudojs/lifecycle": "1.2.3",
27
+ "@zudojs/logger": "1.4.3",
28
+ "@zudojs/messaging": "1.2.3",
29
+ "@zudojs/middleware": "1.1.2",
30
+ "@zudojs/observability": "1.2.3",
31
+ "@zudojs/openapi": "1.5.2",
32
+ "@zudojs/permissions": "1.4.3",
33
+ "@zudojs/plugins": "1.3.3",
34
+ "@zudojs/queue": "1.5.1",
35
+ "@zudojs/rpc": "1.4.3",
36
+ "@zudojs/runtime": "1.3.3",
37
+ "@zudojs/scheduler": "1.2.2",
38
+ "@zudojs/schema": "1.2.3",
39
+ "@zudojs/security": "1.3.3",
40
+ "@zudojs/serialization": "1.2.3",
41
+ "@zudojs/storage": "1.2.3",
42
+ "@zudojs/tenancy": "1.3.3",
43
+ "@zudojs/testing": "1.2.4",
44
+ "@zudojs/transactions": "1.2.2",
45
45
  "@zudojs/types": "1.2.0",
46
- "@zudojs/validation": "1.1.0",
46
+ "@zudojs/validation": "1.1.2",
47
47
  });
48
48
  //# sourceMappingURL=zudojsVersions.generated.js.map
@@ -1,13 +1,19 @@
1
1
  /**
2
2
  * zudojs-cli — Command Generator
3
3
  *
4
- * Generates a CQRS command with handler.
4
+ * Generates a CQRS command, its handler and a barrel under
5
+ * `<basePath>[/<service>]/commands/<name>/`.
5
6
  */
7
+ /** Options for {@link generateCommand}. */
6
8
  export interface GenerateCommandOptions {
7
9
  readonly name: string;
8
10
  readonly service?: string;
9
11
  readonly basePath?: string;
10
12
  readonly dryRun?: boolean;
11
13
  }
14
+ /**
15
+ * Writes `<name>.command.ts`, `<name>.handler.ts` and `index.ts`, and
16
+ * returns their paths (only the paths on a dry run).
17
+ */
12
18
  export declare function generateCommand(options: GenerateCommandOptions, cwd: string): Promise<string[]>;
13
19
  //# sourceMappingURL=command.generator.d.ts.map
@@ -1,55 +1,29 @@
1
1
  /**
2
2
  * zudojs-cli — Command Generator
3
3
  *
4
- * Generates a CQRS command with handler.
4
+ * Generates a CQRS command, its handler and a barrel under
5
+ * `<basePath>[/<service>]/commands/<name>/`.
5
6
  */
6
7
  import { writeFileTree } from "../../utils/utils.fileSystem.js";
7
8
  import { CLIGenerationError } from "../../errors/index.js";
8
- import { normalizeName } from "../../utils/utils.name.js";
9
+ import { assertGeneratableName } from "../../utils/utils.name.js";
10
+ import { cqrsSchematicNames, renderCommandBarrel, renderCommandFile, renderCommandHandlerFile, } from "./command.template.js";
11
+ /**
12
+ * Writes `<name>.command.ts`, `<name>.handler.ts` and `index.ts`, and
13
+ * returns their paths (only the paths on a dry run).
14
+ */
9
15
  export async function generateCommand(options, cwd) {
10
- const name = normalizeName(options.name);
11
- const nameCamel = name
12
- .replace(/-([a-z])/g, (_m, c) => c.toUpperCase())
13
- .replace(/^./, (c) => c.toUpperCase());
14
- const service = options.service;
16
+ const names = cqrsSchematicNames(assertGeneratableName(options.name, "command name"));
15
17
  const basePath = options.basePath ?? "services";
16
- // When no service grouping is given the schematic is written directly under
17
- // basePath. Callers that resolved the owning app into basePath (the
18
- // microservice layout) pass no service, so the path is not nested twice.
19
- const servicePath = service ? `${basePath}/${service}` : basePath;
18
+ // Without a service group the schematic goes directly under basePath:
19
+ // callers that resolved the owning app into basePath (the microservice
20
+ // layout) pass no service, so the path is not nested twice.
21
+ const servicePath = options.service ? `${basePath}/${options.service}` : basePath;
22
+ const dir = `${servicePath}/commands/${names.slug}`;
20
23
  const files = {
21
- [`${servicePath}/commands/${name}/${name}.command.ts`]: `import type { BaseCommand } from "@zudojs/cqrs";
22
-
23
- export interface ${nameCamel}CommandPayload {
24
- readonly [key: string]: unknown;
25
- }
26
-
27
- export class ${nameCamel}Command implements BaseCommand<${nameCamel}CommandPayload> {
28
- readonly commandName = "${name}";
29
-
30
- constructor(public readonly payload: ${nameCamel}CommandPayload) {}
31
- }
32
- `,
33
- [`${servicePath}/commands/${name}/${name}.handler.ts`]: `import type { CommandHandler, CommandResult } from "@zudojs/cqrs";
34
- import { createLogger } from "@zudojs/logger";
35
- import { ${nameCamel}Command } from "./${name}.command.js";
36
-
37
- export class ${nameCamel}CommandHandler implements CommandHandler<${nameCamel}Command> {
38
- private readonly logger = createLogger({ name: "${name}-handler" });
39
-
40
- async handle(command: ${nameCamel}Command): Promise<CommandResult> {
41
- this.logger.info("Processing ${name} command", { payload: command.payload });
42
-
43
- return {
44
- success: true,
45
- data: { id: crypto.randomUUID(), ...command.payload },
46
- };
47
- }
48
- }
49
- `,
50
- [`${servicePath}/commands/${name}/index.ts`]: `export { ${nameCamel}Command } from "./${name}.command.js";
51
- export { ${nameCamel}CommandHandler } from "./${name}.handler.js";
52
- `,
24
+ [`${dir}/${names.slug}.command.ts`]: renderCommandFile(names),
25
+ [`${dir}/${names.slug}.handler.ts`]: renderCommandHandlerFile(names),
26
+ [`${dir}/index.ts`]: renderCommandBarrel(names),
53
27
  };
54
28
  if (options.dryRun) {
55
29
  return Object.keys(files);
@@ -59,7 +33,7 @@ export { ${nameCamel}CommandHandler } from "./${name}.handler.js";
59
33
  return Object.keys(files);
60
34
  }
61
35
  catch (error) {
62
- throw new CLIGenerationError(`Failed to generate command: ${name} in ${servicePath}`, error);
36
+ throw new CLIGenerationError(`Failed to generate command: ${names.slug} in ${servicePath}`, error);
63
37
  }
64
38
  }
65
39
  //# sourceMappingURL=command.generator.js.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * zudojs-cli — Command schematic templates, written against the
3
+ * @zudojs/cqrs API: a `CommandOf` type built with `createCommand`, and a
4
+ * `CommandHandler` subclass (`commandType` + `execute`) registered on a
5
+ * `CommandBus`.
6
+ */
7
+ /** Identifiers derived from a command name (`create-user`). */
8
+ export interface CqrsSchematicNames {
9
+ /** kebab-case: folder and file names (`create-user`). */
10
+ readonly slug: string;
11
+ /** PascalCase: class and type prefix, and the bus discriminator (`CreateUser`). */
12
+ readonly pascal: string;
13
+ /** UPPER_SNAKE: prefix of the discriminator constant (`CREATE_USER`). */
14
+ readonly constant: string;
15
+ }
16
+ /** Derives {@link CqrsSchematicNames} from an already validated slug. */
17
+ export declare function cqrsSchematicNames(slug: string): CqrsSchematicNames;
18
+ /** `<slug>.command.ts`: the discriminator, payload, command type and factory. */
19
+ export declare function renderCommandFile(n: CqrsSchematicNames): string;
20
+ /** `<slug>.handler.ts`: the handler class and its bus registration. */
21
+ export declare function renderCommandHandlerFile(n: CqrsSchematicNames): string;
22
+ /** `index.ts`: the command folder's barrel. */
23
+ export declare function renderCommandBarrel(n: CqrsSchematicNames): string;
24
+ //# sourceMappingURL=command.template.d.ts.map
@@ -0,0 +1,78 @@
1
+ /**
2
+ * zudojs-cli — Command schematic templates, written against the
3
+ * @zudojs/cqrs API: a `CommandOf` type built with `createCommand`, and a
4
+ * `CommandHandler` subclass (`commandType` + `execute`) registered on a
5
+ * `CommandBus`.
6
+ */
7
+ import { toPascalCase } from "../../utils/utils.name.js";
8
+ /** Derives {@link CqrsSchematicNames} from an already validated slug. */
9
+ export function cqrsSchematicNames(slug) {
10
+ return { slug, pascal: toPascalCase(slug), constant: slug.toUpperCase().replace(/-/g, "_") };
11
+ }
12
+ /** `<slug>.command.ts`: the discriminator, payload, command type and factory. */
13
+ export function renderCommandFile(n) {
14
+ const id = `${n.constant}_COMMAND`;
15
+ return `import { createCommand, type CommandOf } from "@zudojs/cqrs";
16
+
17
+ /** Discriminator the command bus routes ${n.pascal} commands on. */
18
+ export const ${id} = "${n.pascal}";
19
+
20
+ /** Data a ${n.pascal} command carries. Replace it with the fields the use case needs. */
21
+ export type ${n.pascal}CommandPayload = {
22
+ readonly data: Readonly<Record<string, unknown>>;
23
+ };
24
+
25
+ /** The ${n.pascal} command: \`{ type: "${n.pascal}", data }\`. */
26
+ export type ${n.pascal}Command = CommandOf<typeof ${id}, ${n.pascal}CommandPayload>;
27
+
28
+ /** Creates an immutable ${n.pascal} command. */
29
+ export function create${n.pascal}Command(payload: ${n.pascal}CommandPayload): ${n.pascal}Command {
30
+ return createCommand(${id}, payload);
31
+ }
32
+ `;
33
+ }
34
+ /** `<slug>.handler.ts`: the handler class and its bus registration. */
35
+ export function renderCommandHandlerFile(n) {
36
+ const id = `${n.constant}_COMMAND`;
37
+ return `import { randomUUID } from "node:crypto";
38
+
39
+ import { CommandHandler, type CommandBus } from "@zudojs/cqrs";
40
+
41
+ import { ${id}, type ${n.pascal}Command } from "./${n.slug}.command.js";
42
+
43
+ /** What executing a ${n.pascal} command returns. */
44
+ export interface ${n.pascal}CommandResult {
45
+ readonly id: string;
46
+ readonly data: Readonly<Record<string, unknown>>;
47
+ }
48
+
49
+ /** Handles ${n.pascal} commands: put the write-side logic in \`execute\`. */
50
+ export class ${n.pascal}CommandHandler extends CommandHandler<${n.pascal}Command, ${n.pascal}CommandResult> {
51
+ public override readonly commandType = ${id};
52
+
53
+ public override async execute(command: ${n.pascal}Command): Promise<${n.pascal}CommandResult> {
54
+ return { id: randomUUID(), data: command.data };
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Registers {@link ${n.pascal}CommandHandler} on a command bus.
60
+ *
61
+ * @example
62
+ * const bus = register${n.pascal}Command(createCommandBus());
63
+ * const result = await bus.execute<${n.pascal}Command, ${n.pascal}CommandResult>(
64
+ * create${n.pascal}Command({ data: {} }),
65
+ * );
66
+ */
67
+ export function register${n.pascal}Command(bus: CommandBus): CommandBus {
68
+ return bus.register(${id}, new ${n.pascal}CommandHandler());
69
+ }
70
+ `;
71
+ }
72
+ /** `index.ts`: the command folder's barrel. */
73
+ export function renderCommandBarrel(n) {
74
+ return `export * from "./${n.slug}.command.js";
75
+ export * from "./${n.slug}.handler.js";
76
+ `;
77
+ }
78
+ //# sourceMappingURL=command.template.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * zudojs-cli — Command schematic: a `CommandOf` command type, its factory
3
+ * and a `CommandHandler` subclass for @zudojs/cqrs.
4
+ */
5
+ export * from "./command.generator.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * zudojs-cli — Command schematic: a `CommandOf` command type, its factory
3
+ * and a `CommandHandler` subclass for @zudojs/cqrs.
4
+ */
5
+ export * from "./command.generator.js";
6
+ //# sourceMappingURL=index.js.map
@@ -8,7 +8,6 @@ export { FullstackComposer } from "./fullstack/index.js";
8
8
  export { IntegrationGenerator } from "./integration/index.js";
9
9
  export { InfrastructureGenerator } from "./infrastructure/index.js";
10
10
  export { generateModule } from "./module/module.generator.js";
11
- export { generateService } from "./service/service.generator.js";
12
11
  export { generateCommand } from "./command/command.generator.js";
13
12
  export { generateQuery } from "./query/query.generator.js";
14
13
  export { generateResource, resolveResourceLayout, planResource, RESOURCE_SCHEMATICS, } from "./resource/index.js";
@@ -8,7 +8,6 @@ export { FullstackComposer } from "./fullstack/index.js";
8
8
  export { IntegrationGenerator } from "./integration/index.js";
9
9
  export { InfrastructureGenerator } from "./infrastructure/index.js";
10
10
  export { generateModule } from "./module/module.generator.js";
11
- export { generateService } from "./service/service.generator.js";
12
11
  export { generateCommand } from "./command/command.generator.js";
13
12
  export { generateQuery } from "./query/query.generator.js";
14
13
  export { generateResource, resolveResourceLayout, planResource, RESOURCE_SCHEMATICS, } from "./resource/index.js";
@@ -7,7 +7,7 @@ import { writeFileTree } from "../../utils/utils.fileSystem.js";
7
7
  import { CLIValidationError } from "../../errors/index.js";
8
8
  import { resolveDatabaseAdapter } from "../../adapters/databases/databaseAdapter.resolver.js";
9
9
  import { resolveMicroserviceServices } from "../../templates/microservice/microservice.template.js";
10
- import { renderAppPackageDockerfile, renderWorkspaceAppDockerfile, } from "../../templates/shared/dockerfile.template.js";
10
+ import { renderAppPackageDockerfile, renderWorkspaceAppDockerfile, } from "../../templates/shared/dockerfile/index.js";
11
11
  const SERVICE_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
12
12
  /**
13
13
  * Database passwords are interpolated by compose from `.env`
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * zudojs-cli — Middleware Generator
3
3
  */
4
- export { generateMiddleware, type GenerateMiddlewareOptions, } from "./middleware.generator.js";
4
+ export { generateMiddleware, middlewareAppSrc, middlewareServerLines, type GenerateMiddlewareOptions, type MiddlewareRegistration, } from "./middleware.generator.js";
5
+ export { renderMiddlewareFile, type MiddlewareFileOptions } from "./middleware.template.js";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * zudojs-cli — Middleware Generator
3
3
  */
4
- export { generateMiddleware, } from "./middleware.generator.js";
4
+ export { generateMiddleware, middlewareAppSrc, middlewareServerLines, } from "./middleware.generator.js";
5
+ export { renderMiddlewareFile } from "./middleware.template.js";
5
6
  //# sourceMappingURL=index.js.map
@@ -1,12 +1,39 @@
1
1
  /**
2
2
  * zudojs-cli — Middleware Generator
3
3
  *
4
- * Generates middleware files.
4
+ * Writes `<basePath>/middlewares/<name>.middleware.ts` (an `HttpMiddleware`
5
+ * from @zudojs/http), exports it from the folder barrel and registers it in
6
+ * the app's `src/server.ts` between the `zudojs:server-imports` and
7
+ * `zudojs:server-middleware` markers. A server.ts missing either marker pair
8
+ * is left unchanged (no import without its registration) and the lines to
9
+ * add by hand are reported instead.
5
10
  */
6
11
  export interface GenerateMiddlewareOptions {
7
12
  readonly name: string;
8
13
  readonly basePath: string;
9
14
  readonly dryRun?: boolean;
15
+ /** Receives the registration outcome (e.g. lines to add by hand). */
16
+ readonly onRegistered?: (registration: MiddlewareRegistration) => void;
10
17
  }
18
+ /** Outcome of registering a generated middleware in `server.ts`. */
19
+ export interface MiddlewareRegistration {
20
+ /** Whether server.ts now runs the middleware. */
21
+ readonly registered: boolean;
22
+ /** The server file it was (or should be) registered in. */
23
+ readonly serverFile: string;
24
+ /** Lines to add by hand when `registered` is false. */
25
+ readonly manualSteps: readonly string[];
26
+ }
27
+ /**
28
+ * The app source directory owning `basePath`: `src` for `src` and
29
+ * `src/modules/billing`, `apps/services/x/src` for a service.
30
+ */
31
+ export declare function middlewareAppSrc(basePath: string): string;
32
+ /** The import and pipeline lines that register `factoryName` in server.ts. */
33
+ export declare function middlewareServerLines(basePath: string, factoryName: string): {
34
+ readonly serverFile: string;
35
+ readonly importLine: string;
36
+ readonly entryLine: string;
37
+ };
11
38
  export declare function generateMiddleware(options: GenerateMiddlewareOptions, cwd: string): Promise<string[]>;
12
39
  //# sourceMappingURL=middleware.generator.d.ts.map
@@ -1,29 +1,74 @@
1
1
  /**
2
2
  * zudojs-cli — Middleware Generator
3
3
  *
4
- * Generates middleware files.
4
+ * Writes `<basePath>/middlewares/<name>.middleware.ts` (an `HttpMiddleware`
5
+ * from @zudojs/http), exports it from the folder barrel and registers it in
6
+ * the app's `src/server.ts` between the `zudojs:server-imports` and
7
+ * `zudojs:server-middleware` markers. A server.ts missing either marker pair
8
+ * is left unchanged (no import without its registration) and the lines to
9
+ * add by hand are reported instead.
5
10
  */
6
- import { writeFileTree } from "../../utils/utils.fileSystem.js";
11
+ import { posix } from "node:path";
12
+ import { CLIValidationError } from "../../errors/index.js";
13
+ import { mergeBarrelExport, writeFileTree } from "../../utils/utils.fileSystem.js";
7
14
  import { normalizeName, toCamelCase } from "../../utils/utils.name.js";
15
+ import { MARKERS, applyMarkerEdits, conflictingImport, planMarkerEdits, } from "../../wiring/index.js";
16
+ import { renderMiddlewareFile } from "./middleware.template.js";
17
+ /**
18
+ * The app source directory owning `basePath`: `src` for `src` and
19
+ * `src/modules/billing`, `apps/services/x/src` for a service.
20
+ */
21
+ export function middlewareAppSrc(basePath) {
22
+ return basePath.replace(/\/modules\/[^/]+$/, "");
23
+ }
24
+ /** The import and pipeline lines that register `factoryName` in server.ts. */
25
+ export function middlewareServerLines(basePath, factoryName) {
26
+ const appSrc = middlewareAppSrc(basePath);
27
+ const barrel = posix.relative(appSrc, `${basePath}/middlewares/index.js`);
28
+ return {
29
+ serverFile: `${appSrc}/server.ts`,
30
+ importLine: `import { ${factoryName} } from "./${barrel}";`,
31
+ entryLine: `${factoryName}(),`,
32
+ };
33
+ }
8
34
  export async function generateMiddleware(options, cwd) {
9
35
  const name = normalizeName(options.name);
10
- const nameCamel = toCamelCase(options.name);
36
+ const factoryName = `${toCamelCase(options.name)}Middleware`;
37
+ const lines = middlewareServerLines(options.basePath, factoryName);
38
+ const barrel = `${options.basePath}/middlewares/index.ts`;
39
+ const clash = conflictingImport(cwd, lines.serverFile, lines.importLine);
40
+ if (clash !== undefined) {
41
+ throw new CLIValidationError(`${lines.serverFile} already imports ${factoryName} (${clash.trim()}), so middleware "${name}" cannot be registered. Choose another name.`);
42
+ }
11
43
  const files = {
12
- [`${options.basePath}/middlewares/${name}.middleware.ts`]: `/**
13
- * ${name} middleware.
14
- */
15
-
16
- export function ${nameCamel}Middleware() {
17
- return async (ctx: unknown, next: () => Promise<void>) => {
18
- await next();
19
- };
20
- }
21
- `,
44
+ [`${options.basePath}/middlewares/${name}.middleware.ts`]: renderMiddlewareFile({
45
+ name,
46
+ factoryName,
47
+ serverFile: lines.serverFile,
48
+ }),
49
+ [barrel]: mergeBarrelExport(cwd, barrel, `export { ${factoryName} } from "./${name}.middleware.js";`),
22
50
  };
51
+ const edits = [
52
+ { file: lines.serverFile, marker: MARKERS.serverImports, line: lines.importLine },
53
+ { file: lines.serverFile, marker: MARKERS.serverMiddleware, line: lines.entryLine },
54
+ ];
55
+ const planned = planMarkerEdits(cwd, edits).outcome;
56
+ const registered = planned.manualSteps.length === 0;
23
57
  if (options.dryRun) {
24
- return Object.keys(files);
58
+ return [...Object.keys(files), ...(registered ? planned.edited : [])];
25
59
  }
26
60
  await writeFileTree(cwd, files);
27
- return Object.keys(files);
61
+ const edited = registered ? (await applyMarkerEdits(cwd, edits)).edited : [];
62
+ options.onRegistered?.({
63
+ registered,
64
+ serverFile: lines.serverFile,
65
+ manualSteps: registered
66
+ ? []
67
+ : [
68
+ `In ${lines.serverFile}: ${lines.importLine}`,
69
+ `In ${lines.serverFile}: add ${lines.entryLine} to the middlewares list of the HttpMiddlewarePipeline, before dispatch`,
70
+ ],
71
+ });
72
+ return [...Object.keys(files), ...edited];
28
73
  }
29
74
  //# sourceMappingURL=middleware.generator.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * zudojs-cli — Generated `<name>.middleware.ts`.
3
+ *
4
+ * `generate middleware` used to write
5
+ * `(ctx: unknown, next: () => Promise<void>) => Promise<void>`: registering
6
+ * it in the server pipeline failed tsc with TS2322, and because it returned
7
+ * nothing the response produced by `next()` was dropped. The file is now a
8
+ * real `HttpMiddleware` from @zudojs/http that returns `next()`'s response.
9
+ */
10
+ /** Options for {@link renderMiddlewareFile}. */
11
+ export interface MiddlewareFileOptions {
12
+ /** Normalized (kebab-case) name, e.g. `request-timer`. */
13
+ readonly name: string;
14
+ /** Factory function name, e.g. `requestTimerMiddleware`. */
15
+ readonly factoryName: string;
16
+ /** Where the factory is registered, e.g. `src/server.ts`. */
17
+ readonly serverFile: string;
18
+ }
19
+ /** Renders the middleware source file. */
20
+ export declare function renderMiddlewareFile(options: MiddlewareFileOptions): string;
21
+ //# sourceMappingURL=middleware.template.d.ts.map