@madojs/mado 0.10.1 → 0.11.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/AGENTS.md +24 -26
- package/CHANGELOG.md +95 -0
- package/README.md +22 -47
- package/TODO.md +52 -48
- package/dist/src/component.d.ts +2 -1
- package/dist/src/component.js +5 -2
- package/dist/src/component.js.map +1 -1
- package/dist/src/each.d.ts +1 -1
- package/dist/src/each.js +1 -1
- package/dist/src/each.js.map +1 -1
- package/dist/src/html/bindings.js +3 -3
- package/dist/src/html/bindings.js.map +1 -1
- package/dist/src/index.d.ts +11 -6
- package/dist/src/index.js +5 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/lazy.d.ts +1 -1
- package/dist/src/lazy.js +1 -1
- package/dist/src/lazy.js.map +1 -1
- package/dist/src/page.d.ts +17 -21
- package/dist/src/page.js +7 -12
- package/dist/src/page.js.map +1 -1
- package/dist/src/router/manifest.d.ts +1 -1
- package/dist/src/router/manifest.js +21 -13
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +2 -2
- package/dist/src/router/match.js +3 -3
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.js +1 -1
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/vite/index.d.ts +10 -0
- package/dist/src/vite/index.js +33 -0
- package/dist/src/vite/index.js.map +1 -0
- package/docs/en/00-the-mado-way.md +25 -12
- package/docs/en/01-routing.md +90 -142
- package/docs/en/02-project-layout.md +59 -53
- package/docs/en/03-static-bake.md +5 -6
- package/docs/en/05-why-mado.md +6 -6
- package/docs/en/06-for-backenders.md +18 -22
- package/docs/en/08-llm-zero-history-test.md +9 -14
- package/docs/en/09-shadow-vs-light-dom.md +28 -36
- package/docs/en/10-app-architecture.md +158 -96
- package/docs/en/11-layouts.md +22 -24
- package/docs/en/12-auth-and-api.md +89 -182
- package/docs/en/13-deployment.md +18 -22
- package/docs/en/14-testing.md +4 -4
- package/docs/en/16-bake-cookbook.md +11 -12
- package/docs/en/18-api-freeze-map.md +6 -4
- package/docs/en/20-v1-stability.md +1 -1
- package/docs/fr/00-the-mado-way.md +55 -90
- package/docs/fr/01-routing.md +70 -152
- package/docs/fr/02-project-layout.md +61 -42
- package/docs/fr/03-static-bake.md +1 -1
- package/docs/fr/05-why-mado.md +6 -6
- package/docs/fr/06-for-backenders.md +7 -7
- package/docs/fr/08-llm-zero-history-test.md +21 -48
- package/docs/fr/09-shadow-vs-light-dom.md +43 -162
- package/docs/fr/10-app-architecture.md +110 -33
- package/docs/fr/11-layouts.md +24 -12
- package/docs/fr/12-auth-and-api.md +63 -22
- package/docs/fr/13-deployment.md +7 -10
- package/docs/fr/14-testing.md +1 -1
- package/docs/fr/16-bake-cookbook.md +2 -2
- package/docs/fr/18-api-freeze-map.md +1 -1
- package/docs/fr/20-v1-stability.md +1 -1
- package/docs/recipes/nginx/README.md +13 -0
- package/docs/ru/00-the-mado-way.md +53 -75
- package/docs/ru/01-routing.md +68 -143
- package/docs/ru/02-project-layout.md +61 -41
- package/docs/ru/03-static-bake.md +2 -2
- package/docs/ru/05-why-mado.md +6 -6
- package/docs/ru/06-for-backenders.md +7 -7
- package/docs/ru/08-llm-zero-history-test.md +9 -14
- package/docs/ru/09-shadow-vs-light-dom.md +43 -178
- package/docs/ru/10-app-architecture.md +115 -63
- package/docs/ru/11-layouts.md +24 -24
- package/docs/ru/12-auth-and-api.md +57 -35
- package/docs/ru/13-deployment.md +7 -11
- package/docs/ru/14-testing.md +1 -1
- package/docs/ru/16-bake-cookbook.md +12 -6
- package/docs/ru/18-api-freeze-map.md +5 -3
- package/docs/ru/20-v1-stability.md +1 -1
- package/docs/uk/00-the-mado-way.md +70 -44
- package/docs/uk/01-routing.md +41 -47
- package/docs/uk/02-project-layout.md +68 -41
- package/docs/uk/03-static-bake.md +1 -2
- package/docs/uk/06-for-backenders.md +3 -3
- package/docs/uk/08-llm-zero-history-test.md +22 -24
- package/docs/uk/09-shadow-vs-light-dom.md +37 -86
- package/docs/uk/10-app-architecture.md +72 -31
- package/docs/uk/11-layouts.md +25 -12
- package/docs/uk/12-auth-and-api.md +58 -22
- package/docs/uk/13-deployment.md +4 -3
- package/docs/uk/14-testing.md +1 -1
- package/docs/uk/18-api-freeze-map.md +1 -1
- package/docs/uk/20-v1-stability.md +1 -1
- package/llms.txt +14 -15
- package/package.json +18 -11
- package/scripts/_config.mjs +15 -161
- package/scripts/bake.mjs +74 -63
- package/scripts/cli/generate.mjs +348 -0
- package/scripts/cli/help.mjs +27 -0
- package/scripts/cli/index.mjs +79 -0
- package/scripts/cli/init.mjs +153 -0
- package/scripts/cli/release.mjs +152 -0
- package/scripts/cli/run.mjs +96 -0
- package/scripts/cli.mjs +2 -621
- package/scripts/package-smoke.mjs +4 -1
- package/scripts/preview.mjs +13 -37
- package/scripts/size-budget.mjs +5 -2
- package/scripts/vite.default.mjs +11 -0
- package/starters/default/.editorconfig +12 -0
- package/starters/default/README.md +74 -0
- package/starters/default/eslint.config.mjs +256 -0
- package/starters/default/index.html +13 -0
- package/starters/default/package.json +30 -0
- package/starters/default/public/favicon.svg +4 -0
- package/starters/default/src/app.routes.ts +39 -0
- package/starters/default/src/layouts/app-shell.layout.ts +35 -0
- package/starters/default/src/layouts/auth-shell.layout.ts +17 -0
- package/starters/default/src/main.ts +16 -0
- package/starters/default/src/modules/auth/_contracts/auth-api.types.ts +17 -0
- package/starters/default/src/modules/auth/auth.connector.ts +45 -0
- package/starters/default/src/modules/auth/auth.guard.ts +22 -0
- package/starters/default/src/modules/auth/auth.public.ts +9 -0
- package/starters/default/src/modules/auth/auth.routes.ts +8 -0
- package/starters/default/src/modules/auth/auth.service.ts +71 -0
- package/starters/default/src/modules/auth/auth.types.ts +15 -0
- package/starters/default/src/modules/auth/login.page.ts +62 -0
- package/starters/default/src/modules/billing/_contracts/stripe.types.ts +17 -0
- package/starters/default/src/modules/billing/api/stripe.connector.ts +71 -0
- package/starters/default/src/modules/billing/billing.public.ts +5 -0
- package/starters/default/src/modules/billing/billing.routes.ts +9 -0
- package/starters/default/src/modules/billing/billing.types.ts +15 -0
- package/starters/default/src/modules/billing/components/invoice-status-badge.component.ts +43 -0
- package/starters/default/src/modules/billing/data/invoices.resource.ts +35 -0
- package/starters/default/src/modules/billing/pages/invoice-detail.page.ts +70 -0
- package/starters/default/src/modules/billing/pages/invoices-list.page.ts +73 -0
- package/starters/default/src/modules/home/home.page.ts +34 -0
- package/starters/default/src/modules/home/not-found.page.ts +11 -0
- package/starters/default/src/shared/http/http-client.ts +86 -0
- package/starters/default/src/shared/http/http-error.ts +37 -0
- package/starters/default/src/shared/http/interceptors.ts +59 -0
- package/starters/default/src/shared/lib/format-date.ts +19 -0
- package/starters/default/src/shared/styles/content.css +70 -0
- package/starters/default/src/shared/styles/reset.css +32 -0
- package/starters/default/src/shared/styles/shell.css +57 -0
- package/starters/default/src/shared/styles/tokens.css +44 -0
- package/starters/default/src/shared/ui/x-button.component.ts +49 -0
- package/starters/default/src/shared/ui/x-spinner.component.ts +22 -0
- package/starters/default/src/styles.d.ts +1 -0
- package/starters/default/src/vite-env.d.ts +1 -0
- package/starters/default/tsconfig.json +24 -0
- package/starters/default/vite.config.ts +9 -0
- package/MADO_V1_PLAN.md +0 -179
- package/ROADMAP.md +0 -178
- package/dist/src/html.d.ts +0 -18
- package/dist/src/html.js +0 -17
- package/dist/src/html.js.map +0 -1
- package/dist/src/router.d.ts +0 -13
- package/dist/src/router.js +0 -13
- package/dist/src/router.js.map +0 -1
- package/scripts/bundle.mjs +0 -212
- package/scripts/llm-zero-history-smoke.mjs +0 -93
- package/scripts/new.mjs +0 -80
- package/scripts/showcase-regression.mjs +0 -392
- package/server/serve.mjs +0 -455
- package/starters/admin/README.md +0 -63
- package/starters/admin/index.html +0 -28
- package/starters/admin/mado.config.json +0 -22
- package/starters/admin/package.json +0 -24
- package/starters/admin/public/favicon.svg +0 -4
- package/starters/admin/src/components/x-button.ts +0 -82
- package/starters/admin/src/components/x-input.ts +0 -105
- package/starters/admin/src/layouts/app.ts +0 -101
- package/starters/admin/src/layouts/auth.ts +0 -41
- package/starters/admin/src/lib/api.ts +0 -184
- package/starters/admin/src/lib/auth.ts +0 -83
- package/starters/admin/src/main.ts +0 -15
- package/starters/admin/src/pages/admin/dashboard.ts +0 -48
- package/starters/admin/src/pages/admin/order-detail.ts +0 -80
- package/starters/admin/src/pages/admin/orders.ts +0 -117
- package/starters/admin/src/pages/home.ts +0 -34
- package/starters/admin/src/pages/login.ts +0 -70
- package/starters/admin/src/pages/not-found.ts +0 -12
- package/starters/admin/src/routes.ts +0 -40
- package/starters/admin/src/styles/global.ts +0 -86
- package/starters/admin/tsconfig.json +0 -15
- package/starters/crud/README.md +0 -33
- package/starters/crud/index.html +0 -28
- package/starters/crud/mado.config.json +0 -20
- package/starters/crud/package.json +0 -24
- package/starters/crud/src/components/app-shell.ts +0 -56
- package/starters/crud/src/components/ticket-detail.ts +0 -33
- package/starters/crud/src/components/ticket-form.ts +0 -69
- package/starters/crud/src/components/ticket-list.ts +0 -66
- package/starters/crud/src/lib/api.ts +0 -76
- package/starters/crud/src/main.ts +0 -9
- package/starters/crud/src/pages/home.ts +0 -34
- package/starters/crud/src/pages/not-found.ts +0 -12
- package/starters/crud/src/pages/ticket-detail.ts +0 -7
- package/starters/crud/src/pages/ticket-new.ts +0 -7
- package/starters/crud/src/pages/tickets.ts +0 -7
- package/starters/crud/src/routes.ts +0 -11
- package/starters/crud/src/styles/global.ts +0 -155
- package/starters/crud/tsconfig.json +0 -15
- package/starters/minimal/README.md +0 -21
- package/starters/minimal/index.html +0 -28
- package/starters/minimal/mado.config.json +0 -20
- package/starters/minimal/package.json +0 -24
- package/starters/minimal/src/components/app-counter.ts +0 -31
- package/starters/minimal/src/main.ts +0 -9
- package/starters/minimal/src/pages/home.ts +0 -35
- package/starters/minimal/src/pages/not-found.ts +0 -14
- package/starters/minimal/src/routes.ts +0 -8
- package/starters/minimal/src/styles/global.ts +0 -60
- package/starters/minimal/tsconfig.json +0 -15
- package/templates/page-detail.ts +0 -63
- package/templates/page-form.ts +0 -94
- package/templates/page-list.ts +0 -79
package/scripts/_config.mjs
CHANGED
|
@@ -1,20 +1,5 @@
|
|
|
1
|
-
// Mado configuration
|
|
2
|
-
//
|
|
3
|
-
// Single source of project configuration for `mado dev`, `mado build`,
|
|
4
|
-
// `mado bundle`, `mado bake`, `mado preview`, `mado release`.
|
|
5
|
-
//
|
|
6
|
-
// Lookup order (first hit wins):
|
|
7
|
-
// 1. `mado.config.json` in PROJECT_ROOT (recommended)
|
|
8
|
-
// 2. built-in defaults
|
|
9
|
-
//
|
|
10
|
-
// CLI flags always override file values, file values override defaults.
|
|
11
|
-
//
|
|
12
|
-
// Context detection:
|
|
13
|
-
// - "app" : a user project that depends on `@madojs/mado`
|
|
14
|
-
// - "repo" : the framework repository itself (has src/index.ts and examples/)
|
|
15
|
-
//
|
|
16
|
-
// In app-mode, defaults assume the canonical layout from MADO_V1_PLAN.md:
|
|
17
|
-
// src/routes.ts index.html public/ dist/ out/
|
|
1
|
+
// Small shared CLI helpers. Mado app configuration lives in vite.config.ts;
|
|
2
|
+
// this file intentionally does not read any project config file.
|
|
18
3
|
|
|
19
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
20
5
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -22,177 +7,38 @@ import { fileURLToPath } from "node:url";
|
|
|
22
7
|
|
|
23
8
|
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
24
9
|
|
|
25
|
-
/**
|
|
26
|
-
* @typedef {Object} MadoDevConfig
|
|
27
|
-
* @property {number} [port]
|
|
28
|
-
* @property {Record<string,string>} [proxy] // path → upstream base URL
|
|
29
|
-
*
|
|
30
|
-
* @typedef {Object} MadoBuildConfig
|
|
31
|
-
* @property {string} [out] // deploy artifact dir (default: "out")
|
|
32
|
-
* @property {string} [dist] // tsc output dir (default: "dist")
|
|
33
|
-
* @property {string} [publicDir] // static assets dir (default: "public")
|
|
34
|
-
*
|
|
35
|
-
* @typedef {Object} MadoBakeConfig
|
|
36
|
-
* @property {string} [entry] // routes module (default: "src/routes.ts")
|
|
37
|
-
* @property {string} [template] // SPA shell html (default: "index.html")
|
|
38
|
-
* @property {string} [baseUrl] // canonical/sitemap base
|
|
39
|
-
* @property {string} [outDir] // override (default: build.out + "/baked")
|
|
40
|
-
*
|
|
41
|
-
* @typedef {Object} MadoBundleConfig
|
|
42
|
-
* @property {boolean} [splitting]
|
|
43
|
-
* @property {Array<"gz"|"br">} [compress]
|
|
44
|
-
*
|
|
45
|
-
* @typedef {Object} MadoConfig
|
|
46
|
-
* @property {"app"|"repo"} context
|
|
47
|
-
* @property {string} projectRoot
|
|
48
|
-
* @property {MadoDevConfig} dev
|
|
49
|
-
* @property {MadoBuildConfig} build
|
|
50
|
-
* @property {MadoBakeConfig} bake
|
|
51
|
-
* @property {MadoBundleConfig} bundle
|
|
52
|
-
*/
|
|
53
|
-
|
|
54
10
|
/**
|
|
55
11
|
* Detect whether we are inside the framework repository or inside a user app.
|
|
56
12
|
*
|
|
57
|
-
* Heuristic: the framework repo has both `src/index.ts` and an `examples/`
|
|
58
|
-
* directory at PROJECT_ROOT. Anything else is treated as an app.
|
|
59
|
-
*
|
|
60
13
|
* @param {string} projectRoot
|
|
61
14
|
* @returns {"app"|"repo"}
|
|
62
15
|
*/
|
|
63
16
|
export function detectContext(projectRoot) {
|
|
64
17
|
const looksLikeRepo =
|
|
65
18
|
existsSync(join(projectRoot, "src/index.ts")) &&
|
|
66
|
-
existsSync(join(projectRoot, "examples")) &&
|
|
67
19
|
existsSync(join(projectRoot, "package.json")) &&
|
|
68
20
|
safeReadJson(join(projectRoot, "package.json"))?.name === "@madojs/mado";
|
|
69
21
|
return looksLikeRepo ? "repo" : "app";
|
|
70
22
|
}
|
|
71
23
|
|
|
72
24
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* @param {"app"|"repo"} context
|
|
76
|
-
* @returns {MadoConfig}
|
|
77
|
-
*/
|
|
78
|
-
function defaults(context) {
|
|
79
|
-
if (context === "repo") {
|
|
80
|
-
return {
|
|
81
|
-
context,
|
|
82
|
-
projectRoot: "",
|
|
83
|
-
dev: { port: 5173, proxy: {} },
|
|
84
|
-
build: { out: "out", dist: "dist", publicDir: "public" },
|
|
85
|
-
bake: {
|
|
86
|
-
entry: "examples/basic/routes.ts",
|
|
87
|
-
template: "examples/index.html",
|
|
88
|
-
baseUrl: "https://example.com",
|
|
89
|
-
},
|
|
90
|
-
bundle: { splitting: true, compress: ["gz", "br"] },
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
return {
|
|
94
|
-
context,
|
|
95
|
-
projectRoot: "",
|
|
96
|
-
dev: { port: 5173, proxy: {} },
|
|
97
|
-
build: { out: "out", dist: "dist", publicDir: "public" },
|
|
98
|
-
bake: {
|
|
99
|
-
entry: "src/routes.ts",
|
|
100
|
-
template: "index.html",
|
|
101
|
-
baseUrl: "https://example.com",
|
|
102
|
-
},
|
|
103
|
-
bundle: { splitting: true, compress: ["gz", "br"] },
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Load and merge configuration for the given project root.
|
|
109
|
-
*
|
|
110
|
-
* Precedence (low → high): defaults, mado.config.json, CLI overrides.
|
|
25
|
+
* Resolve a project-relative path against `projectRoot`.
|
|
111
26
|
*
|
|
112
|
-
* @param {
|
|
113
|
-
* @param {string} [opts.projectRoot=process.cwd()]
|
|
114
|
-
* @param {Partial<MadoConfig>} [opts.overrides]
|
|
115
|
-
* @returns {MadoConfig}
|
|
116
|
-
*/
|
|
117
|
-
export function loadConfig(opts = {}) {
|
|
118
|
-
const projectRoot = resolve(opts.projectRoot ?? process.cwd());
|
|
119
|
-
const context = detectContext(projectRoot);
|
|
120
|
-
const base = defaults(context);
|
|
121
|
-
base.projectRoot = projectRoot;
|
|
122
|
-
|
|
123
|
-
const file = join(projectRoot, "mado.config.json");
|
|
124
|
-
const fromFile = existsSync(file) ? safeReadJson(file) ?? {} : {};
|
|
125
|
-
|
|
126
|
-
const merged = deepMerge(base, fromFile);
|
|
127
|
-
if (opts.overrides) deepMerge(merged, opts.overrides);
|
|
128
|
-
|
|
129
|
-
merged.context = context;
|
|
130
|
-
merged.projectRoot = projectRoot;
|
|
131
|
-
return merged;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Resolve a project-relative path against `projectRoot`. Absolute paths are
|
|
136
|
-
* returned unchanged.
|
|
137
|
-
*
|
|
138
|
-
* @param {MadoConfig} cfg
|
|
27
|
+
* @param {string} projectRoot
|
|
139
28
|
* @param {string} p
|
|
140
29
|
* @returns {string}
|
|
141
30
|
*/
|
|
142
|
-
export function resolveProjectPath(
|
|
143
|
-
|
|
144
|
-
return resolve(cfg.projectRoot, p);
|
|
31
|
+
export function resolveProjectPath(projectRoot, p) {
|
|
32
|
+
return resolve(projectRoot, p);
|
|
145
33
|
}
|
|
146
34
|
|
|
147
|
-
/**
|
|
148
|
-
* @param {MadoConfig} cfg
|
|
149
|
-
* @returns {string}
|
|
150
|
-
*/
|
|
151
35
|
export function getPackageRoot() {
|
|
152
36
|
return PACKAGE_ROOT;
|
|
153
37
|
}
|
|
154
38
|
|
|
155
|
-
// ---------- helpers ----------
|
|
156
|
-
|
|
157
|
-
function safeReadJson(path) {
|
|
158
|
-
try {
|
|
159
|
-
return JSON.parse(readFileSync(path, "utf8"));
|
|
160
|
-
} catch (err) {
|
|
161
|
-
console.warn(`[mado] failed to parse ${path}: ${err.message}`);
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Shallow-deep merge: objects are merged recursively, arrays and primitives
|
|
168
|
-
* are replaced. Mutates `target` and returns it.
|
|
169
|
-
*/
|
|
170
|
-
function deepMerge(target, source) {
|
|
171
|
-
if (!source || typeof source !== "object") return target;
|
|
172
|
-
for (const [k, v] of Object.entries(source)) {
|
|
173
|
-
if (v === undefined) continue;
|
|
174
|
-
if (
|
|
175
|
-
v !== null &&
|
|
176
|
-
typeof v === "object" &&
|
|
177
|
-
!Array.isArray(v) &&
|
|
178
|
-
target[k] &&
|
|
179
|
-
typeof target[k] === "object" &&
|
|
180
|
-
!Array.isArray(target[k])
|
|
181
|
-
) {
|
|
182
|
-
deepMerge(target[k], v);
|
|
183
|
-
} else {
|
|
184
|
-
target[k] = v;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return target;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
39
|
/**
|
|
191
40
|
* Tiny argv parser shared by CLI subcommands.
|
|
192
41
|
*
|
|
193
|
-
* Recognises `--key=value`, `--key value`, and `--flag` (boolean).
|
|
194
|
-
* Unknown leading positionals are returned in `positional`.
|
|
195
|
-
*
|
|
196
42
|
* @param {string[]} argv
|
|
197
43
|
* @returns {{ flags: Record<string, string|boolean>, positional: string[] }}
|
|
198
44
|
*/
|
|
@@ -221,4 +67,12 @@ export function parseFlags(argv) {
|
|
|
221
67
|
}
|
|
222
68
|
}
|
|
223
69
|
return { flags, positional };
|
|
224
|
-
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function safeReadJson(path) {
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
package/scripts/bake.mjs
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Usage:
|
|
4
4
|
// mado bake
|
|
5
|
-
// mado bake --entry src/routes.ts --template index.html --out out
|
|
5
|
+
// mado bake --entry src/routes.ts --template index.html --out out
|
|
6
6
|
// mado bake --base-url https://example.com
|
|
7
7
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
8
|
+
// Defaults:
|
|
9
|
+
// entry: src/app.routes.ts, then src/routes.ts
|
|
10
|
+
// template: index.html
|
|
11
|
+
// out: out/
|
|
10
12
|
//
|
|
11
13
|
// What it does:
|
|
12
|
-
// 1.
|
|
14
|
+
// 1. Loads `entry` (routes module) through Vite SSR/module loading.
|
|
13
15
|
// 2. For every route whose page has `bake`:
|
|
14
16
|
// a) gets params via `bake.paths()`,
|
|
15
17
|
// b) gets data per params via `bake.data(params)`,
|
|
@@ -25,36 +27,32 @@
|
|
|
25
27
|
// - In repo-mode (the framework repository itself) it aliases
|
|
26
28
|
// `@madojs/mado` → ./src/index.ts so the framework can dogfood itself.
|
|
27
29
|
//
|
|
28
|
-
// Required dev deps: linkedom,
|
|
30
|
+
// Required dev deps: linkedom, vite. We print a clear error if missing.
|
|
29
31
|
|
|
30
|
-
import { readFile, writeFile, mkdir
|
|
32
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
31
33
|
import { existsSync, writeSync } from "node:fs";
|
|
32
34
|
import { join, dirname, resolve } from "node:path";
|
|
33
|
-
import { pathToFileURL } from "node:url";
|
|
34
|
-
import { tmpdir } from "node:os";
|
|
35
35
|
|
|
36
|
-
import {
|
|
36
|
+
import { detectContext, parseFlags, resolveProjectPath } from "./_config.mjs";
|
|
37
37
|
|
|
38
38
|
// ---------- Resolve options from config + flags + env ----------
|
|
39
39
|
|
|
40
40
|
const { flags } = parseFlags(process.argv.slice(2));
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const OUT_DIR = process.env.OUT_DIR
|
|
57
|
-
?? resolveProjectPath(cfg, cfg.bake.outDir ?? join(cfg.build.out, "baked"));
|
|
41
|
+
const PROJECT_ROOT = resolve(process.cwd());
|
|
42
|
+
const CONTEXT = detectContext(PROJECT_ROOT);
|
|
43
|
+
const ENTRY = resolveProjectPath(
|
|
44
|
+
PROJECT_ROOT,
|
|
45
|
+
typeof flags.entry === "string" ? flags.entry : pickDefaultEntry(PROJECT_ROOT),
|
|
46
|
+
);
|
|
47
|
+
const TEMPLATE = resolveProjectPath(
|
|
48
|
+
PROJECT_ROOT,
|
|
49
|
+
typeof flags.template === "string" ? flags.template : "index.html",
|
|
50
|
+
);
|
|
51
|
+
const BASE_URL = typeof flags["base-url"] === "string" ? flags["base-url"] : "https://example.com";
|
|
52
|
+
const OUT_DIR = resolveProjectPath(
|
|
53
|
+
PROJECT_ROOT,
|
|
54
|
+
typeof flags.out === "string" ? flags.out : "out",
|
|
55
|
+
);
|
|
58
56
|
|
|
59
57
|
/** Write message to stderr and exit. Sync write keeps CI/execFile output reliable. */
|
|
60
58
|
function fatal(...msgs) {
|
|
@@ -69,13 +67,13 @@ function error(...msgs) {
|
|
|
69
67
|
if (!existsSync(ENTRY)) {
|
|
70
68
|
fatal(
|
|
71
69
|
`[bake] entry not found: ${ENTRY}`,
|
|
72
|
-
`[bake]
|
|
70
|
+
`[bake] expected src/app.routes.ts or src/routes.ts; pass --entry <file> to override`,
|
|
73
71
|
);
|
|
74
72
|
}
|
|
75
73
|
if (!existsSync(TEMPLATE)) {
|
|
76
74
|
fatal(
|
|
77
75
|
`[bake] template not found: ${TEMPLATE}`,
|
|
78
|
-
`[bake]
|
|
76
|
+
`[bake] expected index.html; pass --template <file> to override`,
|
|
79
77
|
);
|
|
80
78
|
}
|
|
81
79
|
|
|
@@ -88,22 +86,22 @@ try {
|
|
|
88
86
|
fatal(
|
|
89
87
|
"[bake] package 'linkedom' is required.",
|
|
90
88
|
"[bake] Install it as a dev dependency in this project:",
|
|
91
|
-
"[bake] npm i -D linkedom
|
|
92
|
-
"[bake] (
|
|
89
|
+
"[bake] npm i -D linkedom vite",
|
|
90
|
+
"[bake] (vite is also required, see next check).",
|
|
93
91
|
"[bake] These are not bundled into @madojs/mado on purpose: bake is an",
|
|
94
92
|
"[bake] optional build step and we don't want to add transitive deps to",
|
|
95
93
|
"[bake] every Mado install.",
|
|
96
94
|
);
|
|
97
95
|
}
|
|
98
96
|
|
|
99
|
-
let
|
|
97
|
+
let createViteServer;
|
|
100
98
|
try {
|
|
101
|
-
|
|
99
|
+
({ createServer: createViteServer } = await import("vite"));
|
|
102
100
|
} catch {
|
|
103
101
|
fatal(
|
|
104
|
-
"[bake] package '
|
|
102
|
+
"[bake] package 'vite' is required.",
|
|
105
103
|
"[bake] Install it as a dev dependency in this project:",
|
|
106
|
-
"[bake] npm i -D
|
|
104
|
+
"[bake] npm i -D vite linkedom",
|
|
107
105
|
);
|
|
108
106
|
}
|
|
109
107
|
|
|
@@ -139,46 +137,40 @@ if (!globalThis.queueMicrotask) {
|
|
|
139
137
|
globalThis.queueMicrotask = (fn) => Promise.resolve().then(fn);
|
|
140
138
|
}
|
|
141
139
|
|
|
142
|
-
// ----------
|
|
140
|
+
// ---------- Load the routes module through Vite ----------
|
|
143
141
|
//
|
|
144
142
|
// In repo-mode the framework dogfoods its own source; alias `@madojs/mado`
|
|
145
143
|
// to ./src/index.ts. In app-mode the package is resolved from node_modules.
|
|
146
144
|
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
? { "@madojs/mado": resolve(cfg.projectRoot, "src/index.ts") }
|
|
145
|
+
const aliases = CONTEXT === "repo"
|
|
146
|
+
? { "@madojs/mado": resolve(PROJECT_ROOT, "src/index.ts") }
|
|
150
147
|
: {};
|
|
151
148
|
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
await esbuild.build({
|
|
156
|
-
entryPoints: [ENTRY],
|
|
157
|
-
bundle: true,
|
|
158
|
-
format: "esm",
|
|
159
|
-
platform: "node",
|
|
160
|
-
target: "es2022",
|
|
161
|
-
outfile: tmpFile,
|
|
162
|
-
absWorkingDir: cfg.projectRoot,
|
|
163
|
-
tsconfig,
|
|
164
|
-
alias: aliases,
|
|
149
|
+
const viteServer = await createViteServer({
|
|
150
|
+
root: PROJECT_ROOT,
|
|
165
151
|
logLevel: "error",
|
|
152
|
+
server: { middlewareMode: true },
|
|
153
|
+
appType: "custom",
|
|
154
|
+
resolve: { alias: aliases },
|
|
166
155
|
});
|
|
167
156
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
await
|
|
157
|
+
let routesModule;
|
|
158
|
+
try {
|
|
159
|
+
routesModule = await viteServer.ssrLoadModule(toViteId(ENTRY));
|
|
160
|
+
} catch (err) {
|
|
161
|
+
await closeAndFatal(`[bake] failed to load ${ENTRY}: ${err.message}`);
|
|
162
|
+
}
|
|
171
163
|
const routeApi = routesModule.default;
|
|
172
164
|
|
|
173
165
|
if (!routeApi) {
|
|
174
|
-
|
|
166
|
+
await closeAndFatal(`[bake] ${ENTRY} must default-export routes({...})`);
|
|
175
167
|
}
|
|
176
168
|
|
|
177
169
|
// Bake needs the source manifest (not the runtime RouterApi).
|
|
178
170
|
// routes.ts must therefore also `export const manifest = {...}`.
|
|
179
171
|
const manifest = routesModule.manifest;
|
|
180
172
|
if (!manifest) {
|
|
181
|
-
|
|
173
|
+
await closeAndFatal(
|
|
182
174
|
`[bake] ${ENTRY} must also \`export const manifest = {...}\` ` +
|
|
183
175
|
"(the same object passed to routes()).",
|
|
184
176
|
);
|
|
@@ -290,7 +282,7 @@ await writeFile(join(OUT_DIR, "sitemap.xml"), sitemap);
|
|
|
290
282
|
|
|
291
283
|
console.log(`[bake] done: ${total} pages + sitemap.xml → ${OUT_DIR}`);
|
|
292
284
|
if (bakedErrors > 0) {
|
|
293
|
-
|
|
285
|
+
await closeAndFatal(`[bake] ${bakedErrors} route(s) failed; see errors above.`);
|
|
294
286
|
}
|
|
295
287
|
// Loud diagnostic when the manifest exists but no page declares `bake`.
|
|
296
288
|
// Previously bake silently produced 0 pages + an empty sitemap and exited
|
|
@@ -319,10 +311,13 @@ if (bakeablePages === 0) {
|
|
|
319
311
|
// it. If you intentionally have an SPA-only deploy, drop `mado bake` from
|
|
320
312
|
// the release pipeline (or set MADO_BAKE_ALLOW_EMPTY=1).
|
|
321
313
|
if (process.env.MADO_BAKE_ALLOW_EMPTY !== "1") {
|
|
314
|
+
await viteServer.close();
|
|
322
315
|
process.exit(1);
|
|
323
316
|
}
|
|
324
317
|
}
|
|
325
318
|
|
|
319
|
+
await viteServer.close();
|
|
320
|
+
|
|
326
321
|
// ---------- Helpers ----------
|
|
327
322
|
|
|
328
323
|
async function resolvePage(entry) {
|
|
@@ -339,6 +334,21 @@ async function resolvePage(entry) {
|
|
|
339
334
|
return null;
|
|
340
335
|
}
|
|
341
336
|
|
|
337
|
+
function toViteId(path) {
|
|
338
|
+
return path.split("\\").join("/");
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function pickDefaultEntry(projectRoot) {
|
|
342
|
+
const appRoutes = "src/app.routes.ts";
|
|
343
|
+
if (existsSync(resolve(projectRoot, appRoutes))) return appRoutes;
|
|
344
|
+
return "src/routes.ts";
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async function closeAndFatal(...msgs) {
|
|
348
|
+
await viteServer.close().catch(() => { });
|
|
349
|
+
fatal(...msgs);
|
|
350
|
+
}
|
|
351
|
+
|
|
342
352
|
function applyParams(pattern, params) {
|
|
343
353
|
return pattern.replace(/:([\w]+)/g, (_, k) => {
|
|
344
354
|
const v = params[k];
|
|
@@ -358,13 +368,14 @@ function renderTemplate(tpl, ctx) {
|
|
|
358
368
|
if (typeof tpl === "string") return escapeHtml(tpl);
|
|
359
369
|
if (typeof tpl === "number") return String(tpl);
|
|
360
370
|
if (Array.isArray(tpl)) return tpl.map((x) => renderTemplate(x, ctx)).join("");
|
|
361
|
-
if (
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
371
|
+
if (typeof tpl === "object") {
|
|
372
|
+
if (tpl._mado === true) return renderMadoTemplate(tpl, ctx);
|
|
373
|
+
|
|
374
|
+
// Unknown shapes (e.g. each() directive results) must NOT silently render
|
|
375
|
+
// as "[object Object]". Either each() unwraps to an array here, or we
|
|
376
|
+
// throw with a meaningful location.
|
|
366
377
|
throw new Error(
|
|
367
|
-
`bake cannot render value of type "${tpl
|
|
378
|
+
`bake cannot render value of type "${tpl._type ?? tpl.constructor?.name ?? "object"}" ` +
|
|
368
379
|
`in route ${ctx?.route ?? "?"}. ` +
|
|
369
380
|
"Hint: each() and other directives are not yet supported in bake. " +
|
|
370
381
|
"Use a plain array (items.map(render)) in baked views, or render this " +
|