zudojs-cli 1.1.1 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/cliParser/cliParser.core.js +4 -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
|
@@ -54,7 +54,7 @@ ${options.modules.map((m) => ` new ${m.className}(),`).join("\n")}
|
|
|
54
54
|
const metadata = options.port === undefined
|
|
55
55
|
? ""
|
|
56
56
|
: `\n metadata: { port: ${options.port} },`;
|
|
57
|
-
return `import
|
|
57
|
+
return `import { resolveEnvironment } from "@zudojs/constants";
|
|
58
58
|
import { createContainer } from "@zudojs/container";
|
|
59
59
|
import type { Module } from "@zudojs/core";
|
|
60
60
|
import { createEventBus } from "@zudojs/events";
|
|
@@ -62,19 +62,6 @@ import { createLogger } from "@zudojs/logger";
|
|
|
62
62
|
import { createRuntime, type Runtime } from "@zudojs/runtime";
|
|
63
63
|
${imports}
|
|
64
64
|
|
|
65
|
-
const ENVIRONMENTS: readonly Environment[] = [
|
|
66
|
-
"development",
|
|
67
|
-
"test",
|
|
68
|
-
"staging",
|
|
69
|
-
"production",
|
|
70
|
-
];
|
|
71
|
-
|
|
72
|
-
/** Narrows NODE_ENV to a framework Environment without an unchecked cast. */
|
|
73
|
-
function resolveEnvironment(): Environment {
|
|
74
|
-
const value = process.env["NODE_ENV"];
|
|
75
|
-
return ENVIRONMENTS.find((candidate) => candidate === value) ?? "development";
|
|
76
|
-
}
|
|
77
|
-
|
|
78
65
|
/**
|
|
79
66
|
* Assembles the application runtime.
|
|
80
67
|
*
|
|
@@ -93,6 +80,8 @@ ${registration}
|
|
|
93
80
|
{
|
|
94
81
|
applicationName: ${literal(options.applicationName)},
|
|
95
82
|
applicationVersion: "0.1.0",
|
|
83
|
+
// NODE_ENV is read the same way the framework reads it: \`prod\` and
|
|
84
|
+
// \`Production\` are production, an unknown value warns once.
|
|
96
85
|
environment: resolveEnvironment(),
|
|
97
86
|
// Signals are handled explicitly in server.ts.
|
|
98
87
|
handleSignals: false,${metadata}
|
|
@@ -105,32 +94,6 @@ ${registration}
|
|
|
105
94
|
}
|
|
106
95
|
`;
|
|
107
96
|
}
|
|
108
|
-
/**
|
|
109
|
-
* Renders `src/server.ts`: the process entry point. Starts the runtime and
|
|
110
|
-
* stops it cleanly on SIGINT/SIGTERM.
|
|
111
|
-
*/
|
|
112
|
-
export function renderServerFile(appImportPath = "./app.js") {
|
|
113
|
-
return `import { createApp } from ${literal(appImportPath)};
|
|
114
|
-
|
|
115
|
-
const runtime = createApp();
|
|
116
|
-
|
|
117
|
-
await runtime.start();
|
|
118
|
-
|
|
119
|
-
for (const signal of ["SIGINT", "SIGTERM"] as const) {
|
|
120
|
-
process.once(signal, () => {
|
|
121
|
-
void runtime
|
|
122
|
-
.stop()
|
|
123
|
-
.then(() => {
|
|
124
|
-
process.exit(0);
|
|
125
|
-
})
|
|
126
|
-
.catch((error: unknown) => {
|
|
127
|
-
console.error(error);
|
|
128
|
-
process.exit(1);
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
`;
|
|
133
|
-
}
|
|
134
97
|
/**
|
|
135
98
|
* Renders a module class extending `BaseModule`.
|
|
136
99
|
*
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zudojs-cli — Generated Dockerfiles.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the microservice template and the InfrastructureGenerator so the
|
|
5
|
+
* fullstack microservice project and the standalone one build the same way.
|
|
6
|
+
*/
|
|
7
|
+
/** The dependency install command for a package manager inside a Dockerfile. */
|
|
8
|
+
export declare function dockerInstallCommand(packageManager: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* A self-contained Dockerfile for one app of a microservice project. The
|
|
11
|
+
* build context is the project root; only `appPath` is copied in.
|
|
12
|
+
*/
|
|
13
|
+
export declare function renderAppPackageDockerfile(options: {
|
|
14
|
+
readonly appPath: string;
|
|
15
|
+
readonly port: number;
|
|
16
|
+
readonly packageManager: string;
|
|
17
|
+
}): string;
|
|
18
|
+
/**
|
|
19
|
+
* A Dockerfile for a workspace whose server lives in `appDirectory`. The whole
|
|
20
|
+
* workspace is installed, then only that app is built and started: the root
|
|
21
|
+
* `build` script writes `apps/*\/dist`, never a root `dist/`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function renderWorkspaceAppDockerfile(options: {
|
|
24
|
+
readonly appDirectory: string;
|
|
25
|
+
readonly port: number;
|
|
26
|
+
readonly packageManager: string;
|
|
27
|
+
}): string;
|
|
28
|
+
//# sourceMappingURL=dockerfile.template.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zudojs-cli — Generated Dockerfiles.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the microservice template and the InfrastructureGenerator so the
|
|
5
|
+
* fullstack microservice project and the standalone one build the same way.
|
|
6
|
+
*/
|
|
7
|
+
/** The dependency install command for a package manager inside a Dockerfile. */
|
|
8
|
+
export function dockerInstallCommand(packageManager) {
|
|
9
|
+
// No lockfile is generated, so never `npm ci`; bare node images need
|
|
10
|
+
// corepack for pnpm/yarn; bun is not available there, so npm is used.
|
|
11
|
+
switch (packageManager) {
|
|
12
|
+
case "pnpm":
|
|
13
|
+
return "corepack enable && pnpm install";
|
|
14
|
+
case "yarn":
|
|
15
|
+
return "corepack enable && yarn install";
|
|
16
|
+
default:
|
|
17
|
+
return "npm install";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A self-contained Dockerfile for one app of a microservice project. The
|
|
22
|
+
* build context is the project root; only `appPath` is copied in.
|
|
23
|
+
*/
|
|
24
|
+
export function renderAppPackageDockerfile(options) {
|
|
25
|
+
const { appPath, port } = options;
|
|
26
|
+
return `FROM node:24-alpine AS builder
|
|
27
|
+
WORKDIR /app
|
|
28
|
+
COPY ${appPath}/package.json ./
|
|
29
|
+
RUN ${dockerInstallCommand(options.packageManager)}
|
|
30
|
+
COPY ${appPath}/tsconfig.json ./
|
|
31
|
+
COPY ${appPath}/src ./src
|
|
32
|
+
RUN npx tsc
|
|
33
|
+
|
|
34
|
+
FROM node:24-alpine AS runtime
|
|
35
|
+
WORKDIR /app
|
|
36
|
+
COPY --from=builder /app/dist ./dist
|
|
37
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
38
|
+
COPY --from=builder /app/package.json ./
|
|
39
|
+
EXPOSE ${port}
|
|
40
|
+
CMD ["node", "dist/server.js"]
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A Dockerfile for a workspace whose server lives in `appDirectory`. The whole
|
|
45
|
+
* workspace is installed, then only that app is built and started: the root
|
|
46
|
+
* `build` script writes `apps/*\/dist`, never a root `dist/`.
|
|
47
|
+
*/
|
|
48
|
+
export function renderWorkspaceAppDockerfile(options) {
|
|
49
|
+
const dir = options.appDirectory.replace(/^\.\/?/, "").replace(/\/$/, "");
|
|
50
|
+
const install = dockerInstallCommand(options.packageManager);
|
|
51
|
+
const runner = options.packageManager === "npm" || options.packageManager === "bun"
|
|
52
|
+
? "npm"
|
|
53
|
+
: options.packageManager;
|
|
54
|
+
const build = dir === "" ? `${runner} run build` : `cd ${dir} && ${runner} run build`;
|
|
55
|
+
const dist = dir === "" ? "dist" : `${dir}/dist`;
|
|
56
|
+
return `FROM node:24-alpine AS builder
|
|
57
|
+
WORKDIR /app
|
|
58
|
+
COPY . .
|
|
59
|
+
RUN ${install}
|
|
60
|
+
RUN ${build}
|
|
61
|
+
|
|
62
|
+
FROM node:24-alpine
|
|
63
|
+
WORKDIR /app
|
|
64
|
+
COPY --from=builder /app /app
|
|
65
|
+
EXPOSE ${options.port}
|
|
66
|
+
CMD ["node", "${dist}/server.js"]
|
|
67
|
+
`;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=dockerfile.template.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zudojs-cli — Generated `src/server.ts`
|
|
3
|
+
*
|
|
4
|
+
* The entry point used to call `runtime.start()` and nothing else. The
|
|
5
|
+
* runtime creates no HTTP server, so `pnpm dev` logged "ready" and exited
|
|
6
|
+
* with 0 even though `.env.example` set `PORT`, the Dockerfile exposed it
|
|
7
|
+
* and the fullstack frontend called it.
|
|
8
|
+
*
|
|
9
|
+
* The emitted server starts the runtime, then serves HTTP with
|
|
10
|
+
* `@zudojs/http` (`createHttpServer` + `createNodeHttpAdapter`) on `PORT`,
|
|
11
|
+
* answers `GET /health`, and keeps the process alive until SIGINT/SIGTERM,
|
|
12
|
+
* when it stops the HTTP server and then the runtime. `tests/
|
|
13
|
+
* generatedProject.typecheck.test.ts` type-checks this output against the
|
|
14
|
+
* current `@zudojs/*` sources.
|
|
15
|
+
*/
|
|
16
|
+
/** Options for {@link renderServerFile}. */
|
|
17
|
+
export interface ServerFileOptions {
|
|
18
|
+
/** Port used when `PORT` is unset. Defaults to 3000. */
|
|
19
|
+
readonly defaultPort?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Import path of a `HealthController` whose `check()` answers `/health`,
|
|
22
|
+
* relative to server.ts. When omitted, `/health` is answered inline.
|
|
23
|
+
*/
|
|
24
|
+
readonly healthControllerImport?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Renders `src/server.ts`: starts the runtime, serves HTTP on `PORT` with a
|
|
28
|
+
* `/health` route, and shuts both down cleanly on SIGINT/SIGTERM.
|
|
29
|
+
*/
|
|
30
|
+
export declare function renderServerFile(appImportPath?: string, options?: ServerFileOptions): string;
|
|
31
|
+
//# sourceMappingURL=server.template.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zudojs-cli — Generated `src/server.ts`
|
|
3
|
+
*
|
|
4
|
+
* The entry point used to call `runtime.start()` and nothing else. The
|
|
5
|
+
* runtime creates no HTTP server, so `pnpm dev` logged "ready" and exited
|
|
6
|
+
* with 0 even though `.env.example` set `PORT`, the Dockerfile exposed it
|
|
7
|
+
* and the fullstack frontend called it.
|
|
8
|
+
*
|
|
9
|
+
* The emitted server starts the runtime, then serves HTTP with
|
|
10
|
+
* `@zudojs/http` (`createHttpServer` + `createNodeHttpAdapter`) on `PORT`,
|
|
11
|
+
* answers `GET /health`, and keeps the process alive until SIGINT/SIGTERM,
|
|
12
|
+
* when it stops the HTTP server and then the runtime. `tests/
|
|
13
|
+
* generatedProject.typecheck.test.ts` type-checks this output against the
|
|
14
|
+
* current `@zudojs/*` sources.
|
|
15
|
+
*/
|
|
16
|
+
/** Emits a string as a TypeScript string literal that cannot break out. */
|
|
17
|
+
function literal(value) {
|
|
18
|
+
return JSON.stringify(value);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Renders `src/server.ts`: starts the runtime, serves HTTP on `PORT` with a
|
|
22
|
+
* `/health` route, and shuts both down cleanly on SIGINT/SIGTERM.
|
|
23
|
+
*/
|
|
24
|
+
export function renderServerFile(appImportPath = "./app.js", options = {}) {
|
|
25
|
+
const defaultPort = options.defaultPort ?? 3000;
|
|
26
|
+
const controller = options.healthControllerImport;
|
|
27
|
+
const controllerImport = controller
|
|
28
|
+
? `import { HealthController } from ${literal(controller)};\n`
|
|
29
|
+
: "";
|
|
30
|
+
const healthBody = controller
|
|
31
|
+
? "new HealthController().check()"
|
|
32
|
+
: `{ status: "ok", timestamp: new Date().toISOString() }`;
|
|
33
|
+
return `import {
|
|
34
|
+
createHttpServer,
|
|
35
|
+
createNodeHttpAdapter,
|
|
36
|
+
createResponseContext,
|
|
37
|
+
type HttpRequestContext,
|
|
38
|
+
} from "@zudojs/http";
|
|
39
|
+
|
|
40
|
+
import { createApp } from ${literal(appImportPath)};
|
|
41
|
+
${controllerImport}
|
|
42
|
+
const DEFAULT_PORT = ${defaultPort};
|
|
43
|
+
|
|
44
|
+
/** Reads PORT, refusing values that are not a TCP port. */
|
|
45
|
+
function resolvePort(): number {
|
|
46
|
+
const raw = process.env["PORT"];
|
|
47
|
+
if (raw === undefined || raw.trim() === "") return DEFAULT_PORT;
|
|
48
|
+
const port = Number(raw);
|
|
49
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
50
|
+
throw new Error(\`PORT must be an integer between 0 and 65535, got "\${raw}".\`);
|
|
51
|
+
}
|
|
52
|
+
return port;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const runtime = createApp();
|
|
56
|
+
await runtime.start();
|
|
57
|
+
|
|
58
|
+
const server = createHttpServer({
|
|
59
|
+
adapter: createNodeHttpAdapter({
|
|
60
|
+
host: process.env["HOST"] ?? "0.0.0.0",
|
|
61
|
+
port: resolvePort(),
|
|
62
|
+
}),
|
|
63
|
+
handler: (request: HttpRequestContext) => {
|
|
64
|
+
if (request.path === "/health") {
|
|
65
|
+
if (runtime.state !== "running") {
|
|
66
|
+
return createResponseContext({ status: 503 }).json({ status: "unavailable" });
|
|
67
|
+
}
|
|
68
|
+
return ${healthBody};
|
|
69
|
+
}
|
|
70
|
+
return createResponseContext({ status: 404 }).json({ error: "Not Found" });
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await server.start();
|
|
75
|
+
console.log(\`Listening on port \${server.address?.port ?? resolvePort()}\`);
|
|
76
|
+
|
|
77
|
+
let stopping = false;
|
|
78
|
+
for (const signal of ["SIGINT", "SIGTERM"] as const) {
|
|
79
|
+
process.once(signal, () => {
|
|
80
|
+
if (stopping) return;
|
|
81
|
+
stopping = true;
|
|
82
|
+
void server
|
|
83
|
+
.stop()
|
|
84
|
+
.then(() => runtime.stop())
|
|
85
|
+
.then(() => {
|
|
86
|
+
process.exit(0);
|
|
87
|
+
})
|
|
88
|
+
.catch((error: unknown) => {
|
|
89
|
+
console.error(error);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=server.template.js.map
|
|
@@ -7,4 +7,5 @@ export { writeFile, writeFileTree, mergeBarrelExport, } from "./utils.fileSystem
|
|
|
7
7
|
export { normalizeName, toPascalCase, toCamelCase } from "./utils.name.js";
|
|
8
8
|
export { detectArchitecture } from "./utils.detect.js";
|
|
9
9
|
export { execCommand, runStreaming } from "./utils.exec.js";
|
|
10
|
+
export { activeWriteCapture, captureWrites, findWriteConflicts, } from "./utils.writeGuard.js";
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/utils/index.js
CHANGED
|
@@ -7,4 +7,5 @@ export { writeFile, writeFileTree, mergeBarrelExport, } from "./utils.fileSystem
|
|
|
7
7
|
export { normalizeName, toPascalCase, toCamelCase } from "./utils.name.js";
|
|
8
8
|
export { detectArchitecture } from "./utils.detect.js";
|
|
9
9
|
export { execCommand, runStreaming } from "./utils.exec.js";
|
|
10
|
+
export { activeWriteCapture, captureWrites, findWriteConflicts, } from "./utils.writeGuard.js";
|
|
10
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { mkdir, writeFile as writeFileAsync } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, relative } from "node:path";
|
|
4
|
+
import { activeWriteCapture } from "./utils.writeGuard.js";
|
|
4
5
|
function assertSafePath(basePath, filePath) {
|
|
5
6
|
const resolved = join(basePath, filePath);
|
|
6
7
|
const relativePath = relative(basePath, resolved);
|
|
@@ -11,6 +12,11 @@ function assertSafePath(basePath, filePath) {
|
|
|
11
12
|
export async function writeFile(basePath, filePath, content) {
|
|
12
13
|
assertSafePath(basePath, filePath);
|
|
13
14
|
const fullPath = join(basePath, filePath);
|
|
15
|
+
const capture = activeWriteCapture();
|
|
16
|
+
if (capture) {
|
|
17
|
+
capture.set(fullPath, content);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
14
20
|
await mkdir(dirname(fullPath), { recursive: true });
|
|
15
21
|
await writeFileAsync(fullPath, content);
|
|
16
22
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zudojs-cli — Refusing to overwrite user files.
|
|
3
|
+
*
|
|
4
|
+
* Every schematic wrote with an unconditional `writeFile`, so re-running
|
|
5
|
+
* `zudojs generate module billing` (or reusing a name by mistake) replaced
|
|
6
|
+
* hand-written `*.module.ts`, `*.service.ts` and handler files without a
|
|
7
|
+
* word. `zudojs generate` now runs the schematic once with its writes
|
|
8
|
+
* captured, checks the captured files against the disk, and only then
|
|
9
|
+
* writes. An existing file may only be left as it is or appended to (the
|
|
10
|
+
* barrel merge done by `mergeBarrelExport`); anything else is a conflict
|
|
11
|
+
* unless `--force` is passed.
|
|
12
|
+
*/
|
|
13
|
+
/** The capture map of the current `captureWrites` call, if any. */
|
|
14
|
+
export declare function activeWriteCapture(): Map<string, string> | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Runs `run` with every `writeFile`/`writeFileTree` recorded instead of
|
|
17
|
+
* written. Returns the absolute paths and contents that would be written.
|
|
18
|
+
*/
|
|
19
|
+
export declare function captureWrites(run: () => Promise<unknown>): Promise<ReadonlyMap<string, string>>;
|
|
20
|
+
/**
|
|
21
|
+
* Lists captured files that already exist and would be changed other than
|
|
22
|
+
* by appending (paths relative to `cwd`).
|
|
23
|
+
*/
|
|
24
|
+
export declare function findWriteConflicts(cwd: string, files: ReadonlyMap<string, string>): string[];
|
|
25
|
+
//# sourceMappingURL=utils.writeGuard.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zudojs-cli — Refusing to overwrite user files.
|
|
3
|
+
*
|
|
4
|
+
* Every schematic wrote with an unconditional `writeFile`, so re-running
|
|
5
|
+
* `zudojs generate module billing` (or reusing a name by mistake) replaced
|
|
6
|
+
* hand-written `*.module.ts`, `*.service.ts` and handler files without a
|
|
7
|
+
* word. `zudojs generate` now runs the schematic once with its writes
|
|
8
|
+
* captured, checks the captured files against the disk, and only then
|
|
9
|
+
* writes. An existing file may only be left as it is or appended to (the
|
|
10
|
+
* barrel merge done by `mergeBarrelExport`); anything else is a conflict
|
|
11
|
+
* unless `--force` is passed.
|
|
12
|
+
*/
|
|
13
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
14
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
+
import { relative } from "node:path";
|
|
16
|
+
const captured = new AsyncLocalStorage();
|
|
17
|
+
/** The capture map of the current `captureWrites` call, if any. */
|
|
18
|
+
export function activeWriteCapture() {
|
|
19
|
+
return captured.getStore();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Runs `run` with every `writeFile`/`writeFileTree` recorded instead of
|
|
23
|
+
* written. Returns the absolute paths and contents that would be written.
|
|
24
|
+
*/
|
|
25
|
+
export async function captureWrites(run) {
|
|
26
|
+
const files = new Map();
|
|
27
|
+
await captured.run(files, run);
|
|
28
|
+
return files;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Lists captured files that already exist and would be changed other than
|
|
32
|
+
* by appending (paths relative to `cwd`).
|
|
33
|
+
*/
|
|
34
|
+
export function findWriteConflicts(cwd, files) {
|
|
35
|
+
const conflicts = [];
|
|
36
|
+
for (const [path, content] of files) {
|
|
37
|
+
if (!existsSync(path))
|
|
38
|
+
continue;
|
|
39
|
+
const existing = readFileSync(path, "utf-8");
|
|
40
|
+
if (content === existing || content.startsWith(existing))
|
|
41
|
+
continue;
|
|
42
|
+
conflicts.push(relative(cwd, path));
|
|
43
|
+
}
|
|
44
|
+
return conflicts;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=utils.writeGuard.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zudojs-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Command-line interface for scaffolding, generating, and managing Zudojs framework projects.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -28,13 +28,14 @@
|
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@clack/prompts": "^1.7.0",
|
|
31
|
-
"@zudojs/config": "1.0
|
|
32
|
-
"@zudojs/core": "1.1
|
|
33
|
-
"@zudojs/errors": "1.0
|
|
34
|
-
"@zudojs/logger": "1.
|
|
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.1",
|
|
38
39
|
"tsx": "^4.23.12",
|
|
39
40
|
"typescript": "7.0.2",
|
|
40
41
|
"vitest": "^4.1.11"
|