zudojs-cli 1.1.1 → 1.2.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.
- package/README.md +27 -0
- package/dist/src/adapters/databases/databaseAdapter.resolver.d.ts +21 -0
- package/dist/src/adapters/databases/databaseAdapter.resolver.js +40 -0
- package/dist/src/adapters/databases/index.d.ts +1 -0
- package/dist/src/adapters/databases/index.js +1 -0
- package/dist/src/adapters/databases/mysql.adapter.d.ts +1 -1
- package/dist/src/adapters/databases/mysql.adapter.js +2 -2
- package/dist/src/adapters/databases/postgres.adapter.d.ts +3 -2
- package/dist/src/adapters/databases/postgres.adapter.js +2 -2
- package/dist/src/adapters/databases/sqlite.adapter.d.ts +1 -1
- package/dist/src/adapters/databases/sqlite.adapter.js +2 -2
- package/dist/src/bin/zudojs.js +7 -1
- package/dist/src/commands/add.command.js +5 -1
- package/dist/src/commands/create.command.js +21 -7
- package/dist/src/commands/generate.command.js +37 -6
- package/dist/src/errors/index.d.ts +3 -14
- package/dist/src/errors/index.js +3 -24
- package/dist/src/generators/frontend/frontendPipeline.js +7 -3
- package/dist/src/generators/fullstack/fullstackBackend.layout.d.ts +26 -0
- package/dist/src/generators/fullstack/fullstackBackend.layout.js +34 -0
- package/dist/src/generators/fullstack/fullstackComposer.core.js +5 -1
- package/dist/src/generators/fullstack/index.d.ts +1 -0
- package/dist/src/generators/fullstack/index.js +1 -0
- package/dist/src/generators/infrastructure/infrastructure.generator.d.ts +5 -10
- package/dist/src/generators/infrastructure/infrastructure.generator.js +41 -75
- package/dist/src/generators/integration/integrationGenerator.core.js +8 -2
- package/dist/src/generators/module/index.d.ts +6 -0
- package/dist/src/generators/module/index.js +6 -0
- package/dist/src/generators/module/module.generator.d.ts +6 -1
- package/dist/src/generators/module/module.generator.js +15 -16
- package/dist/src/generators/module/module.registration.d.ts +23 -0
- package/dist/src/generators/module/module.registration.js +50 -0
- package/dist/src/resolvers/dependency/dependencyResolver.core.js +6 -1
- package/dist/src/resolvers/dependency/dependencyVersions.constant.d.ts +12 -0
- package/dist/src/resolvers/dependency/dependencyVersions.constant.js +40 -0
- package/dist/src/resolvers/dependency/index.d.ts +1 -0
- package/dist/src/resolvers/dependency/index.js +1 -0
- package/dist/src/templates/microservice/microservice.template.js +14 -42
- package/dist/src/templates/modular-monolith/modularMonolith.template.js +2 -1
- package/dist/src/templates/monolith/monolith.template.js +5 -2
- package/dist/src/templates/shared/appRuntime.template.d.ts +0 -5
- package/dist/src/templates/shared/appRuntime.template.js +3 -40
- package/dist/src/templates/shared/dockerfile.template.d.ts +28 -0
- package/dist/src/templates/shared/dockerfile.template.js +69 -0
- package/dist/src/templates/shared/index.d.ts +2 -0
- package/dist/src/templates/shared/index.js +2 -0
- package/dist/src/templates/shared/server.template.d.ts +31 -0
- package/dist/src/templates/shared/server.template.js +96 -0
- package/dist/src/utils/index.d.ts +1 -0
- package/dist/src/utils/index.js +1 -0
- package/dist/src/utils/utils.fileSystem.js +6 -0
- package/dist/src/utils/utils.writeGuard.d.ts +25 -0
- package/dist/src/utils/utils.writeGuard.js +46 -0
- package/package.json +6 -5
|
@@ -5,9 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { writeFileTree } from "../../utils/utils.fileSystem.js";
|
|
7
7
|
import { CLIValidationError } from "../../errors/index.js";
|
|
8
|
+
import { resolveDatabaseAdapter } from "../../adapters/databases/databaseAdapter.resolver.js";
|
|
9
|
+
import { resolveMicroserviceServices } from "../../templates/microservice/microservice.template.js";
|
|
10
|
+
import { renderAppPackageDockerfile, renderWorkspaceAppDockerfile, } from "../../templates/shared/dockerfile.template.js";
|
|
8
11
|
const SERVICE_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
9
12
|
export class InfrastructureGenerator {
|
|
10
13
|
async generate(options, basePath) {
|
|
14
|
+
// Rejects engines with no adapter (e.g. mongodb) instead of falling
|
|
15
|
+
// through to a postgres container.
|
|
16
|
+
resolveDatabaseAdapter(options.database);
|
|
11
17
|
for (const service of options.services ?? []) {
|
|
12
18
|
if (!SERVICE_NAME_PATTERN.test(service)) {
|
|
13
19
|
throw new CLIValidationError(`Invalid service name: "${service}". Service names must match ${SERVICE_NAME_PATTERN}.`);
|
|
@@ -21,43 +27,34 @@ export class InfrastructureGenerator {
|
|
|
21
27
|
if (options.architecture === "microservice") {
|
|
22
28
|
files["docker-compose.yml"] = this.getDockerCompose(options);
|
|
23
29
|
files[".dockerignore"] = "node_modules\ndist\n.git\n.env\n";
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
// The gateway lives at apps/gateway and each service at
|
|
31
|
+
// apps/services/<name>, as the microservice template writes them;
|
|
32
|
+
// each Dockerfile builds from the project root with its app path.
|
|
33
|
+
files["apps/gateway/Dockerfile"] = renderAppPackageDockerfile({
|
|
34
|
+
appPath: "apps/gateway",
|
|
35
|
+
port: 3000,
|
|
36
|
+
packageManager: options.packageManager,
|
|
37
|
+
});
|
|
38
|
+
resolveMicroserviceServices(options.services ?? []).forEach((service, i) => {
|
|
39
|
+
files[`apps/services/${service}/Dockerfile`] = renderAppPackageDockerfile({
|
|
40
|
+
appPath: `apps/services/${service}`,
|
|
41
|
+
port: 3001 + i,
|
|
42
|
+
packageManager: options.packageManager,
|
|
43
|
+
});
|
|
44
|
+
});
|
|
29
45
|
}
|
|
30
46
|
else {
|
|
31
47
|
files["docker-compose.yml"] = this.getSimpleDockerCompose(options);
|
|
32
48
|
files[".dockerignore"] = "node_modules\ndist\n.git\n.env\n";
|
|
33
|
-
files["Dockerfile"] =
|
|
49
|
+
files["Dockerfile"] = renderWorkspaceAppDockerfile({
|
|
50
|
+
appDirectory: options.appDirectory ?? ".",
|
|
51
|
+
port: 3000,
|
|
52
|
+
packageManager: options.packageManager,
|
|
53
|
+
});
|
|
34
54
|
}
|
|
35
55
|
files["migrations/.gitkeep"] = "";
|
|
36
56
|
return files;
|
|
37
57
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Install and build commands for Dockerfiles.
|
|
40
|
-
*
|
|
41
|
-
* Generated projects have no lockfile yet, so never use `npm ci` or
|
|
42
|
-
* `--frozen-lockfile`. pnpm and yarn need `corepack enable` on the bare
|
|
43
|
-
* node:24-alpine image; bun is not available there so it falls back to npm.
|
|
44
|
-
*/
|
|
45
|
-
getDockerCommands(options) {
|
|
46
|
-
switch (options.packageManager) {
|
|
47
|
-
case "pnpm":
|
|
48
|
-
return {
|
|
49
|
-
install: "corepack enable && pnpm install",
|
|
50
|
-
build: "pnpm run build",
|
|
51
|
-
};
|
|
52
|
-
case "yarn":
|
|
53
|
-
return {
|
|
54
|
-
install: "corepack enable && yarn install",
|
|
55
|
-
build: "yarn run build",
|
|
56
|
-
};
|
|
57
|
-
default:
|
|
58
|
-
return { install: "npm install", build: "npm run build" };
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
58
|
getDatabaseCompose(options) {
|
|
62
59
|
if (options.database === "mysql") {
|
|
63
60
|
return {
|
|
@@ -103,45 +100,13 @@ ${db.environment.map((e) => ` - ${e}`).join("\n")}
|
|
|
103
100
|
|
|
104
101
|
volumes:
|
|
105
102
|
db-data:
|
|
106
|
-
`;
|
|
107
|
-
}
|
|
108
|
-
getAppDockerfile(options) {
|
|
109
|
-
const { install, build } = this.getDockerCommands(options);
|
|
110
|
-
return `FROM node:24-alpine AS builder
|
|
111
|
-
WORKDIR /app
|
|
112
|
-
COPY . .
|
|
113
|
-
RUN ${install}
|
|
114
|
-
RUN ${build}
|
|
115
|
-
|
|
116
|
-
FROM node:24-alpine
|
|
117
|
-
WORKDIR /app
|
|
118
|
-
COPY --from=builder /app/dist ./dist
|
|
119
|
-
COPY --from=builder /app/node_modules ./node_modules
|
|
120
|
-
COPY --from=builder /app/package.json ./
|
|
121
|
-
EXPOSE 3000
|
|
122
|
-
CMD ["node", "dist/server.js"]
|
|
123
|
-
`;
|
|
124
|
-
}
|
|
125
|
-
getServiceDockerfile(options) {
|
|
126
|
-
const { install, build } = this.getDockerCommands(options);
|
|
127
|
-
return `FROM node:24-alpine AS builder
|
|
128
|
-
WORKDIR /app
|
|
129
|
-
COPY . .
|
|
130
|
-
RUN ${install}
|
|
131
|
-
RUN ${build}
|
|
132
|
-
|
|
133
|
-
FROM node:24-alpine
|
|
134
|
-
WORKDIR /app
|
|
135
|
-
COPY --from=builder /app/dist ./dist
|
|
136
|
-
COPY --from=builder /app/node_modules ./node_modules
|
|
137
|
-
COPY --from=builder /app/package.json ./
|
|
138
|
-
EXPOSE 3000
|
|
139
|
-
CMD ["node", "dist/server.js"]
|
|
140
103
|
`;
|
|
141
104
|
}
|
|
142
105
|
getSimpleDockerCompose(options) {
|
|
143
106
|
const db = this.getDatabaseCompose(options);
|
|
144
107
|
const databaseUrl = this.getDatabaseUrl(options);
|
|
108
|
+
const appDirectory = (options.appDirectory ?? ".").replace(/^\.\/?/, "").replace(/\/$/, "");
|
|
109
|
+
const src = appDirectory === "" ? "src" : `${appDirectory}/src`;
|
|
145
110
|
return `services:
|
|
146
111
|
app:
|
|
147
112
|
build: .
|
|
@@ -151,30 +116,31 @@ CMD ["node", "dist/server.js"]
|
|
|
151
116
|
- DATABASE_URL=${databaseUrl}
|
|
152
117
|
${db ? " depends_on:\n - db\n" : ""} develop:
|
|
153
118
|
watch:
|
|
154
|
-
- path: src/
|
|
119
|
+
- path: ${src}/
|
|
155
120
|
action: sync
|
|
156
|
-
target: /app
|
|
121
|
+
target: /app/${src}
|
|
157
122
|
${db ? `\n${this.getDbServiceBlock(db)}` : ""}`;
|
|
158
123
|
}
|
|
159
124
|
getDockerCompose(options) {
|
|
160
|
-
const services = options.services ?? [
|
|
125
|
+
const services = resolveMicroserviceServices(options.services ?? []);
|
|
161
126
|
const db = this.getDatabaseCompose(options);
|
|
162
127
|
const databaseUrl = this.getDatabaseUrl(options);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const port = 3001 + i;
|
|
167
|
-
serviceDefs += `
|
|
168
|
-
${service}:
|
|
128
|
+
const dependsOn = db ? " depends_on:\n - db\n" : "";
|
|
129
|
+
const block = (name, appPath, port) => `
|
|
130
|
+
${name}:
|
|
169
131
|
build:
|
|
170
|
-
context:
|
|
132
|
+
context: .
|
|
133
|
+
dockerfile: ${appPath}/Dockerfile
|
|
171
134
|
ports:
|
|
172
135
|
- "${port}:${port}"
|
|
173
136
|
environment:
|
|
174
137
|
- PORT=${port}
|
|
175
138
|
- DATABASE_URL=${databaseUrl}
|
|
176
|
-
${
|
|
177
|
-
|
|
139
|
+
${dependsOn}`;
|
|
140
|
+
const serviceDefs = [
|
|
141
|
+
block("gateway", "apps/gateway", 3000),
|
|
142
|
+
...services.map((service, i) => block(service, `apps/services/${service}`, 3001 + i)),
|
|
143
|
+
].join("");
|
|
178
144
|
return `services:${serviceDefs}${db ? `\n${this.getDbServiceBlock(db)}` : ""}`;
|
|
179
145
|
}
|
|
180
146
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { writeFileTree } from "../../utils/utils.fileSystem.js";
|
|
9
|
+
import { renderDatabaseEnv } from "../../adapters/databases/databaseAdapter.resolver.js";
|
|
9
10
|
/**
|
|
10
11
|
* Generates integration files between frontend and backend.
|
|
11
12
|
*/
|
|
@@ -57,7 +58,7 @@ export class IntegrationGenerator {
|
|
|
57
58
|
`VITE_API_URL=http://localhost:${backendPort}`,
|
|
58
59
|
"",
|
|
59
60
|
"# Database",
|
|
60
|
-
"
|
|
61
|
+
renderDatabaseEnv(context.project.backend?.database, "mydb"),
|
|
61
62
|
"",
|
|
62
63
|
];
|
|
63
64
|
if (context.project.frontend?.framework === "next") {
|
|
@@ -73,7 +74,12 @@ export class IntegrationGenerator {
|
|
|
73
74
|
* Ends with a "/" when non-empty so it can be prefixed directly.
|
|
74
75
|
*/
|
|
75
76
|
getBackendPath(context) {
|
|
76
|
-
|
|
77
|
+
if (context.project.type !== "fullstack")
|
|
78
|
+
return "";
|
|
79
|
+
// A microservice backend's public entry point is the gateway (CLI-02).
|
|
80
|
+
return context.project.backend?.architecture === "microservice"
|
|
81
|
+
? "apps/gateway/"
|
|
82
|
+
: "apps/api/";
|
|
77
83
|
}
|
|
78
84
|
generateApiClient(context) {
|
|
79
85
|
const backendPort = context.backendPort ?? 3000;
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* zudojs-cli — Module Generator
|
|
3
3
|
*
|
|
4
|
-
* Generates a
|
|
4
|
+
* Generates a runtime module (a `BaseModule` subclass, rendered by the same
|
|
5
|
+
* template `zudojs create` uses), exports it from the modules barrel and
|
|
6
|
+
* registers it in the sibling `app.ts`.
|
|
5
7
|
*/
|
|
8
|
+
import { type ModuleRegistration } from "./module.registration.js";
|
|
6
9
|
export interface GenerateModuleOptions {
|
|
7
10
|
readonly name: string;
|
|
8
11
|
readonly feature?: boolean;
|
|
9
12
|
readonly basePath?: string;
|
|
10
13
|
readonly dryRun?: boolean;
|
|
14
|
+
/** Receives the registration outcome (e.g. lines to add by hand). */
|
|
15
|
+
readonly onRegistered?: (registration: ModuleRegistration) => void;
|
|
11
16
|
}
|
|
12
17
|
export declare function generateModule(options: GenerateModuleOptions, cwd: string): Promise<string[]>;
|
|
13
18
|
//# sourceMappingURL=module.generator.d.ts.map
|
|
@@ -1,30 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* zudojs-cli — Module Generator
|
|
3
3
|
*
|
|
4
|
-
* Generates a
|
|
4
|
+
* Generates a runtime module (a `BaseModule` subclass, rendered by the same
|
|
5
|
+
* template `zudojs create` uses), exports it from the modules barrel and
|
|
6
|
+
* registers it in the sibling `app.ts`.
|
|
5
7
|
*/
|
|
6
|
-
import {
|
|
8
|
+
import { basename, dirname } from "node:path";
|
|
9
|
+
import { mergeBarrelExport, writeFileTree } from "../../utils/utils.fileSystem.js";
|
|
7
10
|
import { CLIGenerationError } from "../../errors/index.js";
|
|
8
11
|
import { assertGeneratableName, toPascalCase } from "../../utils/utils.name.js";
|
|
12
|
+
import { moduleSpec, renderModuleFile } from "../../templates/shared/appRuntime.template.js";
|
|
13
|
+
import { registerModuleInApp } from "./module.registration.js";
|
|
9
14
|
export async function generateModule(options, cwd) {
|
|
10
15
|
const basePath = options.basePath ?? "modules";
|
|
11
16
|
const name = assertGeneratableName(options.name, "module name");
|
|
12
17
|
const namePascal = toPascalCase(name);
|
|
18
|
+
const spec = moduleSpec(name, `./${name}/index.js`);
|
|
13
19
|
const files = {
|
|
14
|
-
[`${basePath}/${name}/${name}.module.ts`]:
|
|
15
|
-
|
|
16
|
-
export class ${namePascal}Module {
|
|
17
|
-
private readonly logger = createLogger({ name: "${name}-module" });
|
|
18
|
-
|
|
19
|
-
id = "${name}-module";
|
|
20
|
-
|
|
21
|
-
initialize() {
|
|
22
|
-
this.logger.info("${name} module initialized");
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
`,
|
|
26
|
-
[`${basePath}/${name}/index.ts`]: `export { ${namePascal}Module } from "./${name}.module.js";
|
|
20
|
+
[`${basePath}/${name}/${name}.module.ts`]: renderModuleFile({ module: spec }),
|
|
21
|
+
[`${basePath}/${name}/index.ts`]: `export { ${spec.className} } from "./${name}.module.js";
|
|
27
22
|
`,
|
|
23
|
+
[`${basePath}/index.ts`]: mergeBarrelExport(cwd, `${basePath}/index.ts`, `export { ${spec.className} } from "./${name}/index.js";`),
|
|
28
24
|
};
|
|
29
25
|
if (options.feature) {
|
|
30
26
|
const featureName = `${name}.feature`;
|
|
@@ -43,10 +39,13 @@ export class ${namePascal}Feature {
|
|
|
43
39
|
}
|
|
44
40
|
try {
|
|
45
41
|
await writeFileTree(cwd, files);
|
|
46
|
-
return Object.keys(files);
|
|
47
42
|
}
|
|
48
43
|
catch (error) {
|
|
49
44
|
throw new CLIGenerationError(`Failed to generate module: ${name}`, error);
|
|
50
45
|
}
|
|
46
|
+
const appPath = `${dirname(basePath)}/app.ts`;
|
|
47
|
+
const registration = registerModuleInApp(cwd, appPath, spec.className, `./${basename(basePath)}/index.js`);
|
|
48
|
+
options.onRegistered?.(registration);
|
|
49
|
+
return registration.registered ? [...Object.keys(files), appPath] : Object.keys(files);
|
|
51
50
|
}
|
|
52
51
|
//# sourceMappingURL=module.generator.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zudojs-cli — Registering a generated module with the runtime.
|
|
3
|
+
*
|
|
4
|
+
* `zudojs generate module` used to emit a plain class that was not a runtime
|
|
5
|
+
* `Module` and that nothing imported, so it never ran. The generator now
|
|
6
|
+
* emits a `BaseModule` subclass (the same template `zudojs create` uses),
|
|
7
|
+
* exports it from the modules barrel, and adds it to the module list in the
|
|
8
|
+
* sibling `app.ts` written by `zudojs create`.
|
|
9
|
+
*/
|
|
10
|
+
/** Outcome of {@link registerModuleInApp}. */
|
|
11
|
+
export interface ModuleRegistration {
|
|
12
|
+
/** Whether app.ts now registers the module. */
|
|
13
|
+
readonly registered: boolean;
|
|
14
|
+
/** Lines to add by hand when `registered` is false. */
|
|
15
|
+
readonly manualSteps: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Adds `new <className>()` to the module list in `appPath` and imports it
|
|
19
|
+
* from `importPath`. Idempotent. Leaves a hand-edited app.ts it does not
|
|
20
|
+
* recognise untouched and returns the lines to add instead.
|
|
21
|
+
*/
|
|
22
|
+
export declare function registerModuleInApp(cwd: string, appPath: string, className: string, importPath: string): ModuleRegistration;
|
|
23
|
+
//# sourceMappingURL=module.registration.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zudojs-cli — Registering a generated module with the runtime.
|
|
3
|
+
*
|
|
4
|
+
* `zudojs generate module` used to emit a plain class that was not a runtime
|
|
5
|
+
* `Module` and that nothing imported, so it never ran. The generator now
|
|
6
|
+
* emits a `BaseModule` subclass (the same template `zudojs create` uses),
|
|
7
|
+
* exports it from the modules barrel, and adds it to the module list in the
|
|
8
|
+
* sibling `app.ts` written by `zudojs create`.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { activeWriteCapture } from "../../utils/utils.writeGuard.js";
|
|
13
|
+
/** Marker `renderAppFile` emits at the start of the module list. */
|
|
14
|
+
const MODULE_LIST = " for (const module of [\n";
|
|
15
|
+
/**
|
|
16
|
+
* Adds `new <className>()` to the module list in `appPath` and imports it
|
|
17
|
+
* from `importPath`. Idempotent. Leaves a hand-edited app.ts it does not
|
|
18
|
+
* recognise untouched and returns the lines to add instead.
|
|
19
|
+
*/
|
|
20
|
+
export function registerModuleInApp(cwd, appPath, className, importPath) {
|
|
21
|
+
const importLine = `import { ${className} } from "${importPath}";`;
|
|
22
|
+
const entry = ` new ${className}(),\n`;
|
|
23
|
+
const manualSteps = [
|
|
24
|
+
importLine,
|
|
25
|
+
`new ${className}() in the modules passed to createRuntime`,
|
|
26
|
+
];
|
|
27
|
+
const fullPath = join(cwd, appPath);
|
|
28
|
+
if (!existsSync(fullPath))
|
|
29
|
+
return { registered: false, manualSteps };
|
|
30
|
+
const source = readFileSync(fullPath, "utf-8");
|
|
31
|
+
if (source.includes(entry))
|
|
32
|
+
return { registered: true, manualSteps: [] };
|
|
33
|
+
const listAt = source.indexOf(MODULE_LIST);
|
|
34
|
+
const lastImport = source.lastIndexOf("\nimport ");
|
|
35
|
+
if (listAt === -1 || lastImport === -1)
|
|
36
|
+
return { registered: false, manualSteps };
|
|
37
|
+
const importEnd = source.indexOf("\n", lastImport + 1);
|
|
38
|
+
const insertAt = listAt + MODULE_LIST.length;
|
|
39
|
+
const updated = source.slice(0, importEnd + 1) +
|
|
40
|
+
`${importLine}\n` +
|
|
41
|
+
source.slice(importEnd + 1, insertAt) +
|
|
42
|
+
entry +
|
|
43
|
+
source.slice(insertAt);
|
|
44
|
+
// During the overwrite check the generator runs with writes captured; the
|
|
45
|
+
// registration is an intended edit, not a conflict, so it is skipped then.
|
|
46
|
+
if (!activeWriteCapture())
|
|
47
|
+
writeFileSync(fullPath, updated);
|
|
48
|
+
return { registered: true, manualSteps: [] };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=module.registration.js.map
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module resolvers/dependency
|
|
5
5
|
*/
|
|
6
|
+
import { DEFAULT_DEPENDENCY_VERSIONS } from "./dependencyVersions.constant.js";
|
|
6
7
|
/**
|
|
7
8
|
* Resolves compatible dependency versions.
|
|
8
9
|
*/
|
|
@@ -63,9 +64,13 @@ export class DependencyResolver {
|
|
|
63
64
|
conflicts.push(conflict);
|
|
64
65
|
continue;
|
|
65
66
|
}
|
|
67
|
+
const version = req.version ?? DEFAULT_DEPENDENCY_VERSIONS.get(req.name);
|
|
68
|
+
if (version === undefined) {
|
|
69
|
+
warnings.push(`No version range known for "${req.name}"; it is recorded as "latest", which is not reproducible.`);
|
|
70
|
+
}
|
|
66
71
|
const resolved = {
|
|
67
72
|
name: req.name,
|
|
68
|
-
version:
|
|
73
|
+
version: version ?? "latest",
|
|
69
74
|
type: req.type,
|
|
70
75
|
};
|
|
71
76
|
if (req.type === "dependency") {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default version ranges for frontend dependencies the adapters request
|
|
3
|
+
* without a version.
|
|
4
|
+
*
|
|
5
|
+
* Unpinned requirements used to be written as `"latest"`, so the same
|
|
6
|
+
* scaffold produced a different dependency set every day. Every name a
|
|
7
|
+
* built-in adapter requests unpinned has a caret range here (checked by
|
|
8
|
+
* `tests/cli.round10.*.test.ts`). Ranges were taken from the npm registry on
|
|
9
|
+
* 2026-09-19.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_DEPENDENCY_VERSIONS: ReadonlyMap<string, string>;
|
|
12
|
+
//# sourceMappingURL=dependencyVersions.constant.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default version ranges for frontend dependencies the adapters request
|
|
3
|
+
* without a version.
|
|
4
|
+
*
|
|
5
|
+
* Unpinned requirements used to be written as `"latest"`, so the same
|
|
6
|
+
* scaffold produced a different dependency set every day. Every name a
|
|
7
|
+
* built-in adapter requests unpinned has a caret range here (checked by
|
|
8
|
+
* `tests/cli.round10.*.test.ts`). Ranges were taken from the npm registry on
|
|
9
|
+
* 2026-09-19.
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_DEPENDENCY_VERSIONS = new Map([
|
|
12
|
+
["@angular-builders/jest", "^22.0.1"],
|
|
13
|
+
["@angular/common", "^22.1.7"],
|
|
14
|
+
["@angular/core", "^22.1.7"],
|
|
15
|
+
["@ngrx/store", "^22.0.1"],
|
|
16
|
+
["@testing-library/jest-dom", "^7.0.1"],
|
|
17
|
+
["@testing-library/react", "^16.3.3"],
|
|
18
|
+
["@testing-library/react-native", "^14.0.1"],
|
|
19
|
+
["@testing-library/svelte", "^5.4.2"],
|
|
20
|
+
["@testing-library/user-event", "^14.6.7"],
|
|
21
|
+
["@vue/test-utils", "^2.5.1"],
|
|
22
|
+
["eslint", "^10.11.0"],
|
|
23
|
+
["eslint-plugin-react-hooks", "^7.1.1"],
|
|
24
|
+
["eslint-plugin-react-refresh", "^0.5.7"],
|
|
25
|
+
["eslint-plugin-svelte", "^3.23.0"],
|
|
26
|
+
["eslint-plugin-vue", "^10.11.0"],
|
|
27
|
+
["jest", "^30.5.2"],
|
|
28
|
+
["jsdom", "^30.1.0"],
|
|
29
|
+
["pinia", "^4.0.3"],
|
|
30
|
+
["prettier", "^3.9.8"],
|
|
31
|
+
["prettier-plugin-svelte", "^4.1.1"],
|
|
32
|
+
["react", "^19.3.0"],
|
|
33
|
+
["react-dom", "^19.3.0"],
|
|
34
|
+
["svelte", "^5.57.1"],
|
|
35
|
+
["typescript-eslint", "^8.70.0"],
|
|
36
|
+
["vitest", "^5.0.1"],
|
|
37
|
+
["vue", "^3.5.43"],
|
|
38
|
+
["zustand", "^5.0.15"],
|
|
39
|
+
]);
|
|
40
|
+
//# sourceMappingURL=dependencyVersions.constant.js.map
|
|
@@ -4,4 +4,5 @@
|
|
|
4
4
|
* @module resolvers/dependency
|
|
5
5
|
*/
|
|
6
6
|
export { DependencyResolver, type DependencyResolutionResult, type ResolvedDependency, type DependencyConflict, } from "./dependencyResolver.core.js";
|
|
7
|
+
export { DEFAULT_DEPENDENCY_VERSIONS } from "./dependencyVersions.constant.js";
|
|
7
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { ZUDOJS_PACKAGES_VERSION } from "../../constants/index.js";
|
|
25
25
|
import { normalizeName } from "../../utils/utils.name.js";
|
|
26
|
-
import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppFile, renderModuleFile, renderServerFile, renderPnpmWorkspaceFile, } from "../shared/index.js";
|
|
26
|
+
import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppPackageDockerfile, renderAppFile, renderModuleFile, renderServerFile, renderPnpmWorkspaceFile, } from "../shared/index.js";
|
|
27
27
|
/** Default service names used when none are provided. */
|
|
28
28
|
export const DEFAULT_MICROSERVICE_SERVICES = [
|
|
29
29
|
"identity",
|
|
@@ -173,13 +173,6 @@ MIT
|
|
|
173
173
|
`;
|
|
174
174
|
// Shared types
|
|
175
175
|
files["src/types/index.ts"] = ``;
|
|
176
|
-
// Install command used inside Dockerfiles (no lockfile is generated, so
|
|
177
|
-
// never use `npm ci`; enable corepack for pnpm/yarn on bare node images).
|
|
178
|
-
const dockerInstall = options.packageManager === "pnpm"
|
|
179
|
-
? "corepack enable && pnpm install"
|
|
180
|
-
: options.packageManager === "yarn"
|
|
181
|
-
? "corepack enable && yarn install"
|
|
182
|
-
: "npm install";
|
|
183
176
|
const appTsconfig = `{
|
|
184
177
|
"compilerOptions": {
|
|
185
178
|
"target": "ES2024",
|
|
@@ -223,22 +216,11 @@ MIT
|
|
|
223
216
|
devDependencies: appDevDeps,
|
|
224
217
|
}, null, 2) + "\n";
|
|
225
218
|
files["apps/gateway/tsconfig.json"] = appTsconfig;
|
|
226
|
-
files["apps/gateway/Dockerfile"] =
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
COPY apps/gateway/src ./src
|
|
232
|
-
RUN npx tsc
|
|
233
|
-
|
|
234
|
-
FROM node:24-alpine AS runtime
|
|
235
|
-
WORKDIR /app
|
|
236
|
-
COPY --from=builder /app/dist ./dist
|
|
237
|
-
COPY --from=builder /app/node_modules ./node_modules
|
|
238
|
-
COPY --from=builder /app/package.json ./
|
|
239
|
-
EXPOSE 3000
|
|
240
|
-
CMD ["node", "dist/server.js"]
|
|
241
|
-
`;
|
|
219
|
+
files["apps/gateway/Dockerfile"] = renderAppPackageDockerfile({
|
|
220
|
+
appPath: "apps/gateway",
|
|
221
|
+
port: 3000,
|
|
222
|
+
packageManager: options.packageManager,
|
|
223
|
+
});
|
|
242
224
|
files["apps/gateway/src/index.ts"] =
|
|
243
225
|
`export { createApp } from "./app.js";
|
|
244
226
|
`;
|
|
@@ -284,23 +266,11 @@ CMD ["node", "dist/server.js"]
|
|
|
284
266
|
devDependencies: appDevDeps,
|
|
285
267
|
}, null, 2) + "\n";
|
|
286
268
|
files[`apps/services/${svcName}/tsconfig.json`] = appTsconfig;
|
|
287
|
-
files[`apps/services/${svcName}/Dockerfile`] =
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
COPY apps/services/${svcName}/tsconfig.json ./
|
|
293
|
-
COPY apps/services/${svcName}/src ./src
|
|
294
|
-
RUN npx tsc
|
|
295
|
-
|
|
296
|
-
FROM node:24-alpine AS runtime
|
|
297
|
-
WORKDIR /app
|
|
298
|
-
COPY --from=builder /app/dist ./dist
|
|
299
|
-
COPY --from=builder /app/node_modules ./node_modules
|
|
300
|
-
COPY --from=builder /app/package.json ./
|
|
301
|
-
EXPOSE ${port}
|
|
302
|
-
CMD ["node", "dist/server.js"]
|
|
303
|
-
`;
|
|
269
|
+
files[`apps/services/${svcName}/Dockerfile`] = renderAppPackageDockerfile({
|
|
270
|
+
appPath: `apps/services/${svcName}`,
|
|
271
|
+
port,
|
|
272
|
+
packageManager: options.packageManager,
|
|
273
|
+
});
|
|
304
274
|
// Service structure (modular monolith per service)
|
|
305
275
|
const svcDirs = [
|
|
306
276
|
"configs",
|
|
@@ -337,7 +307,9 @@ CMD ["node", "dist/server.js"]
|
|
|
337
307
|
renderModuleFile({ module: svcModule });
|
|
338
308
|
files[`apps/services/${svcName}/src/modules/index.ts`] =
|
|
339
309
|
`export { ${svcModule.className} } from "./${svcName}.module.js";\n`;
|
|
340
|
-
files[`apps/services/${svcName}/src/server.ts`] = renderServerFile(
|
|
310
|
+
files[`apps/services/${svcName}/src/server.ts`] = renderServerFile("./app.js", {
|
|
311
|
+
defaultPort: port,
|
|
312
|
+
});
|
|
341
313
|
for (const dir of svcDirs) {
|
|
342
314
|
files[`apps/services/${svcName}/src/${dir}/index.ts`] = "";
|
|
343
315
|
}
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
* └── README.md
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
|
+
import { renderDatabaseEnv } from "../../adapters/databases/databaseAdapter.resolver.js";
|
|
34
35
|
import { ZUDOJS_PACKAGES_VERSION } from "../../constants/index.js";
|
|
35
36
|
import { normalizeName } from "../../utils/utils.name.js";
|
|
36
37
|
import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppFile, renderModuleFile, renderServerFile, renderPnpmWorkspaceFile, } from "../shared/index.js";
|
|
@@ -107,7 +108,7 @@ export function generateModularMonolithFiles(options) {
|
|
|
107
108
|
}
|
|
108
109
|
files[".env.example"] = `NODE_ENV=development
|
|
109
110
|
PORT=3000
|
|
110
|
-
|
|
111
|
+
${renderDatabaseEnv(options.database, nameSlug)}
|
|
111
112
|
`;
|
|
112
113
|
files[".gitignore"] = `node_modules/
|
|
113
114
|
dist/
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
* └── README.md
|
|
38
38
|
* ```
|
|
39
39
|
*/
|
|
40
|
+
import { renderDatabaseEnv } from "../../adapters/databases/databaseAdapter.resolver.js";
|
|
40
41
|
import { ZUDOJS_PACKAGES_VERSION } from "../../constants/index.js";
|
|
41
42
|
import { normalizeName, toPascalCase } from "../../utils/utils.name.js";
|
|
42
43
|
import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppFile, renderModuleFile, renderServerFile, renderServiceFile, renderPnpmWorkspaceFile, } from "../shared/index.js";
|
|
@@ -124,7 +125,7 @@ export function generateMonolithFiles(options) {
|
|
|
124
125
|
files[".env.example"] = `NODE_ENV=development
|
|
125
126
|
PORT=3000
|
|
126
127
|
|
|
127
|
-
|
|
128
|
+
${renderDatabaseEnv(options.database, nameSlug)}
|
|
128
129
|
JWT_SECRET=change-this-in-production
|
|
129
130
|
`;
|
|
130
131
|
files[".gitignore"] = `node_modules/
|
|
@@ -179,7 +180,9 @@ MIT
|
|
|
179
180
|
// src/ entry points
|
|
180
181
|
files["src/index.ts"] = `export { createApp } from "./app.js";
|
|
181
182
|
`;
|
|
182
|
-
files["src/server.ts"] = renderServerFile(
|
|
183
|
+
files["src/server.ts"] = renderServerFile("./app.js", {
|
|
184
|
+
healthControllerImport: "./controllers/index.js",
|
|
185
|
+
});
|
|
183
186
|
// Normalized: the name becomes a file path segment and a class name.
|
|
184
187
|
const moduleName = normalizeName(options.services[0] ?? "app") || "app";
|
|
185
188
|
const serviceClassName = `${toPascalCase(moduleName)}Service`;
|
|
@@ -37,11 +37,6 @@ export declare function renderAppFile(options: {
|
|
|
37
37
|
*/
|
|
38
38
|
readonly port?: number;
|
|
39
39
|
}): string;
|
|
40
|
-
/**
|
|
41
|
-
* Renders `src/server.ts`: the process entry point. Starts the runtime and
|
|
42
|
-
* stops it cleanly on SIGINT/SIGTERM.
|
|
43
|
-
*/
|
|
44
|
-
export declare function renderServerFile(appImportPath?: string): string;
|
|
45
40
|
/**
|
|
46
41
|
* Renders a module class extending `BaseModule`.
|
|
47
42
|
*
|