zudojs-cli 1.0.0 → 1.1.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 +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 +12 -10
- package/dist/src/scripts/postinstall.cjs +0 -77
- package/src/scripts/postinstall.cjs +0 -77
|
@@ -1,50 +1,89 @@
|
|
|
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
|
import { existsSync, readFileSync } from "node:fs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
|
+
import { resolveProjectLayout } from "../layout/projectLayout.core.js";
|
|
8
9
|
export class ConfigurationResolver {
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the configuration of the project at `cwd`, or `null` when the
|
|
12
|
+
* directory is not a Zudojs project.
|
|
13
|
+
*
|
|
14
|
+
* Only `zudojs.config.ts` used to be consulted, so every project created
|
|
15
|
+
* since the manifest became the source of truth resolved to `null`.
|
|
16
|
+
*/
|
|
9
17
|
resolve(cwd) {
|
|
10
|
-
const
|
|
11
|
-
if (!
|
|
18
|
+
const layout = resolveProjectLayout(cwd);
|
|
19
|
+
if (!layout)
|
|
20
|
+
return null;
|
|
21
|
+
const manifest = this.readJson(join(cwd, ".zudojs", "manifest.json"));
|
|
22
|
+
const pkg = this.readJson(join(cwd, "package.json"));
|
|
23
|
+
const legacy = this.readLegacyConfig(cwd);
|
|
24
|
+
const features = manifest?.capabilities ??
|
|
25
|
+
(Array.isArray(pkg?.zudojs?.features)
|
|
26
|
+
? pkg.zudojs.features.filter((feature) => typeof feature === "string")
|
|
27
|
+
: undefined) ??
|
|
28
|
+
this.extractArray(legacy, "features") ??
|
|
29
|
+
[];
|
|
30
|
+
const frontendArchitecture = manifest?.frontend?.architecture ??
|
|
31
|
+
this.extractValue(legacy, "architecture", "frontend");
|
|
32
|
+
const language = this.extractValue(legacy, "language");
|
|
33
|
+
return {
|
|
34
|
+
projectName: this.extractValue(legacy, "name") ?? pkg?.name ?? "unknown",
|
|
35
|
+
projectType: layout.projectType,
|
|
36
|
+
architecture: layout.architecture,
|
|
37
|
+
packageManager: layout.packageManager,
|
|
38
|
+
database: manifest?.database?.provider ??
|
|
39
|
+
this.extractValue(legacy, "provider") ??
|
|
40
|
+
"postgresql",
|
|
41
|
+
api: manifest?.backend?.api ?? this.extractValue(legacy, "api") ?? "rest",
|
|
42
|
+
...(layout.frontendFramework !== undefined
|
|
43
|
+
? { frontend: layout.frontendFramework }
|
|
44
|
+
: {}),
|
|
45
|
+
...(frontendArchitecture !== undefined ? { frontendArchitecture } : {}),
|
|
46
|
+
...(language !== undefined ? { language } : {}),
|
|
47
|
+
features,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
readJson(path) {
|
|
51
|
+
if (!existsSync(path))
|
|
12
52
|
return null;
|
|
13
|
-
}
|
|
14
53
|
try {
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
projectName: this.extractValue(content, "name") ?? "unknown",
|
|
18
|
-
projectType: this.extractValue(content, "type") ?? "backend",
|
|
19
|
-
architecture: this.extractValue(content, "architecture") ?? "monolith",
|
|
20
|
-
packageManager: this.extractValue(content, "packageManager") ?? "pnpm",
|
|
21
|
-
database: this.extractValue(content, "provider") ?? "postgresql",
|
|
22
|
-
api: this.extractValue(content, "api") ?? "rest",
|
|
23
|
-
frontend: this.extractValue(content, "framework"),
|
|
24
|
-
frontendArchitecture: this.extractValue(content, "architecture", "frontend"),
|
|
25
|
-
language: this.extractValue(content, "language"),
|
|
26
|
-
features: this.extractArray(content, "features") ?? [],
|
|
27
|
-
};
|
|
54
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
28
55
|
}
|
|
29
56
|
catch {
|
|
30
57
|
return null;
|
|
31
58
|
}
|
|
32
59
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
60
|
+
readLegacyConfig(cwd) {
|
|
61
|
+
for (const name of ["zudojs.config.ts", "zudojs.config.js"]) {
|
|
62
|
+
const path = join(cwd, name);
|
|
63
|
+
if (!existsSync(path))
|
|
64
|
+
continue;
|
|
65
|
+
try {
|
|
66
|
+
return readFileSync(path, "utf-8");
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return "";
|
|
70
|
+
}
|
|
40
71
|
}
|
|
41
|
-
|
|
42
|
-
|
|
72
|
+
return "";
|
|
73
|
+
}
|
|
74
|
+
extractValue(content, key, section) {
|
|
75
|
+
if (content.length === 0)
|
|
76
|
+
return undefined;
|
|
77
|
+
const pattern = section
|
|
78
|
+
? new RegExp(`${section}[\\s\\S]*?${key}:\\s*["']([^"']+)["']`)
|
|
79
|
+
: new RegExp(`(?<![\\w])${key}:\\s*["']([^"']+)["']`);
|
|
80
|
+
return content.match(pattern)?.[1];
|
|
43
81
|
}
|
|
44
82
|
extractArray(content, key) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
83
|
+
if (content.length === 0)
|
|
84
|
+
return undefined;
|
|
85
|
+
const match = content.match(new RegExp(`${key}:\\s*\\[([^\\]]+)\\]`));
|
|
86
|
+
if (!match?.[1])
|
|
48
87
|
return undefined;
|
|
49
88
|
return match[1]
|
|
50
89
|
.split(",")
|
|
@@ -7,4 +7,5 @@ export { detectArchitecture } from "./architecture.resolver.js";
|
|
|
7
7
|
export { resolveProjectPath, findProjectRoot } from "./project.resolver.js";
|
|
8
8
|
export { CapabilityResolver, type CapabilityDependency, type CapabilityResolutionResult, } from "./capability/index.js";
|
|
9
9
|
export { ConfigurationResolver, type ResolvedConfiguration, } from "./configuration/index.js";
|
|
10
|
+
export { resolveProjectLayout, detectPackageManager, type ProjectLayout, type ProjectLayoutType, type ProjectLayoutSource, } from "./layout/index.js";
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -7,4 +7,5 @@ export { detectArchitecture } from "./architecture.resolver.js";
|
|
|
7
7
|
export { resolveProjectPath, findProjectRoot } from "./project.resolver.js";
|
|
8
8
|
export { CapabilityResolver, } from "./capability/index.js";
|
|
9
9
|
export { ConfigurationResolver, } from "./configuration/index.js";
|
|
10
|
+
export { resolveProjectLayout, detectPackageManager, } from "./layout/index.js";
|
|
10
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zudojs-cli — Project Layout Resolver
|
|
3
|
+
*
|
|
4
|
+
* One place that answers "what kind of project is this directory, and where
|
|
5
|
+
* do its apps live?". Every follow-up command (`dev`, `add`, `generate`,
|
|
6
|
+
* `doctor`, `info`, `build`) used to answer that question on its own, and
|
|
7
|
+
* each answer was different: `dev` read `zudojs.config.ts` first and only
|
|
8
|
+
* fell back to the manifest, `generate` never looked past the workspace
|
|
9
|
+
* root for a fullstack project, `doctor` required a `tsconfig.json` at the
|
|
10
|
+
* root of a workspace that has none, and `add` wrote into the workspace root
|
|
11
|
+
* `package.json` where no app can import from.
|
|
12
|
+
*
|
|
13
|
+
* Resolution order:
|
|
14
|
+
*
|
|
15
|
+
* 1. `.zudojs/manifest.json` — written by `zudojs create`, machine-managed.
|
|
16
|
+
* 2. `zudojs.config.ts` / `.js` — older projects; read with a regex, never
|
|
17
|
+
* executed.
|
|
18
|
+
* 3. the `zudojs` block in `package.json`.
|
|
19
|
+
*
|
|
20
|
+
* @module resolvers/layout
|
|
21
|
+
*/
|
|
22
|
+
import type { PackageManager } from "../../types/index.js";
|
|
23
|
+
/** Project type recorded by `zudojs create`. */
|
|
24
|
+
export type ProjectLayoutType = "backend" | "frontend" | "fullstack";
|
|
25
|
+
/** Where the description of the project came from. */
|
|
26
|
+
export type ProjectLayoutSource = "manifest" | "config" | "package.json";
|
|
27
|
+
/** The resolved shape of a Zudojs project on disk. */
|
|
28
|
+
export interface ProjectLayout {
|
|
29
|
+
/** Absolute project root: the directory holding the manifest or config. */
|
|
30
|
+
readonly root: string;
|
|
31
|
+
readonly projectType: ProjectLayoutType;
|
|
32
|
+
/** Backend architecture (`monolith`, `modular-monolith`, `microservice`). */
|
|
33
|
+
readonly architecture: string;
|
|
34
|
+
readonly packageManager: PackageManager;
|
|
35
|
+
/** Whether the root is a workspace (pnpm-workspace.yaml or `workspaces`). */
|
|
36
|
+
readonly isWorkspace: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Absolute directories that hold a backend app, each with its own
|
|
39
|
+
* `package.json`. Empty for a frontend-only project. A monolith has one
|
|
40
|
+
* (the root, or `apps/api` in a fullstack workspace); a microservice
|
|
41
|
+
* project has the gateway plus one per service.
|
|
42
|
+
*/
|
|
43
|
+
readonly backendDirs: readonly string[];
|
|
44
|
+
/** Absolute directory of the frontend app, when the project has one. */
|
|
45
|
+
readonly frontendDir?: string;
|
|
46
|
+
readonly frontendFramework?: string;
|
|
47
|
+
/** Service names of a microservice project. */
|
|
48
|
+
readonly services: readonly string[];
|
|
49
|
+
readonly source: ProjectLayoutSource;
|
|
50
|
+
}
|
|
51
|
+
/** Detects the package manager from lock files and workspace markers. */
|
|
52
|
+
export declare function detectPackageManager(root: string): PackageManager;
|
|
53
|
+
/**
|
|
54
|
+
* Resolves the layout of the Zudojs project rooted at `cwd`.
|
|
55
|
+
*
|
|
56
|
+
* Returns `null` when the directory is not a Zudojs project: no manifest,
|
|
57
|
+
* no legacy config and no `zudojs` block in `package.json`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveProjectLayout(cwd: string): ProjectLayout | null;
|
|
60
|
+
//# sourceMappingURL=projectLayout.core.d.ts.map
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zudojs-cli — Project Layout Resolver
|
|
3
|
+
*
|
|
4
|
+
* One place that answers "what kind of project is this directory, and where
|
|
5
|
+
* do its apps live?". Every follow-up command (`dev`, `add`, `generate`,
|
|
6
|
+
* `doctor`, `info`, `build`) used to answer that question on its own, and
|
|
7
|
+
* each answer was different: `dev` read `zudojs.config.ts` first and only
|
|
8
|
+
* fell back to the manifest, `generate` never looked past the workspace
|
|
9
|
+
* root for a fullstack project, `doctor` required a `tsconfig.json` at the
|
|
10
|
+
* root of a workspace that has none, and `add` wrote into the workspace root
|
|
11
|
+
* `package.json` where no app can import from.
|
|
12
|
+
*
|
|
13
|
+
* Resolution order:
|
|
14
|
+
*
|
|
15
|
+
* 1. `.zudojs/manifest.json` — written by `zudojs create`, machine-managed.
|
|
16
|
+
* 2. `zudojs.config.ts` / `.js` — older projects; read with a regex, never
|
|
17
|
+
* executed.
|
|
18
|
+
* 3. the `zudojs` block in `package.json`.
|
|
19
|
+
*
|
|
20
|
+
* @module resolvers/layout
|
|
21
|
+
*/
|
|
22
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import { SAFE_PATH_SEGMENT } from "../../utils/utils.name.js";
|
|
25
|
+
const PROJECT_TYPES = [
|
|
26
|
+
"backend",
|
|
27
|
+
"frontend",
|
|
28
|
+
"fullstack",
|
|
29
|
+
];
|
|
30
|
+
const PACKAGE_MANAGERS = [
|
|
31
|
+
"pnpm",
|
|
32
|
+
"npm",
|
|
33
|
+
"yarn",
|
|
34
|
+
"bun",
|
|
35
|
+
];
|
|
36
|
+
function readJson(path) {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
39
|
+
return typeof parsed === "object" && parsed !== null
|
|
40
|
+
? parsed
|
|
41
|
+
: null;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function asString(value) {
|
|
48
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
49
|
+
}
|
|
50
|
+
function readManifest(root) {
|
|
51
|
+
const manifest = readJson(join(root, ".zudojs", "manifest.json"));
|
|
52
|
+
if (!manifest)
|
|
53
|
+
return null;
|
|
54
|
+
const backend = manifest.backend;
|
|
55
|
+
const frontend = manifest.frontend;
|
|
56
|
+
const workspace = manifest.workspace;
|
|
57
|
+
const services = Array.isArray(manifest.services)
|
|
58
|
+
? manifest.services.filter((service) => typeof service === "string")
|
|
59
|
+
: undefined;
|
|
60
|
+
return {
|
|
61
|
+
projectType: asString(manifest.projectType),
|
|
62
|
+
architecture: asString(backend?.architecture) ?? asString(manifest.architecture),
|
|
63
|
+
packageManager: asString(workspace?.packageManager),
|
|
64
|
+
frontendFramework: asString(frontend?.framework),
|
|
65
|
+
services,
|
|
66
|
+
source: "manifest",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Reads the legacy `zudojs.config.ts` with a regex. The file is user-owned
|
|
71
|
+
* TypeScript and is never executed.
|
|
72
|
+
*/
|
|
73
|
+
function readLegacyConfig(root) {
|
|
74
|
+
for (const name of ["zudojs.config.ts", "zudojs.config.js"]) {
|
|
75
|
+
const path = join(root, name);
|
|
76
|
+
if (!existsSync(path))
|
|
77
|
+
continue;
|
|
78
|
+
let content;
|
|
79
|
+
try {
|
|
80
|
+
content = readFileSync(path, "utf-8");
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const projectType = content.match(/projectType:\s*["'](\w+)["']/)?.[1];
|
|
86
|
+
const architecture = content.match(/architecture:\s*["'](\w[\w-]*)["']/)?.[1];
|
|
87
|
+
const frontendFramework = content.match(/frontend:\s*\{[\s\S]*?framework:\s*["']([^"']+)["']/)?.[1];
|
|
88
|
+
return {
|
|
89
|
+
projectType,
|
|
90
|
+
architecture,
|
|
91
|
+
frontendFramework,
|
|
92
|
+
source: "config",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
function readPackageJsonBlock(root) {
|
|
98
|
+
const pkg = readJson(join(root, "package.json"));
|
|
99
|
+
const block = pkg?.zudojs;
|
|
100
|
+
if (typeof block !== "object" || block === null)
|
|
101
|
+
return null;
|
|
102
|
+
const zudojs = block;
|
|
103
|
+
return {
|
|
104
|
+
projectType: asString(zudojs.projectType),
|
|
105
|
+
architecture: asString(zudojs.architecture),
|
|
106
|
+
frontendFramework: asString(zudojs.frontend),
|
|
107
|
+
source: "package.json",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Detects the package manager from lock files and workspace markers. */
|
|
111
|
+
export function detectPackageManager(root) {
|
|
112
|
+
if (existsSync(join(root, "pnpm-lock.yaml")) ||
|
|
113
|
+
existsSync(join(root, "pnpm-workspace.yaml"))) {
|
|
114
|
+
return "pnpm";
|
|
115
|
+
}
|
|
116
|
+
if (existsSync(join(root, "yarn.lock")))
|
|
117
|
+
return "yarn";
|
|
118
|
+
if (existsSync(join(root, "bun.lock")) || existsSync(join(root, "bun.lockb")))
|
|
119
|
+
return "bun";
|
|
120
|
+
return "npm";
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Whether `root` is a multi-package workspace.
|
|
124
|
+
*
|
|
125
|
+
* A `pnpm-workspace.yaml` alone is not enough: pnpm 10+ also uses that file
|
|
126
|
+
* for settings (the build-script allow-list every generated project
|
|
127
|
+
* carries), so only one that declares `packages:` makes a workspace.
|
|
128
|
+
*/
|
|
129
|
+
function isWorkspaceRoot(root) {
|
|
130
|
+
const workspaceFile = join(root, "pnpm-workspace.yaml");
|
|
131
|
+
if (existsSync(workspaceFile)) {
|
|
132
|
+
try {
|
|
133
|
+
if (/^packages:/m.test(readFileSync(workspaceFile, "utf-8")))
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// unreadable: fall through to package.json
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const pkg = readJson(join(root, "package.json"));
|
|
141
|
+
return pkg?.workspaces !== undefined;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Lists the service apps under `<backendRoot>/apps/services`, preferring the
|
|
145
|
+
* names recorded in the manifest and falling back to the directory listing.
|
|
146
|
+
*/
|
|
147
|
+
function listServiceDirs(backendRoot, recorded) {
|
|
148
|
+
const servicesRoot = join(backendRoot, "apps", "services");
|
|
149
|
+
let names;
|
|
150
|
+
if (recorded && recorded.length > 0) {
|
|
151
|
+
// Names come from a file on disk and become path segments.
|
|
152
|
+
names = recorded.filter((name) => SAFE_PATH_SEGMENT.test(name));
|
|
153
|
+
}
|
|
154
|
+
else if (existsSync(servicesRoot)) {
|
|
155
|
+
names = readdirSync(servicesRoot, { withFileTypes: true })
|
|
156
|
+
.filter((entry) => entry.isDirectory())
|
|
157
|
+
.map((entry) => entry.name)
|
|
158
|
+
.filter((name) => SAFE_PATH_SEGMENT.test(name));
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
names = [];
|
|
162
|
+
}
|
|
163
|
+
const dirs = names
|
|
164
|
+
.map((name) => join(servicesRoot, name))
|
|
165
|
+
.filter((dir) => existsSync(join(dir, "package.json")));
|
|
166
|
+
return { names, dirs };
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Resolves the layout of the Zudojs project rooted at `cwd`.
|
|
170
|
+
*
|
|
171
|
+
* Returns `null` when the directory is not a Zudojs project: no manifest,
|
|
172
|
+
* no legacy config and no `zudojs` block in `package.json`.
|
|
173
|
+
*/
|
|
174
|
+
export function resolveProjectLayout(cwd) {
|
|
175
|
+
const raw = readManifest(cwd) ?? readLegacyConfig(cwd) ?? readPackageJsonBlock(cwd);
|
|
176
|
+
if (!raw)
|
|
177
|
+
return null;
|
|
178
|
+
const projectType = PROJECT_TYPES.includes(raw.projectType)
|
|
179
|
+
? raw.projectType
|
|
180
|
+
: "backend";
|
|
181
|
+
const architecture = raw.architecture ?? "monolith";
|
|
182
|
+
const packageManager = PACKAGE_MANAGERS.includes(raw.packageManager)
|
|
183
|
+
? raw.packageManager
|
|
184
|
+
: detectPackageManager(cwd);
|
|
185
|
+
// In a fullstack workspace the backend is generated into apps/api; a
|
|
186
|
+
// backend-only project is its own backend root.
|
|
187
|
+
const fullstackApi = join(cwd, "apps", "api");
|
|
188
|
+
const backendRoot = projectType === "fullstack" &&
|
|
189
|
+
existsSync(join(fullstackApi, "package.json"))
|
|
190
|
+
? fullstackApi
|
|
191
|
+
: cwd;
|
|
192
|
+
let backendDirs = [];
|
|
193
|
+
let services = [];
|
|
194
|
+
if (projectType !== "frontend") {
|
|
195
|
+
if (architecture === "microservice") {
|
|
196
|
+
const gateway = join(backendRoot, "apps", "gateway");
|
|
197
|
+
const listed = listServiceDirs(backendRoot, raw.services);
|
|
198
|
+
services = listed.names;
|
|
199
|
+
backendDirs = [
|
|
200
|
+
...(existsSync(join(gateway, "package.json")) ? [gateway] : []),
|
|
201
|
+
...listed.dirs,
|
|
202
|
+
];
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
backendDirs = [backendRoot];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
let frontendDir;
|
|
209
|
+
let frontendFramework;
|
|
210
|
+
if (projectType === "frontend") {
|
|
211
|
+
const nested = join(cwd, "apps", "web");
|
|
212
|
+
frontendDir = existsSync(join(nested, "package.json")) ? nested : cwd;
|
|
213
|
+
frontendFramework = raw.frontendFramework ?? "react";
|
|
214
|
+
}
|
|
215
|
+
else if (projectType === "fullstack") {
|
|
216
|
+
frontendDir = join(cwd, "apps", "web");
|
|
217
|
+
frontendFramework = raw.frontendFramework ?? "react";
|
|
218
|
+
}
|
|
219
|
+
else if (raw.frontendFramework && raw.frontendFramework !== "none") {
|
|
220
|
+
frontendDir = join(cwd, "apps", "web");
|
|
221
|
+
frontendFramework = raw.frontendFramework;
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
root: cwd,
|
|
225
|
+
projectType,
|
|
226
|
+
architecture,
|
|
227
|
+
packageManager,
|
|
228
|
+
isWorkspace: isWorkspaceRoot(cwd),
|
|
229
|
+
backendDirs,
|
|
230
|
+
...(frontendDir !== undefined ? { frontendDir } : {}),
|
|
231
|
+
...(frontendFramework !== undefined ? { frontendFramework } : {}),
|
|
232
|
+
services,
|
|
233
|
+
source: raw.source,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=projectLayout.core.js.map
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walks upward from `startDir` to the nearest directory that looks like a
|
|
3
|
+
* project root: one holding a `.zudojs/manifest.json`, a legacy
|
|
4
|
+
* `zudojs.config.ts`, or a `package.json`.
|
|
5
|
+
*/
|
|
1
6
|
export declare function findProjectRoot(startDir?: string): string | null;
|
|
2
7
|
export declare function resolveProjectPath(cwd: string, name: string): string;
|
|
3
8
|
//# sourceMappingURL=project.resolver.d.ts.map
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Walks upward from `startDir` to the nearest directory that looks like a
|
|
5
|
+
* project root: one holding a `.zudojs/manifest.json`, a legacy
|
|
6
|
+
* `zudojs.config.ts`, or a `package.json`.
|
|
7
|
+
*/
|
|
3
8
|
export function findProjectRoot(startDir = process.cwd()) {
|
|
4
9
|
let dir = startDir;
|
|
5
10
|
while (true) {
|
|
6
|
-
if (existsSync(join(dir, "zudojs.
|
|
11
|
+
if (existsSync(join(dir, ".zudojs", "manifest.json")) ||
|
|
12
|
+
existsSync(join(dir, "zudojs.config.ts")) ||
|
|
13
|
+
existsSync(join(dir, "package.json"))) {
|
|
7
14
|
return dir;
|
|
8
15
|
}
|
|
9
|
-
|
|
10
|
-
return dir;
|
|
11
|
-
}
|
|
12
|
-
const parent = join(dir, "..");
|
|
16
|
+
const parent = dirname(dir);
|
|
13
17
|
if (parent === dir) {
|
|
14
18
|
return null;
|
|
15
19
|
}
|
|
@@ -17,14 +17,13 @@
|
|
|
17
17
|
* ├── infrastructure/
|
|
18
18
|
* ├── package.json
|
|
19
19
|
* ├── pnpm-workspace.yaml
|
|
20
|
-
* ├── zudojs.config.ts
|
|
21
20
|
* ├── docker-compose.yml
|
|
22
21
|
* └── README.md
|
|
23
22
|
* ```
|
|
24
23
|
*/
|
|
25
24
|
import { ZUDOJS_PACKAGES_VERSION } from "../../constants/index.js";
|
|
26
25
|
import { normalizeName } from "../../utils/utils.name.js";
|
|
27
|
-
import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppFile, renderModuleFile, renderServerFile,
|
|
26
|
+
import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppFile, renderModuleFile, renderServerFile, renderPnpmWorkspaceFile, } from "../shared/index.js";
|
|
28
27
|
/** Default service names used when none are provided. */
|
|
29
28
|
export const DEFAULT_MICROSERVICE_SERVICES = [
|
|
30
29
|
"identity",
|
|
@@ -104,9 +103,7 @@ export function generateMicroserviceFiles(options) {
|
|
|
104
103
|
},
|
|
105
104
|
}, null, 2) + "\n";
|
|
106
105
|
if (options.packageManager === "pnpm") {
|
|
107
|
-
files["pnpm-workspace.yaml"] =
|
|
108
|
-
${workspaceGlobs.map((g) => ` - "${g}"`).join("\n")}
|
|
109
|
-
`;
|
|
106
|
+
files["pnpm-workspace.yaml"] = renderPnpmWorkspaceFile(workspaceGlobs);
|
|
110
107
|
}
|
|
111
108
|
// Docker Compose — gateway on 3000, service i on 3001+i.
|
|
112
109
|
let composeServices = "";
|
|
@@ -139,11 +136,6 @@ ${composeServices}
|
|
|
139
136
|
volumes:
|
|
140
137
|
${services.map((s) => `${s}-data:`).join("\n ")}
|
|
141
138
|
`;
|
|
142
|
-
files["zudojs.config.ts"] = renderZudojsConfig({
|
|
143
|
-
projectName: nameSlug,
|
|
144
|
-
projectType: "backend",
|
|
145
|
-
architecture: "microservice",
|
|
146
|
-
});
|
|
147
139
|
files[".env.example"] = `NODE_ENV=development
|
|
148
140
|
PORT=3000
|
|
149
141
|
`;
|
|
@@ -28,13 +28,12 @@
|
|
|
28
28
|
* ├── tests/
|
|
29
29
|
* ├── package.json
|
|
30
30
|
* ├── tsconfig.json
|
|
31
|
-
* ├── zudojs.config.ts
|
|
32
31
|
* └── README.md
|
|
33
32
|
* ```
|
|
34
33
|
*/
|
|
35
34
|
import { ZUDOJS_PACKAGES_VERSION } from "../../constants/index.js";
|
|
36
35
|
import { normalizeName } from "../../utils/utils.name.js";
|
|
37
|
-
import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppFile, renderModuleFile, renderServerFile,
|
|
36
|
+
import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppFile, renderModuleFile, renderServerFile, renderPnpmWorkspaceFile, } from "../shared/index.js";
|
|
38
37
|
export function generateModularMonolithFiles(options) {
|
|
39
38
|
const nameSlug = options.projectName
|
|
40
39
|
.replace(/[^a-z0-9-]+/gi, "-")
|
|
@@ -103,11 +102,9 @@ export function generateModularMonolithFiles(options) {
|
|
|
103
102
|
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
104
103
|
}
|
|
105
104
|
`;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
architecture: "modular-monolith",
|
|
110
|
-
});
|
|
105
|
+
if (options.packageManager === "pnpm") {
|
|
106
|
+
files["pnpm-workspace.yaml"] = renderPnpmWorkspaceFile();
|
|
107
|
+
}
|
|
111
108
|
files[".env.example"] = `NODE_ENV=development
|
|
112
109
|
PORT=3000
|
|
113
110
|
DATABASE_URL=postgresql://localhost:5432/${nameSlug}
|
|
@@ -34,13 +34,12 @@
|
|
|
34
34
|
* ├── tests/
|
|
35
35
|
* ├── package.json
|
|
36
36
|
* ├── tsconfig.json
|
|
37
|
-
* ├── zudojs.config.ts
|
|
38
37
|
* └── README.md
|
|
39
38
|
* ```
|
|
40
39
|
*/
|
|
41
40
|
import { ZUDOJS_PACKAGES_VERSION } from "../../constants/index.js";
|
|
42
41
|
import { normalizeName, toPascalCase } from "../../utils/utils.name.js";
|
|
43
|
-
import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppFile, renderModuleFile, renderServerFile, renderServiceFile,
|
|
42
|
+
import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppFile, renderModuleFile, renderServerFile, renderServiceFile, renderPnpmWorkspaceFile, } from "../shared/index.js";
|
|
44
43
|
export function generateMonolithFiles(options) {
|
|
45
44
|
const name = options.projectName;
|
|
46
45
|
const nameSlug = name.replace(/[^a-z0-9-]+/gi, "-").toLowerCase();
|
|
@@ -118,11 +117,9 @@ export function generateMonolithFiles(options) {
|
|
|
118
117
|
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
119
118
|
}
|
|
120
119
|
`;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
architecture: "monolith",
|
|
125
|
-
});
|
|
120
|
+
if (options.packageManager === "pnpm") {
|
|
121
|
+
files["pnpm-workspace.yaml"] = renderPnpmWorkspaceFile();
|
|
122
|
+
}
|
|
126
123
|
// Environment
|
|
127
124
|
files[".env.example"] = `NODE_ENV=development
|
|
128
125
|
PORT=3000
|
|
@@ -24,17 +24,6 @@ export interface RuntimeModuleSpec {
|
|
|
24
24
|
}
|
|
25
25
|
/** Builds a module spec from a raw name. */
|
|
26
26
|
export declare function moduleSpec(name: string, importPath: string): RuntimeModuleSpec;
|
|
27
|
-
/**
|
|
28
|
-
* Renders `zudojs.config.ts`.
|
|
29
|
-
*
|
|
30
|
-
* The shape matches what the fullstack composer writes and what
|
|
31
|
-
* `zudojs generate` parses to detect the project architecture.
|
|
32
|
-
*/
|
|
33
|
-
export declare function renderZudojsConfig(options: {
|
|
34
|
-
readonly projectName: string;
|
|
35
|
-
readonly projectType: string;
|
|
36
|
-
readonly architecture: string;
|
|
37
|
-
}): string;
|
|
38
27
|
/**
|
|
39
28
|
* Renders `src/app.ts`: assembles the runtime dependencies and returns a
|
|
40
29
|
* started-on-demand `Runtime`.
|
|
@@ -33,26 +33,6 @@ export function moduleSpec(name, importPath) {
|
|
|
33
33
|
function literal(value) {
|
|
34
34
|
return JSON.stringify(value);
|
|
35
35
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Renders `zudojs.config.ts`.
|
|
38
|
-
*
|
|
39
|
-
* The shape matches what the fullstack composer writes and what
|
|
40
|
-
* `zudojs generate` parses to detect the project architecture.
|
|
41
|
-
*/
|
|
42
|
-
export function renderZudojsConfig(options) {
|
|
43
|
-
return `/**
|
|
44
|
-
* Zudojs project configuration.
|
|
45
|
-
*
|
|
46
|
-
* Read by the \`zudojs\` CLI to detect this project's architecture.
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
export default {
|
|
50
|
-
name: ${literal(options.projectName)},
|
|
51
|
-
projectType: ${literal(options.projectType)},
|
|
52
|
-
architecture: ${literal(options.architecture)},
|
|
53
|
-
};
|
|
54
|
-
`;
|
|
55
|
-
}
|
|
56
36
|
/**
|
|
57
37
|
* Renders `src/app.ts`: assembles the runtime dependencies and returns a
|
|
58
38
|
* started-on-demand `Runtime`.
|