zudojs-cli 1.0.0 → 1.1.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 +16 -6
- package/dist/src/adapters/frontend/frontendAdapter.type.d.ts +6 -0
- package/dist/src/bin/zudojs.js +8 -32
- package/dist/src/cliApplication/cliApplication.core.js +2 -2
- package/dist/src/cliApplication/cliApplication.logger.d.ts +28 -0
- package/dist/src/cliApplication/cliApplication.logger.js +68 -0
- package/dist/src/cliApplication/index.d.ts +1 -3
- package/dist/src/cliApplication/index.js +1 -3
- package/dist/src/cliVersion/cliVersion.update.d.ts +38 -0
- package/dist/src/cliVersion/cliVersion.update.js +63 -0
- package/dist/src/cliVersion/index.d.ts +2 -1
- package/dist/src/cliVersion/index.js +2 -1
- package/dist/src/commands/add.command.d.ts +29 -0
- package/dist/src/commands/add.command.js +94 -109
- package/dist/src/commands/build.command.d.ts +8 -0
- package/dist/src/commands/build.command.js +12 -18
- package/dist/src/commands/create.command.js +8 -5
- package/dist/src/commands/dev.command.d.ts +30 -2
- package/dist/src/commands/dev.command.js +104 -183
- package/dist/src/commands/doctor.command.d.ts +18 -0
- package/dist/src/commands/doctor.command.js +141 -113
- package/dist/src/commands/generate.command.d.ts +2 -1
- package/dist/src/commands/generate.command.js +23 -42
- package/dist/src/commands/info.command.js +61 -30
- package/dist/src/constants/index.d.ts +18 -2
- package/dist/src/constants/index.js +31 -2
- package/dist/src/generators/frontend/frontendGenerator.core.d.ts +2 -0
- package/dist/src/generators/frontend/frontendGenerator.core.js +11 -0
- package/dist/src/generators/frontend/frontendPipeline.js +45 -2
- package/dist/src/generators/fullstack/fullstackComposer.core.d.ts +2 -0
- package/dist/src/generators/fullstack/fullstackComposer.core.js +8 -22
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.js +3 -2
- package/dist/src/installers/dependency.installer.d.ts +9 -0
- package/dist/src/installers/dependency.installer.js +21 -0
- package/dist/src/installers/index.d.ts +1 -3
- package/dist/src/installers/index.js +1 -3
- package/dist/src/resolvers/architecture.resolver.d.ts +11 -1
- package/dist/src/resolvers/architecture.resolver.js +12 -13
- package/dist/src/resolvers/configuration/configurationResolver.core.d.ts +10 -1
- package/dist/src/resolvers/configuration/configurationResolver.core.js +68 -29
- package/dist/src/resolvers/index.d.ts +1 -0
- package/dist/src/resolvers/index.js +1 -0
- package/dist/src/resolvers/layout/index.d.ts +5 -0
- package/dist/src/resolvers/layout/index.js +5 -0
- package/dist/src/resolvers/layout/projectLayout.core.d.ts +60 -0
- package/dist/src/resolvers/layout/projectLayout.core.js +236 -0
- package/dist/src/resolvers/project.resolver.d.ts +5 -0
- package/dist/src/resolvers/project.resolver.js +10 -6
- package/dist/src/templates/microservice/microservice.template.d.ts +0 -1
- package/dist/src/templates/microservice/microservice.template.js +2 -10
- package/dist/src/templates/modular-monolith/modularMonolith.template.d.ts +0 -1
- package/dist/src/templates/modular-monolith/modularMonolith.template.js +4 -7
- package/dist/src/templates/monolith/monolith.template.d.ts +0 -1
- package/dist/src/templates/monolith/monolith.template.js +4 -7
- package/dist/src/templates/shared/appRuntime.template.d.ts +0 -11
- package/dist/src/templates/shared/appRuntime.template.js +0 -20
- package/dist/src/templates/shared/index.d.ts +1 -0
- package/dist/src/templates/shared/index.js +1 -0
- package/dist/src/templates/shared/pnpm.template.d.ts +27 -0
- package/dist/src/templates/shared/pnpm.template.js +57 -0
- package/dist/src/utils/utils.detect.d.ts +9 -0
- package/dist/src/utils/utils.detect.js +11 -11
- package/dist/src/utils/utils.exec.d.ts +17 -1
- package/dist/src/utils/utils.exec.js +65 -4
- package/package.json +8 -6
- package/dist/src/scripts/postinstall.cjs +0 -77
- package/src/scripts/postinstall.cjs +0 -77
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
* zudojs-cli — Generate Command
|
|
3
3
|
*
|
|
4
4
|
* The `zudojs generate` (alias: `g`) command.
|
|
5
|
-
* Reads
|
|
5
|
+
* Reads the project manifest to determine the architecture and where the
|
|
6
|
+
* backend lives, then places the schematic accordingly.
|
|
6
7
|
*/
|
|
7
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
-
import { join } from "node:path";
|
|
9
8
|
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";
|
|
@@ -21,6 +20,7 @@ import { generateDto } from "../generators/dto/dto.generator.js";
|
|
|
21
20
|
import { generateValidator } from "../generators/validator/validator.generator.js";
|
|
22
21
|
import { CLIGenerationError, CLIValidationError } from "../errors/index.js";
|
|
23
22
|
import { assertGeneratableName, assertSafePathSegment, } from "../utils/utils.name.js";
|
|
23
|
+
import { resolveProjectLayout } from "../resolvers/layout/projectLayout.core.js";
|
|
24
24
|
const VALID_SCHEMATICS = [
|
|
25
25
|
"service",
|
|
26
26
|
"module",
|
|
@@ -82,41 +82,20 @@ function getArchitectureRoot(architecture, schematic, serviceName) {
|
|
|
82
82
|
return "src";
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (existsSync(manifestPath)) {
|
|
98
|
-
try {
|
|
99
|
-
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
100
|
-
if (manifest.architecture)
|
|
101
|
-
return manifest.architecture;
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
// ignore malformed manifest
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// Fall back to the zudojs field in package.json.
|
|
108
|
-
const pkgPath = join(cwd, "package.json");
|
|
109
|
-
if (existsSync(pkgPath)) {
|
|
110
|
-
try {
|
|
111
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
112
|
-
if (pkg.zudojs?.architecture)
|
|
113
|
-
return pkg.zudojs.architecture;
|
|
114
|
-
}
|
|
115
|
-
catch {
|
|
116
|
-
// ignore malformed package.json
|
|
117
|
-
}
|
|
85
|
+
/**
|
|
86
|
+
* Prefix under which backend schematics are generated.
|
|
87
|
+
*
|
|
88
|
+
* A fullstack project keeps its backend in `apps/api`; generating into the
|
|
89
|
+
* workspace root's `src/` produced files no app compiled. Microservice apps
|
|
90
|
+
* are addressed inside getArchitectureRoot, relative to that prefix.
|
|
91
|
+
*/
|
|
92
|
+
function backendPrefix(cwd) {
|
|
93
|
+
const layout = resolveProjectLayout(cwd);
|
|
94
|
+
const [first] = layout?.backendDirs ?? [];
|
|
95
|
+
if (layout?.projectType === "fullstack" && first !== undefined && first !== cwd) {
|
|
96
|
+
return "apps/api/";
|
|
118
97
|
}
|
|
119
|
-
return
|
|
98
|
+
return "";
|
|
120
99
|
}
|
|
121
100
|
export async function runGenerateCommand(context) {
|
|
122
101
|
const schematic = context.values.schematic;
|
|
@@ -143,12 +122,13 @@ export async function runGenerateCommand(context) {
|
|
|
143
122
|
assertSafePathSegment(moduleName, "--module");
|
|
144
123
|
}
|
|
145
124
|
const cwd = context.cwd;
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
125
|
+
const layout = resolveProjectLayout(cwd);
|
|
126
|
+
const architecture = layout?.architecture ?? null;
|
|
127
|
+
if (layout) {
|
|
128
|
+
context.logger.info(`Detected architecture: ${layout.architecture}`);
|
|
149
129
|
}
|
|
150
130
|
else {
|
|
151
|
-
context.logger.warn("No Zudojs project detected (no
|
|
131
|
+
context.logger.warn("No Zudojs project detected (no .zudojs/manifest.json, zudojs.config.ts " +
|
|
152
132
|
"or `zudojs` field in package.json). Run `zudojs create` first.");
|
|
153
133
|
}
|
|
154
134
|
if (architecture === "microservice" && schematic === "service") {
|
|
@@ -175,7 +155,8 @@ export async function runGenerateCommand(context) {
|
|
|
175
155
|
}
|
|
176
156
|
async function runSchematic(schematic, name, options, cwd) {
|
|
177
157
|
try {
|
|
178
|
-
const basePath =
|
|
158
|
+
const basePath = backendPrefix(cwd) +
|
|
159
|
+
getBasePath(options.architecture, schematic, options.module, options.service);
|
|
179
160
|
const dryRun = options.dryRun;
|
|
180
161
|
// In the microservice layout `--service` selected the owning app and is
|
|
181
162
|
// already part of basePath, so it must not nest the schematic again.
|
|
@@ -3,42 +3,73 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The `zudojs info` command.
|
|
5
5
|
*/
|
|
6
|
-
import { readFileSync
|
|
7
|
-
import { join } from "node:path";
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
import { join, relative } from "node:path";
|
|
8
|
+
import { CLI_VERSION } from "../constants/index.js";
|
|
9
|
+
import { checkForNewerVersion } from "../cliVersion/cliVersion.update.js";
|
|
10
|
+
import { resolveProjectLayout } from "../resolvers/layout/projectLayout.core.js";
|
|
8
11
|
export async function runInfoCommand(context) {
|
|
9
|
-
|
|
12
|
+
context.logger.info("Zudojs CLI");
|
|
13
|
+
context.logger.info(` Version: ${CLI_VERSION}`);
|
|
14
|
+
context.logger.info(` Node.js: ${process.version}`);
|
|
15
|
+
const update = await checkForNewerVersion(CLI_VERSION, { env: context.env });
|
|
16
|
+
if (update.latest) {
|
|
17
|
+
context.logger.info(` Update available: ${update.latest} (npm install -g zudojs-cli@latest)`);
|
|
18
|
+
}
|
|
19
|
+
context.logger.info("");
|
|
20
|
+
const layout = resolveProjectLayout(context.cwd);
|
|
21
|
+
if (!layout) {
|
|
22
|
+
context.logger.info("Not in a Zudojs project directory.");
|
|
23
|
+
context.logger.info("Run `zudojs create <project-name>` to create a new project.");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
10
26
|
let projectName = "unknown";
|
|
27
|
+
let projectVersion = "0.0.0";
|
|
11
28
|
try {
|
|
12
|
-
const pkg = JSON.parse(readFileSync(
|
|
13
|
-
projectName = pkg.name ??
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
context.logger.info(`
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
const pkg = JSON.parse(readFileSync(join(layout.root, "package.json"), "utf-8"));
|
|
30
|
+
projectName = pkg.name ?? projectName;
|
|
31
|
+
projectVersion = pkg.version ?? projectVersion;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// A project without a root package.json still has a manifest.
|
|
35
|
+
}
|
|
36
|
+
context.logger.info("Project");
|
|
37
|
+
context.logger.info(` Name: ${projectName}`);
|
|
38
|
+
context.logger.info(` Version: ${projectVersion}`);
|
|
39
|
+
context.logger.info(` Type: ${layout.projectType}`);
|
|
40
|
+
if (layout.backendDirs.length > 0) {
|
|
41
|
+
context.logger.info(` Architecture: ${layout.architecture}`);
|
|
42
|
+
}
|
|
43
|
+
if (layout.frontendFramework) {
|
|
44
|
+
context.logger.info(` Frontend: ${layout.frontendFramework}`);
|
|
45
|
+
}
|
|
46
|
+
context.logger.info(` Package manager: ${layout.packageManager}`);
|
|
47
|
+
if (layout.services.length > 0) {
|
|
48
|
+
context.logger.info(` Services: ${layout.services.join(", ")}`);
|
|
49
|
+
}
|
|
50
|
+
context.logger.info("");
|
|
51
|
+
context.logger.info("Zudojs dependencies");
|
|
52
|
+
const deps = new Map();
|
|
53
|
+
for (const dir of layout.backendDirs) {
|
|
54
|
+
try {
|
|
55
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
|
|
56
|
+
for (const [name, version] of Object.entries(pkg.dependencies ?? {})) {
|
|
57
|
+
if (name.startsWith("@zudojs/")) {
|
|
58
|
+
const where = relative(layout.root, dir);
|
|
59
|
+
deps.set(where === "" ? name : `${name} (${where})`, version);
|
|
60
|
+
}
|
|
33
61
|
}
|
|
34
62
|
}
|
|
63
|
+
catch {
|
|
64
|
+
// Reported by `zudojs doctor`.
|
|
65
|
+
}
|
|
35
66
|
}
|
|
36
|
-
|
|
37
|
-
context.logger.info("
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
context.logger.info(
|
|
67
|
+
if (deps.size === 0) {
|
|
68
|
+
context.logger.info(" (none)");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
for (const [name, version] of [...deps.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
72
|
+
context.logger.info(` ${name}: ${version}`);
|
|
42
73
|
}
|
|
43
74
|
}
|
|
44
75
|
//# sourceMappingURL=info.command.js.map
|
|
@@ -4,8 +4,24 @@
|
|
|
4
4
|
* Constants used by the CLI scaffolding system.
|
|
5
5
|
*/
|
|
6
6
|
export { CLI_VERSION } from "../cliConstant/cliVersion.js";
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Version range every generated project depends on for `@zudojs/*` packages.
|
|
9
|
+
*
|
|
10
|
+
* This is a caret range, not an exact pin, on purpose. The framework
|
|
11
|
+
* packages are released independently, so at any moment their latest
|
|
12
|
+
* versions differ (`@zudojs/openapi` went to 1.1.0 while the rest stayed at
|
|
13
|
+
* 1.0.0). An exact `1.0.0` pin made `pnpm install` in every freshly created
|
|
14
|
+
* project abort with `ERR_PNPM_NO_MATCHING_VERSION` for the package that had
|
|
15
|
+
* moved on. A range resolves each package to its newest compatible release.
|
|
16
|
+
*/
|
|
17
|
+
export declare const ZUDOJS_PACKAGES_VERSION: "^1.0.0";
|
|
18
|
+
/**
|
|
19
|
+
* Packages installed by `zudojs add <feature>`.
|
|
20
|
+
*
|
|
21
|
+
* Also used by `zudojs doctor` to verify that every feature recorded in a
|
|
22
|
+
* project is backed by its package.
|
|
23
|
+
*/
|
|
24
|
+
export declare const FEATURE_PACKAGES: Readonly<Record<string, readonly string[]>>;
|
|
9
25
|
export declare const ARCHITECTURE_CHOICES: readonly [{
|
|
10
26
|
readonly value: "monolith";
|
|
11
27
|
readonly label: "Monolith";
|
|
@@ -4,8 +4,37 @@
|
|
|
4
4
|
* Constants used by the CLI scaffolding system.
|
|
5
5
|
*/
|
|
6
6
|
export { CLI_VERSION } from "../cliConstant/cliVersion.js";
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Version range every generated project depends on for `@zudojs/*` packages.
|
|
9
|
+
*
|
|
10
|
+
* This is a caret range, not an exact pin, on purpose. The framework
|
|
11
|
+
* packages are released independently, so at any moment their latest
|
|
12
|
+
* versions differ (`@zudojs/openapi` went to 1.1.0 while the rest stayed at
|
|
13
|
+
* 1.0.0). An exact `1.0.0` pin made `pnpm install` in every freshly created
|
|
14
|
+
* project abort with `ERR_PNPM_NO_MATCHING_VERSION` for the package that had
|
|
15
|
+
* moved on. A range resolves each package to its newest compatible release.
|
|
16
|
+
*/
|
|
17
|
+
export const ZUDOJS_PACKAGES_VERSION = "^1.0.0";
|
|
18
|
+
/**
|
|
19
|
+
* Packages installed by `zudojs add <feature>`.
|
|
20
|
+
*
|
|
21
|
+
* Also used by `zudojs doctor` to verify that every feature recorded in a
|
|
22
|
+
* project is backed by its package.
|
|
23
|
+
*/
|
|
24
|
+
export const FEATURE_PACKAGES = {
|
|
25
|
+
database: ["@zudojs/database"],
|
|
26
|
+
queue: ["@zudojs/queue"],
|
|
27
|
+
messaging: ["@zudojs/messaging"],
|
|
28
|
+
openapi: ["@zudojs/openapi"],
|
|
29
|
+
observability: ["@zudojs/observability"],
|
|
30
|
+
security: ["@zudojs/security"],
|
|
31
|
+
cache: ["@zudojs/cache"],
|
|
32
|
+
storage: ["@zudojs/storage"],
|
|
33
|
+
// Used to install @zudojs/queue; the scheduler package exists and is the
|
|
34
|
+
// one the feature name promises.
|
|
35
|
+
scheduler: ["@zudojs/scheduler"],
|
|
36
|
+
docs: ["@zudojs/docs"],
|
|
37
|
+
};
|
|
9
38
|
export const ARCHITECTURE_CHOICES = [
|
|
10
39
|
{ value: "monolith", label: "Monolith" },
|
|
11
40
|
{ value: "modular-monolith", label: "Modular Monolith" },
|
|
@@ -14,6 +14,8 @@ export interface FrontendGenerationOptions {
|
|
|
14
14
|
readonly architecture?: "zudojs-standard" | "feature-based" | "minimal" | "framework-default";
|
|
15
15
|
readonly language?: "typescript" | "javascript";
|
|
16
16
|
readonly packageManager?: "pnpm" | "npm" | "yarn" | "bun";
|
|
17
|
+
/** `false` records dependencies in package.json instead of installing. */
|
|
18
|
+
readonly installDeps?: boolean;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Frontend generation result.
|
|
@@ -8,6 +8,8 @@ import { PackageManagerRegistry } from "../../registries/adapter/packageManagerR
|
|
|
8
8
|
import { DependencyResolver } from "../../resolvers/dependency/dependencyResolver.core.js";
|
|
9
9
|
import { CLIGenerationError } from "../../errors/index.js";
|
|
10
10
|
import { runFrontendPipeline } from "./frontendPipeline.js";
|
|
11
|
+
import { writeFileTree } from "../../utils/utils.fileSystem.js";
|
|
12
|
+
import { renderPnpmWorkspaceFile } from "../../templates/shared/pnpm.template.js";
|
|
11
13
|
/**
|
|
12
14
|
* Generates frontend projects using framework adapters.
|
|
13
15
|
*/
|
|
@@ -40,10 +42,19 @@ export class FrontendGenerator {
|
|
|
40
42
|
linting: true,
|
|
41
43
|
formatting: true,
|
|
42
44
|
},
|
|
45
|
+
...(options.installDeps === false ? { skipInstall: true } : {}),
|
|
43
46
|
};
|
|
44
47
|
const files = [];
|
|
45
48
|
const errors = [];
|
|
46
49
|
try {
|
|
50
|
+
// A frontend-only project is its own pnpm root. Vite depends on
|
|
51
|
+
// esbuild, whose install script pnpm 10+ refuses without this list,
|
|
52
|
+
// and the pipeline installs dependencies right after scaffolding.
|
|
53
|
+
if (context.packageManager === "pnpm") {
|
|
54
|
+
await writeFileTree(context.projectPath, {
|
|
55
|
+
"pnpm-workspace.yaml": renderPnpmWorkspaceFile(),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
47
58
|
const outcome = await runFrontendPipeline(adapter, context, this.packageManagerRegistry, this.dependencyResolver);
|
|
48
59
|
files.push(...outcome.files);
|
|
49
60
|
errors.push(...outcome.errors);
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
*
|
|
11
11
|
* @module generators/frontend/pipeline
|
|
12
12
|
*/
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { writeFileTree } from "../../utils/utils.fileSystem.js";
|
|
13
16
|
/**
|
|
14
17
|
* Rejects package names a package manager would read as a flag.
|
|
15
18
|
*
|
|
@@ -65,9 +68,14 @@ export async function runFrontendPipeline(adapter, context, packageManagerRegist
|
|
|
65
68
|
}
|
|
66
69
|
// 5. Install BOTH runtime and dev dependencies. Only devDependencies used
|
|
67
70
|
// to be installed, so every runtime package an adapter declared was
|
|
68
|
-
// resolved and then dropped on the floor.
|
|
71
|
+
// resolved and then dropped on the floor. With `--no-install` the
|
|
72
|
+
// dependencies are written to package.json for a later install.
|
|
69
73
|
const packageManager = packageManagerRegistry.get(context.packageManager);
|
|
70
|
-
if (
|
|
74
|
+
if (context.skipInstall === true) {
|
|
75
|
+
await recordDependencies(context.projectPath, resolution.dependencies, resolution.devDependencies);
|
|
76
|
+
files.push("dependencies");
|
|
77
|
+
}
|
|
78
|
+
else if (packageManager) {
|
|
71
79
|
const deps = resolution.dependencies.map((d) => d.name);
|
|
72
80
|
const devDeps = resolution.devDependencies.map((d) => d.name);
|
|
73
81
|
assertSafePackageNames(deps);
|
|
@@ -90,4 +98,39 @@ export async function runFrontendPipeline(adapter, context, packageManagerRegist
|
|
|
90
98
|
}
|
|
91
99
|
return { files, errors };
|
|
92
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Adds the resolved dependencies to the project's package.json without
|
|
103
|
+
* running a package manager. Existing entries win: the adapter's own
|
|
104
|
+
* template may already pin a version.
|
|
105
|
+
*/
|
|
106
|
+
async function recordDependencies(projectPath, dependencies, devDependencies) {
|
|
107
|
+
const pkgPath = join(projectPath, "package.json");
|
|
108
|
+
let pkg = {};
|
|
109
|
+
if (existsSync(pkgPath)) {
|
|
110
|
+
try {
|
|
111
|
+
pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
pkg = {};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const merge = (target, entries) => {
|
|
118
|
+
const merged = { ...(target ?? {}) };
|
|
119
|
+
for (const entry of entries) {
|
|
120
|
+
if (!(entry.name in merged)) {
|
|
121
|
+
merged[entry.name] = entry.version || "latest";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return merged;
|
|
125
|
+
};
|
|
126
|
+
if (dependencies.length > 0) {
|
|
127
|
+
pkg.dependencies = merge(pkg.dependencies, dependencies);
|
|
128
|
+
}
|
|
129
|
+
if (devDependencies.length > 0) {
|
|
130
|
+
pkg.devDependencies = merge(pkg.devDependencies, devDependencies);
|
|
131
|
+
}
|
|
132
|
+
await writeFileTree(projectPath, {
|
|
133
|
+
"package.json": JSON.stringify(pkg, null, 2) + "\n",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
93
136
|
//# sourceMappingURL=frontendPipeline.js.map
|
|
@@ -12,6 +12,8 @@ import type { ProjectConfiguration } from "../../types/projectConfiguration.type
|
|
|
12
12
|
export interface FullstackGenerationContext {
|
|
13
13
|
readonly project: ProjectConfiguration;
|
|
14
14
|
readonly projectPath: string;
|
|
15
|
+
/** `false` records frontend dependencies instead of installing them. */
|
|
16
|
+
readonly installDeps?: boolean;
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* Fullstack generation result.
|
|
@@ -12,6 +12,7 @@ import { PackageManagerRegistry } from "../../registries/adapter/packageManagerR
|
|
|
12
12
|
import { DependencyResolver } from "../../resolvers/dependency/dependencyResolver.core.js";
|
|
13
13
|
import { runFrontendPipeline } from "../frontend/frontendPipeline.js";
|
|
14
14
|
import { writeFileTree } from "../../utils/utils.fileSystem.js";
|
|
15
|
+
import { renderPnpmWorkspaceFile } from "../../templates/shared/pnpm.template.js";
|
|
15
16
|
/**
|
|
16
17
|
* Composes fullstack projects by orchestrating multiple generators.
|
|
17
18
|
*/
|
|
@@ -33,7 +34,7 @@ export class FullstackComposer {
|
|
|
33
34
|
try {
|
|
34
35
|
// 1. Create workspace structure
|
|
35
36
|
await this.createWorkspace(context);
|
|
36
|
-
files.push("package.json"
|
|
37
|
+
files.push("package.json");
|
|
37
38
|
if (this.getPackageManager(context) === "pnpm") {
|
|
38
39
|
files.push("pnpm-workspace.yaml");
|
|
39
40
|
}
|
|
@@ -90,31 +91,15 @@ export class FullstackComposer {
|
|
|
90
91
|
typescript: "^5.0.0",
|
|
91
92
|
},
|
|
92
93
|
};
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
const literal = (value) => JSON.stringify(value);
|
|
98
|
-
const frontendBlock = context.project.frontend
|
|
99
|
-
? `
|
|
100
|
-
frontend: {
|
|
101
|
-
framework: ${literal(context.project.frontend.framework)},
|
|
102
|
-
},`
|
|
103
|
-
: "";
|
|
104
|
-
const zudojsConfig = `export default {
|
|
105
|
-
name: ${literal(context.project.name)},
|
|
106
|
-
projectType: "fullstack",
|
|
107
|
-
architecture: ${literal(context.project.backend?.architecture ?? "monolith")},${frontendBlock}
|
|
108
|
-
};
|
|
109
|
-
`;
|
|
94
|
+
// The project's type, architecture and frontend are recorded in
|
|
95
|
+
// .zudojs/manifest.json by the create command; no zudojs.config.ts is
|
|
96
|
+
// written. The name reaches package.json through JSON.stringify, so a
|
|
97
|
+
// quote or newline in it stays data.
|
|
110
98
|
const files = {
|
|
111
99
|
"package.json": JSON.stringify(rootPackageJson, null, 2) + "\n",
|
|
112
|
-
"zudojs.config.ts": zudojsConfig,
|
|
113
100
|
};
|
|
114
101
|
if (packageManager === "pnpm") {
|
|
115
|
-
files["pnpm-workspace.yaml"] =
|
|
116
|
-
${workspaceGlobs.map((g) => ` - "${g}"`).join("\n")}
|
|
117
|
-
`;
|
|
102
|
+
files["pnpm-workspace.yaml"] = renderPnpmWorkspaceFile(workspaceGlobs);
|
|
118
103
|
}
|
|
119
104
|
await writeFileTree(context.projectPath, files);
|
|
120
105
|
}
|
|
@@ -172,6 +157,7 @@ export type Timestamp = string;
|
|
|
172
157
|
formatting: true,
|
|
173
158
|
envValidation: true,
|
|
174
159
|
},
|
|
160
|
+
...(context.installDeps === false ? { skipInstall: true } : {}),
|
|
175
161
|
};
|
|
176
162
|
// The same pipeline the frontend-only path uses: availability check,
|
|
177
163
|
// scaffold, structure, integration, dependency resolution + install, and
|
package/dist/src/index.d.ts
CHANGED
|
@@ -23,8 +23,8 @@ export { CLI_NAME, CLI_DEFAULTS, CLI_COMMANDS, CLI_ALIASES, CLI_OPTION_PREFIXES,
|
|
|
23
23
|
export { CLIError, isCLIError, normalizeCLIError, getCLIExitCode, getCLIErrorCode, type CLIErrorOptions, CommandNotFoundError, DuplicateCommandError, InvalidCommandNameError, InvalidArgumentsError, MissingArgumentError, InvalidOptionError, InvalidOptionNameError, MissingOptionValueError, DuplicateOptionError, CLIExecutionError, CLIPermissionError, CLIInterruptedError, CLIConfigurationError, } from "./cliError/index.js";
|
|
24
24
|
export { CLICommandRegistry, CLICommandBuilder, createCommand, command, executeCommand, validateCommand, isCLICommand, sortCommands, } from "./cliCommand/index.js";
|
|
25
25
|
export { CLIParser, type CLIParserOptions, parseCLIArguments, parseOptionValue, parseBoolean, isOption, isLongOption, isShortOption, resolveCommand, normalizeCLIValue, } from "./cliParser/index.js";
|
|
26
|
-
export { ZudojsCLI, createCLI, createCLIWriter, registerCLIInterruptHandler, } from "./cliApplication/index.js";
|
|
27
|
-
export { getCLIVersion, formatCLIVersion, getVersionString, isValidVersion, compareVersions, parseVersion, isCompatibleVersion, type CLIVersionInfo, } from "./cliVersion/index.js";
|
|
26
|
+
export { ZudojsCLI, createCLI, createCLIWriter, createCLILogger, formatCLILogLine, registerCLIInterruptHandler, type CLILoggerOptions, } from "./cliApplication/index.js";
|
|
27
|
+
export { getCLIVersion, formatCLIVersion, getVersionString, isValidVersion, compareVersions, parseVersion, isCompatibleVersion, checkForNewerVersion, isUpdateCheckDisabled, type CLIVersionInfo, type UpdateCheckOptions, type UpdateCheckResult, } from "./cliVersion/index.js";
|
|
28
28
|
export type { FrontendAdapter, FrontendGenerationContext, FrontendFeatures, DependencyRequirement, ValidationResult, } from "./adapters/frontend/index.js";
|
|
29
29
|
export { ReactAdapter, NextAdapter, VueAdapter, NuxtAdapter, AngularAdapter, SvelteAdapter, SvelteKitAdapter, AstroAdapter, VanillaAdapter, FlutterAdapter, ReactNativeAdapter, } from "./adapters/frontend/index.js";
|
|
30
30
|
export { PnpmAdapter, NpmAdapter, YarnAdapter, BunAdapter, } from "./adapters/package-managers/index.js";
|
|
@@ -34,6 +34,7 @@ export { detectArchitecture } from "./resolvers/index.js";
|
|
|
34
34
|
export { resolveProjectPath, findProjectRoot, } from "./resolvers/project.resolver.js";
|
|
35
35
|
export { CapabilityResolver, type CapabilityDependency, type CapabilityResolutionResult, } from "./resolvers/capability/index.js";
|
|
36
36
|
export { ConfigurationResolver, type ResolvedConfiguration, } from "./resolvers/configuration/index.js";
|
|
37
|
+
export { resolveProjectLayout, detectPackageManager, type ProjectLayout, type ProjectLayoutType, type ProjectLayoutSource, } from "./resolvers/layout/index.js";
|
|
37
38
|
export { EnvironmentValidator, type EnvironmentCheck, type EnvironmentValidationResult, } from "./validators/index.js";
|
|
38
39
|
export { ProjectValidator, type ProjectCheck, type ProjectValidationResult, } from "./validators/index.js";
|
|
39
40
|
export { CompatibilityValidator, type CompatibilityCheck, type CompatibilityResult, } from "./validators/compatibility/index.js";
|
package/dist/src/index.js
CHANGED
|
@@ -27,9 +27,9 @@ export { CLICommandRegistry, CLICommandBuilder, createCommand, command, executeC
|
|
|
27
27
|
// Parser
|
|
28
28
|
export { CLIParser, parseCLIArguments, parseOptionValue, parseBoolean, isOption, isLongOption, isShortOption, resolveCommand, normalizeCLIValue, } from "./cliParser/index.js";
|
|
29
29
|
// Application
|
|
30
|
-
export { ZudojsCLI, createCLI, createCLIWriter, registerCLIInterruptHandler, } from "./cliApplication/index.js";
|
|
30
|
+
export { ZudojsCLI, createCLI, createCLIWriter, createCLILogger, formatCLILogLine, registerCLIInterruptHandler, } from "./cliApplication/index.js";
|
|
31
31
|
// Version
|
|
32
|
-
export { getCLIVersion, formatCLIVersion, getVersionString, isValidVersion, compareVersions, parseVersion, isCompatibleVersion, } from "./cliVersion/index.js";
|
|
32
|
+
export { getCLIVersion, formatCLIVersion, getVersionString, isValidVersion, compareVersions, parseVersion, isCompatibleVersion, checkForNewerVersion, isUpdateCheckDisabled, } from "./cliVersion/index.js";
|
|
33
33
|
export { ReactAdapter, NextAdapter, VueAdapter, NuxtAdapter, AngularAdapter, SvelteAdapter, SvelteKitAdapter, AstroAdapter, VanillaAdapter, FlutterAdapter, ReactNativeAdapter, } from "./adapters/frontend/index.js";
|
|
34
34
|
export { PnpmAdapter, NpmAdapter, YarnAdapter, BunAdapter, } from "./adapters/package-managers/index.js";
|
|
35
35
|
export { PostgresAdapter, MySqlAdapter, SqliteAdapter, } from "./adapters/databases/index.js";
|
|
@@ -39,6 +39,7 @@ export { detectArchitecture } from "./resolvers/index.js";
|
|
|
39
39
|
export { resolveProjectPath, findProjectRoot, } from "./resolvers/project.resolver.js";
|
|
40
40
|
export { CapabilityResolver, } from "./resolvers/capability/index.js";
|
|
41
41
|
export { ConfigurationResolver, } from "./resolvers/configuration/index.js";
|
|
42
|
+
export { resolveProjectLayout, detectPackageManager, } from "./resolvers/layout/index.js";
|
|
42
43
|
// Validators
|
|
43
44
|
export { EnvironmentValidator, } from "./validators/index.js";
|
|
44
45
|
export { ProjectValidator, } from "./validators/index.js";
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { PackageManager } from "../types/index.js";
|
|
2
2
|
export declare function getInstallCommand(packageManager: PackageManager): [string, ...string[]];
|
|
3
3
|
export declare function getAddCommand(packageManager: PackageManager, pkg: string): [string, ...string[]];
|
|
4
|
+
/**
|
|
5
|
+
* The command that runs a `package.json` script with the given manager.
|
|
6
|
+
*
|
|
7
|
+
* Generated projects declare their dev/build tooling (`tsx`, `vite`, `ng`)
|
|
8
|
+
* as devDependencies, so the binaries live in `node_modules/.bin` and are
|
|
9
|
+
* not on the user's PATH. Running the script through the package manager is
|
|
10
|
+
* what puts them there.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getRunScriptCommand(packageManager: PackageManager, script: string): [string, ...string[]];
|
|
4
13
|
export declare function installDependencies(packageManager: PackageManager, cwd: string): Promise<void>;
|
|
5
14
|
//# sourceMappingURL=dependency.installer.d.ts.map
|
|
@@ -27,6 +27,27 @@ export function getAddCommand(packageManager, pkg) {
|
|
|
27
27
|
return ["pnpm", "add", pkg];
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* The command that runs a `package.json` script with the given manager.
|
|
32
|
+
*
|
|
33
|
+
* Generated projects declare their dev/build tooling (`tsx`, `vite`, `ng`)
|
|
34
|
+
* as devDependencies, so the binaries live in `node_modules/.bin` and are
|
|
35
|
+
* not on the user's PATH. Running the script through the package manager is
|
|
36
|
+
* what puts them there.
|
|
37
|
+
*/
|
|
38
|
+
export function getRunScriptCommand(packageManager, script) {
|
|
39
|
+
switch (packageManager) {
|
|
40
|
+
case "npm":
|
|
41
|
+
return ["npm", "run", script];
|
|
42
|
+
case "yarn":
|
|
43
|
+
return ["yarn", "run", script];
|
|
44
|
+
case "bun":
|
|
45
|
+
return ["bun", "run", script];
|
|
46
|
+
case "pnpm":
|
|
47
|
+
default:
|
|
48
|
+
return ["pnpm", "run", script];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
30
51
|
export async function installDependencies(packageManager, cwd) {
|
|
31
52
|
const [file, ...args] = getInstallCommand(packageManager);
|
|
32
53
|
await runStreaming(file, args, cwd);
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* zudojs-cli — Installers
|
|
3
|
-
*
|
|
4
|
-
* Dependency installation utilities.
|
|
5
3
|
*/
|
|
6
|
-
export {
|
|
4
|
+
export { getInstallCommand, getAddCommand, getRunScriptCommand, installDependencies, } from "./dependency.installer.js";
|
|
7
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* zudojs-cli — Installers
|
|
3
|
-
*
|
|
4
|
-
* Dependency installation utilities.
|
|
5
3
|
*/
|
|
6
|
-
export {
|
|
4
|
+
export { getInstallCommand, getAddCommand, getRunScriptCommand, installDependencies, } from "./dependency.installer.js";
|
|
7
5
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
declare const ARCHITECTURES: readonly ["monolith", "modular-monolith", "microservice"];
|
|
2
|
+
type Architecture = (typeof ARCHITECTURES)[number];
|
|
3
|
+
/**
|
|
4
|
+
* Detects the backend architecture of the project at `cwd`.
|
|
5
|
+
*
|
|
6
|
+
* Reads the manifest (then the legacy config, then package.json) through the
|
|
7
|
+
* shared layout resolver and falls back to directory heuristics for projects
|
|
8
|
+
* that were not created by the CLI.
|
|
9
|
+
*/
|
|
10
|
+
export declare function detectArchitecture(cwd: string): Promise<Architecture | null>;
|
|
11
|
+
export {};
|
|
2
12
|
//# sourceMappingURL=architecture.resolver.d.ts.map
|
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
1
|
import { existsSync } from "node:fs";
|
|
3
2
|
import { join } from "node:path";
|
|
3
|
+
import { resolveProjectLayout } from "./layout/projectLayout.core.js";
|
|
4
|
+
const ARCHITECTURES = ["monolith", "modular-monolith", "microservice"];
|
|
5
|
+
/**
|
|
6
|
+
* Detects the backend architecture of the project at `cwd`.
|
|
7
|
+
*
|
|
8
|
+
* Reads the manifest (then the legacy config, then package.json) through the
|
|
9
|
+
* shared layout resolver and falls back to directory heuristics for projects
|
|
10
|
+
* that were not created by the CLI.
|
|
11
|
+
*/
|
|
4
12
|
export async function detectArchitecture(cwd) {
|
|
5
|
-
const
|
|
6
|
-
if (
|
|
7
|
-
|
|
8
|
-
const content = await readFile(configPath, "utf-8");
|
|
9
|
-
const match = content.match(/architecture:\s*["'](monolith|modular-monolith|microservice)["']/);
|
|
10
|
-
if (match) {
|
|
11
|
-
return match[1];
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
// fall through
|
|
16
|
-
}
|
|
13
|
+
const recorded = resolveProjectLayout(cwd)?.architecture;
|
|
14
|
+
if (recorded && ARCHITECTURES.includes(recorded)) {
|
|
15
|
+
return recorded;
|
|
17
16
|
}
|
|
18
17
|
if (existsSync(join(cwd, "pnpm-workspace.yaml"))) {
|
|
19
18
|
const gateway = existsSync(join(cwd, "apps/gateway"));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* zudojs-cli — Configuration Resolver
|
|
3
3
|
*
|
|
4
|
-
* Resolves
|
|
4
|
+
* Resolves project configuration from the sources `zudojs create` writes.
|
|
5
5
|
*/
|
|
6
6
|
export interface ResolvedConfiguration {
|
|
7
7
|
readonly projectName: string;
|
|
@@ -16,7 +16,16 @@ export interface ResolvedConfiguration {
|
|
|
16
16
|
readonly features: readonly string[];
|
|
17
17
|
}
|
|
18
18
|
export declare class ConfigurationResolver {
|
|
19
|
+
/**
|
|
20
|
+
* Resolves the configuration of the project at `cwd`, or `null` when the
|
|
21
|
+
* directory is not a Zudojs project.
|
|
22
|
+
*
|
|
23
|
+
* Only `zudojs.config.ts` used to be consulted, so every project created
|
|
24
|
+
* since the manifest became the source of truth resolved to `null`.
|
|
25
|
+
*/
|
|
19
26
|
resolve(cwd: string): ResolvedConfiguration | null;
|
|
27
|
+
private readJson;
|
|
28
|
+
private readLegacyConfig;
|
|
20
29
|
private extractValue;
|
|
21
30
|
private extractArray;
|
|
22
31
|
}
|