@madojs/mado 0.11.0 → 0.12.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/AGENTS.md +102 -25
- package/CHANGELOG.md +185 -1
- package/README.md +137 -172
- package/dist/src/component.d.ts +3 -2
- package/dist/src/component.js +98 -6
- package/dist/src/component.js.map +1 -1
- package/dist/src/css.js +34 -5
- package/dist/src/css.js.map +1 -1
- package/dist/src/head.d.ts +6 -6
- package/dist/src/head.js +6 -6
- package/dist/src/html/bindings.js +3 -3
- package/dist/src/html/bindings.js.map +1 -1
- package/dist/src/html/parser.d.ts +1 -1
- package/dist/src/html/parser.js +1 -1
- package/dist/src/html/template.js +24 -10
- package/dist/src/html/template.js.map +1 -1
- package/dist/src/index.d.ts +8 -4
- package/dist/src/index.js +13 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/page.d.ts +62 -28
- package/dist/src/page.js +5 -0
- package/dist/src/page.js.map +1 -1
- package/dist/src/resource.js +2 -1
- package/dist/src/resource.js.map +1 -1
- package/dist/src/router/base.d.ts +65 -0
- package/dist/src/router/base.js +168 -0
- package/dist/src/router/base.js.map +1 -0
- package/dist/src/router/manifest.js +75 -31
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +16 -2
- package/dist/src/router/match.js +41 -1
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.js +58 -9
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/static-runtime.d.ts +81 -0
- package/dist/src/static-runtime.js +209 -0
- package/dist/src/static-runtime.js.map +1 -0
- package/dist/src/vite/index.d.ts +31 -3
- package/dist/src/vite/index.js +49 -18
- package/dist/src/vite/index.js.map +1 -1
- package/docs/README.md +5 -8
- package/docs/architecture/adr/0001-browser-static-snapshots.md +132 -0
- package/docs/en/00-the-mado-way.md +1 -1
- package/docs/en/01-quickstart.md +183 -0
- package/docs/en/10-pages-and-components.md +271 -0
- package/docs/en/11-templates-and-signals.md +211 -0
- package/docs/en/12-routing.md +229 -0
- package/docs/en/13-data.md +225 -0
- package/docs/en/14-forms.md +244 -0
- package/docs/en/15-static-snapshots.md +181 -0
- package/docs/en/{10-app-architecture.md → 16-app-architecture.md} +53 -2
- package/docs/en/{13-deployment.md → 20-deployment.md} +59 -10
- package/docs/en/{14-testing.md → 22-testing.md} +6 -5
- package/docs/en/23-cookbook.md +292 -0
- package/docs/en/{18-api-freeze-map.md → 30-api-freeze-map.md} +12 -3
- package/docs/en/{20-v1-stability.md → 32-v1-stability.md} +3 -3
- package/docs/en/40-llm-guide.md +776 -0
- package/docs/en/{06-for-backenders.md → 41-for-backenders.md} +15 -16
- package/docs/en/{05-why-mado.md → 42-why-mado.md} +1 -1
- package/docs/en/README.md +97 -27
- package/docs/recipes/nginx/Containerfile +26 -0
- package/docs/recipes/nginx/nginx.conf +42 -0
- package/llms.txt +246 -211
- package/package.json +17 -10
- package/scripts/bake.mjs +4 -568
- package/scripts/cli/generate.mjs +55 -5
- package/scripts/cli/help.mjs +11 -5
- package/scripts/cli/index.mjs +20 -4
- package/scripts/cli/init.mjs +7 -2
- package/scripts/cli/release.mjs +20 -68
- package/scripts/docs-lint.mjs +170 -0
- package/scripts/package-smoke.mjs +67 -9
- package/scripts/preview.mjs +95 -6
- package/scripts/size-budget.mjs +5 -1
- package/scripts/static/browser.mjs +654 -0
- package/scripts/static/discover.mjs +281 -0
- package/scripts/static/output.mjs +141 -0
- package/scripts/static/serialize.mjs +212 -0
- package/scripts/static/server.mjs +167 -0
- package/scripts/static.mjs +191 -0
- package/starters/default/README.md +57 -55
- package/starters/default/package.json +3 -9
- package/starters/default/src/app.routes.ts +20 -33
- package/starters/default/src/components/feature-card.component.ts +40 -0
- package/starters/default/src/components/live-counter.component.ts +57 -0
- package/starters/default/src/content/guides.ts +55 -0
- package/starters/default/src/main.ts +9 -13
- package/starters/default/src/pages/app.page.ts +41 -0
- package/starters/default/src/pages/guide.page.ts +40 -0
- package/starters/default/src/pages/home.page.ts +49 -0
- package/starters/default/src/pages/not-found.page.ts +18 -0
- package/starters/default/src/styles/document.css +39 -0
- package/starters/default/src/styles/reset.css +37 -0
- package/starters/default/src/styles/tokens.css +30 -0
- package/starters/default/src/styles.d.ts +3 -1
- package/starters/default/src/vite-env.d.ts +1 -1
- package/starters/default/vite.config.ts +14 -1
- package/starters/modular/README.md +142 -0
- package/starters/modular/index.html +13 -0
- package/starters/modular/package.json +30 -0
- package/starters/modular/src/app.routes.ts +39 -0
- package/starters/{default → modular}/src/layouts/app-shell.layout.ts +8 -5
- package/starters/{default → modular}/src/layouts/auth-shell.layout.ts +3 -3
- package/starters/modular/src/main.ts +16 -0
- package/starters/{default → modular}/src/modules/billing/pages/invoices-list.page.ts +3 -3
- package/starters/{default → modular}/src/modules/home/home.page.ts +5 -7
- package/starters/modular/src/modules/home/not-found.page.ts +14 -0
- package/starters/modular/src/styles.d.ts +1 -0
- package/starters/modular/src/vite-env.d.ts +1 -0
- package/starters/modular/tsconfig.json +24 -0
- package/starters/modular/vite.config.ts +131 -0
- package/TODO.md +0 -79
- package/docs/en/01-routing.md +0 -152
- package/docs/en/02-project-layout.md +0 -124
- package/docs/en/03-static-bake.md +0 -249
- package/docs/en/04-ide-setup.md +0 -162
- package/docs/en/07-llm-pitfalls.md +0 -724
- package/docs/en/08-llm-zero-history-test.md +0 -56
- package/docs/en/09-shadow-vs-light-dom.md +0 -174
- package/docs/en/11-layouts.md +0 -113
- package/docs/en/12-auth-and-api.md +0 -124
- package/docs/en/16-bake-cookbook.md +0 -100
- package/docs/en/17-shadow-dom-forms.md +0 -192
- package/docs/fr/00-the-mado-way.md +0 -85
- package/docs/fr/01-routing.md +0 -120
- package/docs/fr/02-project-layout.md +0 -84
- package/docs/fr/03-static-bake.md +0 -289
- package/docs/fr/04-ide-setup.md +0 -162
- package/docs/fr/05-why-mado.md +0 -193
- package/docs/fr/06-for-backenders.md +0 -438
- package/docs/fr/07-llm-pitfalls.md +0 -625
- package/docs/fr/08-llm-zero-history-test.md +0 -38
- package/docs/fr/09-shadow-vs-light-dom.md +0 -65
- package/docs/fr/10-app-architecture.md +0 -138
- package/docs/fr/11-layouts.md +0 -47
- package/docs/fr/12-auth-and-api.md +0 -76
- package/docs/fr/13-deployment.md +0 -57
- package/docs/fr/14-testing.md +0 -41
- package/docs/fr/15-error-handling.md +0 -50
- package/docs/fr/16-bake-cookbook.md +0 -88
- package/docs/fr/17-shadow-dom-forms.md +0 -196
- package/docs/fr/18-api-freeze-map.md +0 -63
- package/docs/fr/19-reactivity-ordering.md +0 -97
- package/docs/fr/20-v1-stability.md +0 -88
- package/docs/fr/README.md +0 -27
- package/docs/ru/00-the-mado-way.md +0 -84
- package/docs/ru/01-routing.md +0 -119
- package/docs/ru/02-project-layout.md +0 -84
- package/docs/ru/03-static-bake.md +0 -250
- package/docs/ru/04-ide-setup.md +0 -144
- package/docs/ru/05-why-mado.md +0 -193
- package/docs/ru/06-for-backenders.md +0 -428
- package/docs/ru/07-llm-pitfalls.md +0 -624
- package/docs/ru/08-llm-zero-history-test.md +0 -57
- package/docs/ru/09-shadow-vs-light-dom.md +0 -63
- package/docs/ru/10-app-architecture.md +0 -152
- package/docs/ru/11-layouts.md +0 -47
- package/docs/ru/12-auth-and-api.md +0 -75
- package/docs/ru/13-deployment.md +0 -66
- package/docs/ru/14-testing.md +0 -50
- package/docs/ru/15-error-handling.md +0 -56
- package/docs/ru/16-bake-cookbook.md +0 -95
- package/docs/ru/17-shadow-dom-forms.md +0 -193
- package/docs/ru/18-api-freeze-map.md +0 -64
- package/docs/ru/19-reactivity-ordering.md +0 -95
- package/docs/ru/20-v1-stability.md +0 -82
- package/docs/ru/README.md +0 -25
- package/docs/uk/00-the-mado-way.md +0 -82
- package/docs/uk/01-routing.md +0 -76
- package/docs/uk/02-project-layout.md +0 -73
- package/docs/uk/03-static-bake.md +0 -48
- package/docs/uk/04-ide-setup.md +0 -26
- package/docs/uk/05-why-mado.md +0 -34
- package/docs/uk/06-for-backenders.md +0 -55
- package/docs/uk/07-llm-pitfalls.md +0 -145
- package/docs/uk/08-llm-zero-history-test.md +0 -34
- package/docs/uk/09-shadow-vs-light-dom.md +0 -58
- package/docs/uk/10-app-architecture.md +0 -97
- package/docs/uk/11-layouts.md +0 -47
- package/docs/uk/12-auth-and-api.md +0 -70
- package/docs/uk/13-deployment.md +0 -40
- package/docs/uk/14-testing.md +0 -34
- package/docs/uk/15-error-handling.md +0 -32
- package/docs/uk/16-bake-cookbook.md +0 -36
- package/docs/uk/17-shadow-dom-forms.md +0 -193
- package/docs/uk/18-api-freeze-map.md +0 -61
- package/docs/uk/19-reactivity-ordering.md +0 -95
- package/docs/uk/20-v1-stability.md +0 -83
- package/docs/uk/README.md +0 -27
- package/starters/default/src/modules/home/not-found.page.ts +0 -11
- /package/docs/en/{15-error-handling.md → 21-error-handling.md} +0 -0
- /package/docs/en/{19-reactivity-ordering.md → 31-reactivity-ordering.md} +0 -0
- /package/starters/{default → modular}/.editorconfig +0 -0
- /package/starters/{default → modular}/eslint.config.mjs +0 -0
- /package/starters/{default → modular}/public/favicon.svg +0 -0
- /package/starters/{default → modular}/src/modules/auth/_contracts/auth-api.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.connector.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.guard.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.public.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.routes.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.service.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/login.page.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/_contracts/stripe.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/api/stripe.connector.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.public.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.routes.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/components/invoice-status-badge.component.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/data/invoices.resource.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/pages/invoice-detail.page.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/http-client.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/http-error.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/interceptors.ts +0 -0
- /package/starters/{default → modular}/src/shared/lib/format-date.ts +0 -0
- /package/starters/{default → modular}/src/shared/styles/content.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/reset.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/shell.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/tokens.css +0 -0
- /package/starters/{default → modular}/src/shared/ui/x-button.component.ts +0 -0
- /package/starters/{default → modular}/src/shared/ui/x-spinner.component.ts +0 -0
package/scripts/cli/index.mjs
CHANGED
|
@@ -31,10 +31,21 @@ export async function main(argv) {
|
|
|
31
31
|
await runInit(ctx, args);
|
|
32
32
|
break;
|
|
33
33
|
case "build":
|
|
34
|
-
|
|
34
|
+
// Framework repo bootstraps via `tsc` (so the published package
|
|
35
|
+
// ships compiled .js). Every other context — user apps, the
|
|
36
|
+
// starter — gets a production Vite build of the deployable SPA.
|
|
37
|
+
if (ctx.isRepo) {
|
|
38
|
+
await runNodeBin(ctx, "typescript/bin/tsc", args);
|
|
39
|
+
} else {
|
|
40
|
+
await runVite(ctx, ["build", ...args], { defaultConfig: true });
|
|
41
|
+
}
|
|
35
42
|
break;
|
|
36
43
|
case "watch":
|
|
37
|
-
|
|
44
|
+
if (ctx.isRepo) {
|
|
45
|
+
await runNodeBin(ctx, "typescript/bin/tsc", ["-w", ...args]);
|
|
46
|
+
} else {
|
|
47
|
+
await runVite(ctx, ["build", "--watch", ...args], { defaultConfig: true });
|
|
48
|
+
}
|
|
38
49
|
break;
|
|
39
50
|
case "typecheck":
|
|
40
51
|
await runNodeBin(ctx, "typescript/bin/tsc", ["--noEmit", ...args]);
|
|
@@ -42,7 +53,7 @@ export async function main(argv) {
|
|
|
42
53
|
case "test": {
|
|
43
54
|
await runNodeBin(ctx, "typescript/bin/tsc", []);
|
|
44
55
|
const files = await listTestFiles(projectRoot);
|
|
45
|
-
await run(process.execPath, ["--test", "--test-timeout=
|
|
56
|
+
await run(process.execPath, ["--test", "--test-timeout=120000", ...files, ...args], {
|
|
46
57
|
cwd: projectRoot,
|
|
47
58
|
});
|
|
48
59
|
break;
|
|
@@ -55,7 +66,12 @@ export async function main(argv) {
|
|
|
55
66
|
);
|
|
56
67
|
break;
|
|
57
68
|
case "bake":
|
|
58
|
-
|
|
69
|
+
console.error("[mado] `mado bake` was removed.");
|
|
70
|
+
console.error("Use `mado static`, or run the complete pipeline with `mado release`.");
|
|
71
|
+
process.exit(1);
|
|
72
|
+
break;
|
|
73
|
+
case "static":
|
|
74
|
+
await runNodeScript(ctx, "scripts/static.mjs", args);
|
|
59
75
|
break;
|
|
60
76
|
case "preview":
|
|
61
77
|
await runNodeScript(ctx, "scripts/preview.mjs", args);
|
package/scripts/cli/init.mjs
CHANGED
|
@@ -4,7 +4,12 @@ import { join, resolve } from "node:path";
|
|
|
4
4
|
|
|
5
5
|
import { parseFlags } from "../_config.mjs";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// `default` is the universal starter: minimal, runnable, zero backend.
|
|
8
|
+
// `modular` is the long-lived business-app reference architecture:
|
|
9
|
+
// auth, billing, guards, layouts, services, connectors, contracts.
|
|
10
|
+
// Both target the same Mado runtime — they only differ in how much
|
|
11
|
+
// structure they pre-create.
|
|
12
|
+
const STARTERS = ["default", "modular"];
|
|
8
13
|
|
|
9
14
|
export async function runInit(ctx, rawArgs) {
|
|
10
15
|
const { flags, positional } = parseFlags(rawArgs);
|
|
@@ -109,8 +114,8 @@ async function ensureStarterPackageJson(ctx, target) {
|
|
|
109
114
|
pkg.devDependencies = {
|
|
110
115
|
...(pkg.devDependencies ?? {}),
|
|
111
116
|
typescript: rootDev.typescript ?? "^6.0.3",
|
|
112
|
-
linkedom: rootDev.linkedom ?? "^0.18.12",
|
|
113
117
|
lightningcss: rootDev.lightningcss ?? "^1.32.0",
|
|
118
|
+
"playwright-core": rootDev["playwright-core"] ?? "^1.47.0",
|
|
114
119
|
vite: rootDev.vite ?? "^8.0.16",
|
|
115
120
|
};
|
|
116
121
|
|
package/scripts/cli/release.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
4
|
import { brotliCompressSync, constants as zlibConst, gzipSync } from "node:zlib";
|
|
5
5
|
|
|
6
6
|
import { parseFlags } from "../_config.mjs";
|
|
@@ -12,8 +12,6 @@ export async function runRelease(ctx, rawArgs) {
|
|
|
12
12
|
ctx.projectRoot,
|
|
13
13
|
typeof releaseFlags.out === "string" ? releaseFlags.out : "out",
|
|
14
14
|
);
|
|
15
|
-
const bundledHtml = join(outDir, "index.html");
|
|
16
|
-
const bakedDir = join(outDir, "baked");
|
|
17
15
|
|
|
18
16
|
console.log(`[release] context: ${ctx.context}`);
|
|
19
17
|
console.log(`[release] artifact: ${outDir}`);
|
|
@@ -34,37 +32,22 @@ export async function runRelease(ctx, rawArgs) {
|
|
|
34
32
|
console.log("[release] step 2/5 vite build");
|
|
35
33
|
await runVite(ctx, ["build", "--outDir", outDir], { defaultConfig: true });
|
|
36
34
|
|
|
37
|
-
console.log("[release] step 3/5
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
console.log(`[release] copied sitemap.xml -> ${join(outDir, "sitemap.xml")}`);
|
|
52
|
-
}
|
|
53
|
-
} else {
|
|
54
|
-
await runNodeScript(ctx, "scripts/bake.mjs", [
|
|
55
|
-
...rawArgs,
|
|
56
|
-
"--template",
|
|
57
|
-
bundledHtml,
|
|
58
|
-
"--out",
|
|
59
|
-
outDir,
|
|
60
|
-
]);
|
|
35
|
+
console.log("[release] step 3/5 static snapshots");
|
|
36
|
+
await runNodeScript(ctx, "scripts/static.mjs", [
|
|
37
|
+
...rawArgs.filter((a) => a !== "--no-clean"),
|
|
38
|
+
"--out",
|
|
39
|
+
outDir,
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
console.log("[release] step 4/5 deployment files");
|
|
43
|
+
// GitHub Pages / Netlify / Cloudflare Pages fallback. SPA fallback shell
|
|
44
|
+
// is written by `mado static`; here we only register the deployment
|
|
45
|
+
// bindings, and respect user-supplied files (writeIfMissing).
|
|
46
|
+
const spaShell = join(outDir, "_mado/spa.html");
|
|
47
|
+
if (existsSync(spaShell)) {
|
|
48
|
+
await writeIfMissing(join(outDir, "404.html"), await readFile(spaShell, "utf8"), "[release] ");
|
|
61
49
|
}
|
|
62
|
-
|
|
63
|
-
console.log("[release] step 4/5 precompress assets");
|
|
64
|
-
await precompressOut(outDir);
|
|
65
|
-
|
|
66
|
-
console.log("[release] step 5/5 CDN config");
|
|
67
|
-
await writeIfMissing(join(outDir, "_redirects"), "/* /index.html 200\n", "[release] ");
|
|
50
|
+
await writeIfMissing(join(outDir, "_redirects"), "/* /_mado/spa.html 200\n", "[release] ");
|
|
68
51
|
await writeIfMissing(
|
|
69
52
|
join(outDir, "_headers"),
|
|
70
53
|
[
|
|
@@ -78,45 +61,14 @@ export async function runRelease(ctx, rawArgs) {
|
|
|
78
61
|
"[release] ",
|
|
79
62
|
);
|
|
80
63
|
|
|
64
|
+
console.log("[release] step 5/5 precompress assets");
|
|
65
|
+
await precompressOut(outDir);
|
|
66
|
+
|
|
81
67
|
console.log("");
|
|
82
68
|
console.log(`[release] done. Deploy artifact: ${outDir}`);
|
|
83
69
|
console.log("[release] try: mado preview");
|
|
84
70
|
}
|
|
85
71
|
|
|
86
|
-
async function promoteBakedHtml(bakedDir, outDir) {
|
|
87
|
-
if (!existsSync(bakedDir)) return { html: 0, sitemap: false };
|
|
88
|
-
|
|
89
|
-
let html = 0;
|
|
90
|
-
|
|
91
|
-
async function walk(dir, rel = "") {
|
|
92
|
-
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
93
|
-
const nextRel = rel ? `${rel}/${entry.name}` : entry.name;
|
|
94
|
-
const source = join(dir, entry.name);
|
|
95
|
-
if (entry.isDirectory()) {
|
|
96
|
-
await walk(source, nextRel);
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
if (!entry.isFile() || !entry.name.endsWith(".html")) continue;
|
|
100
|
-
const target = join(outDir, nextRel);
|
|
101
|
-
await mkdir(dirname(target), { recursive: true });
|
|
102
|
-
await copyFile(source, target);
|
|
103
|
-
html++;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
await walk(bakedDir);
|
|
108
|
-
|
|
109
|
-
const bakedSitemap = join(bakedDir, "sitemap.xml");
|
|
110
|
-
const rootSitemap = join(outDir, "sitemap.xml");
|
|
111
|
-
let sitemap = false;
|
|
112
|
-
if (existsSync(bakedSitemap)) {
|
|
113
|
-
await copyFile(bakedSitemap, rootSitemap);
|
|
114
|
-
sitemap = true;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return { html, sitemap };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
72
|
async function precompressOut(outDir) {
|
|
121
73
|
if (!existsSync(outDir)) return;
|
|
122
74
|
const files = await listCompressibleFiles(outDir);
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Docs lint: refuses to ship documentation that still uses the legacy
|
|
3
|
+
// vocabulary the 0.12 release replaced. Active reference docs, README,
|
|
4
|
+
// llms.txt, AGENTS.md, CONTRIBUTING.md, TODO.md and the starter READMEs
|
|
5
|
+
// must use the new terms; migration guides, CHANGELOG, release notes
|
|
6
|
+
// and ADRs are allowed to mention the old names by name so users can
|
|
7
|
+
// find them.
|
|
8
|
+
|
|
9
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
10
|
+
import { dirname, join, resolve } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const REPO_ROOT = resolve(__dirname, "..");
|
|
15
|
+
|
|
16
|
+
// Phrases that MUST disappear from current docs/README/llms/AGENTS. They
|
|
17
|
+
// remain legal inside the allow-listed contexts below.
|
|
18
|
+
const FORBIDDEN = [
|
|
19
|
+
// Page API renamed from `bake` to `static`.
|
|
20
|
+
{ pattern: /\bpage\.bake\b/, replacement: "page.static" },
|
|
21
|
+
{ pattern: /\bbake\.paths\b/, replacement: "static.paths" },
|
|
22
|
+
{ pattern: /\bbake\.data\b/, replacement: "static.initialData" },
|
|
23
|
+
{ pattern: /\bbake\.revalidate\b/, replacement: "drop; no revalidate API" },
|
|
24
|
+
|
|
25
|
+
// CLI renamed from `bake` to `static`.
|
|
26
|
+
{ pattern: /\bmado bake\b/, replacement: "mado static" },
|
|
27
|
+
{ pattern: /\bnpm run bake\b/, replacement: "npm run release" },
|
|
28
|
+
|
|
29
|
+
// Output / marker shape changed.
|
|
30
|
+
{ pattern: /\bout\/baked\b/, replacement: "out/<route>/index.html" },
|
|
31
|
+
{ pattern: /#bake\b/, replacement: "data-mado-static-data" },
|
|
32
|
+
|
|
33
|
+
// Old "Smart Static (`bake`)" headline.
|
|
34
|
+
{ pattern: /Smart Static \(`bake`\)/, replacement: "Static snapshots (`mado static`)" },
|
|
35
|
+
|
|
36
|
+
// Old transport / renderer claims.
|
|
37
|
+
{ pattern: /No Vite required/i, replacement: "drop the phrase; Vite is the canonical transport" },
|
|
38
|
+
{ pattern: /\bNo Chromium needed\b/i, replacement: "drop the phrase; mado static REQUIRES Chromium" },
|
|
39
|
+
// `linkedom` is also a legitimate Node-side test helper (the framework
|
|
40
|
+
// uses it in test/router/* for DOM unit tests). We only flag the
|
|
41
|
+
// phrases that mis-described the snapshot pipeline as a linkedom
|
|
42
|
+
// renderer.
|
|
43
|
+
{ pattern: /linkedom (?:as|is) (?:the |a )?(?:static|snapshot|production) renderer/i,
|
|
44
|
+
replacement: "drop the claim; capture uses Playwright/Chromium" },
|
|
45
|
+
{ pattern: /linkedom-?based renderer/i,
|
|
46
|
+
replacement: "drop the claim; capture uses Playwright/Chromium" },
|
|
47
|
+
{ pattern: /server-rendered snapshot/i, replacement: "browser-rendered snapshot" },
|
|
48
|
+
{ pattern: /\bmeta[- ]shell\b/i, replacement: "static snapshot" },
|
|
49
|
+
|
|
50
|
+
// Old positioning.
|
|
51
|
+
{
|
|
52
|
+
pattern: /SEO-heavy public sites are not supported/i,
|
|
53
|
+
replacement: "drop the phrase — Mado now snapshots public sites",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
pattern: /\binternal tools only\b/i,
|
|
57
|
+
replacement: "drop the phrase — Mado is for sites and apps",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
pattern: /SPA framework for internal tools/i,
|
|
61
|
+
replacement: "drop the phrase — Mado is for sites and apps",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
pattern: /shadow:\s*false[^.\n]*SEO/i,
|
|
65
|
+
replacement: "drop the recommendation; SEO is handled by snapshots",
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// Old pre-Vite transport language.
|
|
69
|
+
{ pattern: /\btsc[- ]only\b/i, replacement: "drop the phrase — Vite is the canonical transport" },
|
|
70
|
+
{ pattern: /\bno bundler\b/i, replacement: "drop the phrase — Vite is the canonical bundler" },
|
|
71
|
+
{ pattern: /\btsc\s*(?:→|->)+\s*browser\b/i,
|
|
72
|
+
replacement: "drop the phrase — generated apps go through Vite" },
|
|
73
|
+
{ pattern: /\bimport[\s-]?map(?:s)?\b/i, replacement: "drop the phrase — apps use Vite, not import maps" },
|
|
74
|
+
|
|
75
|
+
// Old dev / static / edge claims.
|
|
76
|
+
{ pattern: /SSE[- ]?reload/i, replacement: "drop the phrase — dev uses Vite HMR" },
|
|
77
|
+
{ pattern: /\bedge[- ]prerender\b/i, replacement: "drop the phrase — capture is `mado static` only" },
|
|
78
|
+
|
|
79
|
+
// Old doc filenames in active docs (the migration table in
|
|
80
|
+
// docs/en/README.md is allow-listed via docs-lint:allow-legacy-mention
|
|
81
|
+
// markers around it if needed).
|
|
82
|
+
{ pattern: /\b03-static-bake\.md\b/, replacement: "15-static-snapshots.md" },
|
|
83
|
+
{ pattern: /\b09-shadow-vs-light-dom\.md\b/, replacement: "10-pages-and-components.md" },
|
|
84
|
+
{ pattern: /\b10-app-architecture\.md\b/, replacement: "16-app-architecture.md" },
|
|
85
|
+
{ pattern: /\b13-deployment\.md\b/, replacement: "20-deployment.md" },
|
|
86
|
+
{ pattern: /\b16-bake-cookbook\.md\b/, replacement: "23-cookbook.md" },
|
|
87
|
+
{ pattern: /\b18-api-freeze-map\.md\b/, replacement: "30-api-freeze-map.md" },
|
|
88
|
+
{ pattern: /\b19-reactivity-ordering\.md\b/, replacement: "31-reactivity-ordering.md" },
|
|
89
|
+
{ pattern: /\b20-v1-stability\.md\b/, replacement: "32-v1-stability.md" },
|
|
90
|
+
{ pattern: /\b07-llm-pitfalls\.md\b/, replacement: "40-llm-guide.md" },
|
|
91
|
+
{ pattern: /\b08-llm-zero-history-test\.md\b/, replacement: "40-llm-guide.md" },
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
const ALLOW_LISTED = [
|
|
95
|
+
/(^|\/)CHANGELOG\.md$/,
|
|
96
|
+
/(^|\/)docs\/architecture\/adr\//,
|
|
97
|
+
/(^|\/)docs\/.+migration/i,
|
|
98
|
+
/(^|\/)docs\/.+v1-stability/i,
|
|
99
|
+
/(^|\/)scripts\/docs-lint\.mjs$/,
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
// Files / trees that must stay on-message about the post-0.12 API. New
|
|
103
|
+
// roots must be added here AND covered by the CI step.
|
|
104
|
+
const ROOTS = [
|
|
105
|
+
"README.md",
|
|
106
|
+
"AGENTS.md",
|
|
107
|
+
"llms.txt",
|
|
108
|
+
"CONTRIBUTING.md",
|
|
109
|
+
"TODO.md",
|
|
110
|
+
"docs/README.md",
|
|
111
|
+
"docs/en",
|
|
112
|
+
"starters/default/README.md",
|
|
113
|
+
"starters/modular/README.md",
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
let errors = 0;
|
|
117
|
+
|
|
118
|
+
for (const root of ROOTS) {
|
|
119
|
+
const full = join(REPO_ROOT, root);
|
|
120
|
+
try {
|
|
121
|
+
statSync(full);
|
|
122
|
+
} catch {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
scan(full);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (errors > 0) {
|
|
129
|
+
console.error(`\n[docs-lint] ${errors} forbidden term(s) in current docs.`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
console.log("[docs-lint] OK");
|
|
133
|
+
|
|
134
|
+
function scan(path) {
|
|
135
|
+
const stat = statSync(path);
|
|
136
|
+
if (stat.isDirectory()) {
|
|
137
|
+
for (const entry of readdirSync(path)) scan(join(path, entry));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (!path.endsWith(".md") && !path.endsWith(".txt")) return;
|
|
141
|
+
const rel = path.slice(REPO_ROOT.length + 1);
|
|
142
|
+
if (ALLOW_LISTED.some((rx) => rx.test(rel))) return;
|
|
143
|
+
|
|
144
|
+
const body = readFileSync(path, "utf8");
|
|
145
|
+
const lines = body.split("\n");
|
|
146
|
+
let inIgnoreBlock = false;
|
|
147
|
+
for (let i = 0; i < lines.length; i++) {
|
|
148
|
+
const line = lines[i];
|
|
149
|
+
// Block-scoped allowance: paragraphs that teach LLMs which names
|
|
150
|
+
// are obsolete must be free to mention those names verbatim.
|
|
151
|
+
// <!-- docs-lint:allow-legacy-mention -->
|
|
152
|
+
// ...legacy terms...
|
|
153
|
+
// <!-- /docs-lint:allow-legacy-mention -->
|
|
154
|
+
if (line.includes("docs-lint:allow-legacy-mention")) {
|
|
155
|
+
inIgnoreBlock = !line.includes("/docs-lint:allow-legacy-mention");
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (inIgnoreBlock) continue;
|
|
159
|
+
|
|
160
|
+
for (const rule of FORBIDDEN) {
|
|
161
|
+
if (rule.pattern.test(line)) {
|
|
162
|
+
errors++;
|
|
163
|
+
console.error(
|
|
164
|
+
`${rel}:${i + 1}: forbidden term matches /${rule.pattern.source}/. ` +
|
|
165
|
+
`Replace with: ${rule.replacement}`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -21,18 +21,24 @@ try {
|
|
|
21
21
|
await mkdir(installRoot, { recursive: true });
|
|
22
22
|
await run("npm", ["install", tarball], { cwd: installRoot });
|
|
23
23
|
|
|
24
|
+
// Public-API surface smoke: prove the published tarball exposes the
|
|
25
|
+
// root entry, the devtools side-effect import and the Vite plugin,
|
|
26
|
+
// while still hiding internal subpaths behind `exports`.
|
|
24
27
|
await run(
|
|
25
28
|
process.execPath,
|
|
26
29
|
[
|
|
27
30
|
"--input-type=module",
|
|
28
31
|
"--eval",
|
|
29
32
|
`
|
|
30
|
-
import { html, signal } from "@madojs/mado";
|
|
33
|
+
import { html, signal, routeUrl, appBase } from "@madojs/mado";
|
|
31
34
|
import "@madojs/mado/devtools.js";
|
|
32
35
|
import { mado } from "@madojs/mado/vite";
|
|
33
36
|
if (typeof html !== "function" || typeof signal !== "function") {
|
|
34
37
|
throw new Error("public root import failed");
|
|
35
38
|
}
|
|
39
|
+
if (typeof routeUrl !== "function" || typeof appBase !== "string") {
|
|
40
|
+
throw new Error("routing helpers missing from public root");
|
|
41
|
+
}
|
|
36
42
|
if (typeof mado !== "function") throw new Error("vite plugin import failed");
|
|
37
43
|
try {
|
|
38
44
|
await import("@madojs/mado/lifecycle.js");
|
|
@@ -45,15 +51,56 @@ try {
|
|
|
45
51
|
{ cwd: installRoot },
|
|
46
52
|
);
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
// We exercise BOTH published starters end-to-end:
|
|
55
|
+
//
|
|
56
|
+
// smoke-universal — `mado init` (default), npm install, typecheck,
|
|
57
|
+
// release. This is the path the README quick-start
|
|
58
|
+
// points new users to; if it ever breaks for a
|
|
59
|
+
// published tarball the first-run experience is
|
|
60
|
+
// broken.
|
|
61
|
+
//
|
|
62
|
+
// smoke-modular — `mado init --starter modular`, npm install,
|
|
63
|
+
// `mado new module`, release. Long-lived apps
|
|
64
|
+
// deploy this shape; the `mado new` generator
|
|
65
|
+
// also lives here.
|
|
66
|
+
//
|
|
67
|
+
// Each run uses MADO_SITE so static routes resolve canonical URLs,
|
|
68
|
+
// and points the starter at the freshly packed tarball through
|
|
69
|
+
// MADO_PACKAGE_SPEC so package.json doesn't reference an unpublished
|
|
70
|
+
// version.
|
|
71
|
+
|
|
72
|
+
await smokeStarter({
|
|
73
|
+
label: "universal",
|
|
74
|
+
appName: "smoke-universal",
|
|
75
|
+
initArgs: ["mado", "init", "smoke-universal"],
|
|
76
|
+
after: async (appRoot) => {
|
|
77
|
+
await run("npm", ["run", "typecheck"], { cwd: appRoot });
|
|
78
|
+
await run("npm", ["run", "release"], {
|
|
79
|
+
cwd: appRoot,
|
|
80
|
+
env: { ...process.env, MADO_SITE: "https://package-smoke.test" },
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
installRoot,
|
|
84
|
+
tarball,
|
|
51
85
|
});
|
|
52
86
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
87
|
+
await smokeStarter({
|
|
88
|
+
label: "modular",
|
|
89
|
+
appName: "smoke-modular",
|
|
90
|
+
initArgs: ["mado", "init", "smoke-modular", "--starter", "modular"],
|
|
91
|
+
after: async (appRoot) => {
|
|
92
|
+
await run("npm", ["run", "new", "--", "module", "smoke"], {
|
|
93
|
+
cwd: appRoot,
|
|
94
|
+
});
|
|
95
|
+
await run("npm", ["run", "typecheck"], { cwd: appRoot });
|
|
96
|
+
await run("npm", ["run", "release"], {
|
|
97
|
+
cwd: appRoot,
|
|
98
|
+
env: { ...process.env, MADO_SITE: "https://package-smoke.test" },
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
installRoot,
|
|
102
|
+
tarball,
|
|
103
|
+
});
|
|
57
104
|
|
|
58
105
|
console.log(`[package-smoke] ok ${basename(tarball)}`);
|
|
59
106
|
} finally {
|
|
@@ -61,6 +108,17 @@ try {
|
|
|
61
108
|
if (tarball) await rm(tarball, { force: true });
|
|
62
109
|
}
|
|
63
110
|
|
|
111
|
+
async function smokeStarter({ label, appName, initArgs, after, installRoot, tarball }) {
|
|
112
|
+
console.log(`[package-smoke] === ${label} starter ===`);
|
|
113
|
+
await run("npx", initArgs, {
|
|
114
|
+
cwd: installRoot,
|
|
115
|
+
env: { ...process.env, MADO_PACKAGE_SPEC: tarball },
|
|
116
|
+
});
|
|
117
|
+
const appRoot = join(installRoot, appName);
|
|
118
|
+
await run("npm", ["install"], { cwd: appRoot });
|
|
119
|
+
await after(appRoot);
|
|
120
|
+
}
|
|
121
|
+
|
|
64
122
|
async function run(cmd, args, options) {
|
|
65
123
|
console.log(`[package-smoke] ${cmd} ${args.join(" ")}`);
|
|
66
124
|
try {
|
|
@@ -74,4 +132,4 @@ async function run(cmd, args, options) {
|
|
|
74
132
|
if (err.stderr) process.stderr.write(err.stderr);
|
|
75
133
|
throw err;
|
|
76
134
|
}
|
|
77
|
-
}
|
|
135
|
+
}
|
package/scripts/preview.mjs
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// PREVIEW_AUTOBUILD=1.
|
|
10
10
|
// 3. Starts a static server with:
|
|
11
11
|
// - immutable cache for hashed bundles;
|
|
12
|
-
// - SPA fallback to index.html;
|
|
12
|
+
// - SPA fallback to _mado/spa.html when present, else index.html;
|
|
13
13
|
// - exact `out/` route files before SPA fallback;
|
|
14
14
|
// - precompressed .gz / .br serving via Accept-Encoding.
|
|
15
15
|
//
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
// what a static host (nginx / Cloudflare Pages / S3) would serve.
|
|
18
18
|
|
|
19
19
|
import { createServer } from "node:http";
|
|
20
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
20
21
|
import { readFile, stat, access } from "node:fs/promises";
|
|
21
22
|
import { extname, join, resolve, sep } from "node:path";
|
|
22
23
|
import { spawnSync } from "node:child_process";
|
|
@@ -55,6 +56,68 @@ const HOST = String(PREVIEW_FLAGS.host ?? process.env.HOST ?? "localhost");
|
|
|
55
56
|
const AUTOBUILD = process.env.PREVIEW_AUTOBUILD === "1";
|
|
56
57
|
const SKIP_BUILD = process.env.SKIP_BUILD === "1" || !AUTOBUILD;
|
|
57
58
|
|
|
59
|
+
// Active Vite base. We try the internal build bridge first (the
|
|
60
|
+
// `@madojs/mado/vite` plugin writes `_mado/build.json` during `vite
|
|
61
|
+
// build`); if `mado static` has already dropped that file before
|
|
62
|
+
// shipping the production artifact, we fall back to parsing the asset
|
|
63
|
+
// prefix out of `out/index.html`. Both paths converge on the same
|
|
64
|
+
// canonical "/" or "/prefix/" form so the preview server replays the
|
|
65
|
+
// real deployment URL shape regardless of pipeline stage.
|
|
66
|
+
const BASE = detectDeployedBase(OUT);
|
|
67
|
+
|
|
68
|
+
function detectDeployedBase(out) {
|
|
69
|
+
const fromBridge = readBridgeBase(out);
|
|
70
|
+
if (fromBridge != null) return fromBridge;
|
|
71
|
+
const fromHtml = readHtmlBase(out);
|
|
72
|
+
if (fromHtml != null) return fromHtml;
|
|
73
|
+
return "/";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function readBridgeBase(out) {
|
|
77
|
+
try {
|
|
78
|
+
const meta = JSON.parse(
|
|
79
|
+
readFileSync(join(out, "_mado/build.json"), "utf8"),
|
|
80
|
+
);
|
|
81
|
+
return meta?.base ? normalizeBase(meta.base) : null;
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function readHtmlBase(out) {
|
|
88
|
+
try {
|
|
89
|
+
const html = readFileSync(join(out, "index.html"), "utf8");
|
|
90
|
+
// Look at the first hashed asset; Vite always prefixes it with
|
|
91
|
+
// the deployed base.
|
|
92
|
+
const match = /(?:href|src)="(\/[^"]*?\/)assets\//.exec(html);
|
|
93
|
+
if (match && match[1]) return normalizeBase(match[1]);
|
|
94
|
+
// No prefix means root-deployed.
|
|
95
|
+
if (/(?:href|src)="\/assets\//.test(html)) return "/";
|
|
96
|
+
return null;
|
|
97
|
+
} catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function normalizeBase(raw) {
|
|
103
|
+
if (!raw) return "/";
|
|
104
|
+
let s = String(raw).trim();
|
|
105
|
+
if (!s || s === "/") return "/";
|
|
106
|
+
if (!s.startsWith("/")) s = "/" + s;
|
|
107
|
+
if (!s.endsWith("/")) s = s + "/";
|
|
108
|
+
return s.replace(/\/+/g, "/");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function stripBase(pathname) {
|
|
112
|
+
if (BASE === "/") return pathname.startsWith("/") ? pathname : "/" + pathname;
|
|
113
|
+
if (pathname === BASE || pathname === BASE.slice(0, -1)) return "/";
|
|
114
|
+
if (pathname.startsWith(BASE)) {
|
|
115
|
+
const rest = pathname.slice(BASE.length - 1);
|
|
116
|
+
return rest || "/";
|
|
117
|
+
}
|
|
118
|
+
return pathname;
|
|
119
|
+
}
|
|
120
|
+
|
|
58
121
|
const MIME = {
|
|
59
122
|
".html": "text/html; charset=utf-8",
|
|
60
123
|
".js": "text/javascript; charset=utf-8",
|
|
@@ -86,11 +149,13 @@ if (!(await exists(OUT))) {
|
|
|
86
149
|
process.exit(1);
|
|
87
150
|
}
|
|
88
151
|
|
|
89
|
-
const spaShell = join(OUT, "
|
|
152
|
+
const spaShell = existsSync(join(OUT, "_mado", "spa.html"))
|
|
153
|
+
? join(OUT, "_mado", "spa.html")
|
|
154
|
+
: join(OUT, "index.html");
|
|
90
155
|
if (!(await exists(spaShell))) {
|
|
91
156
|
console.error(
|
|
92
157
|
`[preview] missing ${spaShell} — \`mado release\` did not produce an HTML entry.\n` +
|
|
93
|
-
`[preview] Without it any non-
|
|
158
|
+
`[preview] Without it any non-static route will 404 instead of falling back to the SPA.`,
|
|
94
159
|
);
|
|
95
160
|
process.exit(1);
|
|
96
161
|
}
|
|
@@ -103,7 +168,24 @@ const isImmutable = (filename) =>
|
|
|
103
168
|
const server = createServer(async (req, res) => {
|
|
104
169
|
try {
|
|
105
170
|
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
106
|
-
const
|
|
171
|
+
const fullPathname = decodeURIComponent(url.pathname);
|
|
172
|
+
// Honour the deployed Vite base: redirect bare `/` to `/${base}/` and
|
|
173
|
+
// strip the prefix before any lookup, so `out/index.html`,
|
|
174
|
+
// `out/assets/...` and `out/docs/index.html` are reachable through
|
|
175
|
+
// the same URL shape the CDN serves.
|
|
176
|
+
if (BASE !== "/") {
|
|
177
|
+
const bareBase = BASE.slice(0, -1);
|
|
178
|
+
if (fullPathname === "/" || fullPathname === bareBase) {
|
|
179
|
+
res.writeHead(302, { location: BASE });
|
|
180
|
+
res.end();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (!fullPathname.startsWith(BASE) && fullPathname !== bareBase) {
|
|
184
|
+
res.writeHead(404).end("not found");
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const pathname = stripBase(fullPathname);
|
|
107
189
|
const accepts = (req.headers["accept-encoding"] ?? "").toString();
|
|
108
190
|
|
|
109
191
|
const target = await resolveTarget(pathname);
|
|
@@ -158,7 +240,12 @@ server.listen(PORT, HOST, async () => {
|
|
|
158
240
|
const urlHost = HOST === "0.0.0.0" || HOST === "::" ? "localhost" : HOST;
|
|
159
241
|
console.log("");
|
|
160
242
|
console.log("Mado preview (production-like)");
|
|
161
|
-
|
|
243
|
+
// Print the URL the server actually serves the app at. When the
|
|
244
|
+
// deployment uses a non-trivial Vite `base`, the bare root just
|
|
245
|
+
// 302-redirects to the base prefix, which surprises users who copy
|
|
246
|
+
// the logged URL into a browser and land on "Not Found".
|
|
247
|
+
console.log(` url: http://${urlHost}:${PORT}${BASE}`);
|
|
248
|
+
console.log(` base: ${BASE}`);
|
|
162
249
|
console.log(` out: ${OUT}`);
|
|
163
250
|
console.log(" (Ctrl-C to stop)");
|
|
164
251
|
console.log("");
|
|
@@ -216,7 +303,9 @@ async function resolveTarget(pathname) {
|
|
|
216
303
|
// extension) deliberately 404 instead — otherwise a 200 on
|
|
217
304
|
// /missing.png would mask real bugs.
|
|
218
305
|
if (!extname(pathname)) {
|
|
219
|
-
const spa = join(OUT, "
|
|
306
|
+
const spa = existsSync(join(OUT, "_mado", "spa.html"))
|
|
307
|
+
? join(OUT, "_mado", "spa.html")
|
|
308
|
+
: join(OUT, "index.html");
|
|
220
309
|
if (await exists(spa)) return spa;
|
|
221
310
|
}
|
|
222
311
|
|
package/scripts/size-budget.mjs
CHANGED
|
@@ -8,7 +8,11 @@ import { gzipSync } from "node:zlib";
|
|
|
8
8
|
|
|
9
9
|
const API_ENTRY = "src/index.ts";
|
|
10
10
|
const SAMPLE_ENTRY = "starters/default/src/main.ts";
|
|
11
|
-
|
|
11
|
+
// Public API budget bumped from 16 KiB → 17 KiB to accommodate the
|
|
12
|
+
// base-aware router/navigation pack added in 0.12.0 (`src/router/base.ts`,
|
|
13
|
+
// stripBase/withBase wiring in navigation, manifest and static-runtime).
|
|
14
|
+
// Override per-environment via MADO_SIZE_API_GZIP_LIMIT.
|
|
15
|
+
const API_GZIP_LIMIT = readLimit("MADO_SIZE_API_GZIP_LIMIT", 17 * 1024);
|
|
12
16
|
const SAMPLE_GZIP_LIMIT = readLimit("MADO_SIZE_SAMPLE_GZIP_LIMIT", 42 * 1024);
|
|
13
17
|
|
|
14
18
|
let failed = false;
|