@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/bake.mjs
CHANGED
|
@@ -1,569 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
// Usage:
|
|
4
|
-
// mado bake
|
|
5
|
-
// mado bake --entry src/routes.ts --template index.html --out out
|
|
6
|
-
// mado bake --base-url https://example.com
|
|
7
|
-
//
|
|
8
|
-
// Defaults:
|
|
9
|
-
// entry: src/app.routes.ts, then src/routes.ts
|
|
10
|
-
// template: index.html
|
|
11
|
-
// out: out/
|
|
12
|
-
//
|
|
13
|
-
// What it does:
|
|
14
|
-
// 1. Loads `entry` (routes module) through Vite SSR/module loading.
|
|
15
|
-
// 2. For every route whose page has `bake`:
|
|
16
|
-
// a) gets params via `bake.paths()`,
|
|
17
|
-
// b) gets data per params via `bake.data(params)`,
|
|
18
|
-
// c) renders the TemplateResult to an HTML string (no browser),
|
|
19
|
-
// d) materializes head() into <meta>/<link>/<script type=json-ld>,
|
|
20
|
-
// e) inlines baked data in <script id="bake" type="application/json">,
|
|
21
|
-
// f) writes <out>/<path>/index.html.
|
|
22
|
-
// 3. Generates <out>/sitemap.xml.
|
|
23
|
-
//
|
|
24
|
-
// Context awareness:
|
|
25
|
-
// - In app-mode (default outside the framework repository) the bundler
|
|
26
|
-
// resolves `@madojs/mado` from node_modules normally.
|
|
27
|
-
// - In repo-mode (the framework repository itself) it aliases
|
|
28
|
-
// `@madojs/mado` → ./src/index.ts so the framework can dogfood itself.
|
|
29
|
-
//
|
|
30
|
-
// Required dev deps: linkedom, vite. We print a clear error if missing.
|
|
1
|
+
#!/usr/bin/env node
|
|
31
2
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
import { detectContext, parseFlags, resolveProjectPath } from "./_config.mjs";
|
|
37
|
-
|
|
38
|
-
// ---------- Resolve options from config + flags + env ----------
|
|
39
|
-
|
|
40
|
-
const { flags } = parseFlags(process.argv.slice(2));
|
|
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
|
-
);
|
|
56
|
-
|
|
57
|
-
/** Write message to stderr and exit. Sync write keeps CI/execFile output reliable. */
|
|
58
|
-
function fatal(...msgs) {
|
|
59
|
-
writeSync(2, msgs.join("\n") + "\n");
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function error(...msgs) {
|
|
64
|
-
writeSync(2, msgs.join(" ") + "\n");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (!existsSync(ENTRY)) {
|
|
68
|
-
fatal(
|
|
69
|
-
`[bake] entry not found: ${ENTRY}`,
|
|
70
|
-
`[bake] expected src/app.routes.ts or src/routes.ts; pass --entry <file> to override`,
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
if (!existsSync(TEMPLATE)) {
|
|
74
|
-
fatal(
|
|
75
|
-
`[bake] template not found: ${TEMPLATE}`,
|
|
76
|
-
`[bake] expected index.html; pass --template <file> to override`,
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// ---------- Optional dependencies ----------
|
|
81
|
-
|
|
82
|
-
let parseHTML;
|
|
83
|
-
try {
|
|
84
|
-
({ parseHTML } = await import("linkedom"));
|
|
85
|
-
} catch {
|
|
86
|
-
fatal(
|
|
87
|
-
"[bake] package 'linkedom' is required.",
|
|
88
|
-
"[bake] Install it as a dev dependency in this project:",
|
|
89
|
-
"[bake] npm i -D linkedom vite",
|
|
90
|
-
"[bake] (vite is also required, see next check).",
|
|
91
|
-
"[bake] These are not bundled into @madojs/mado on purpose: bake is an",
|
|
92
|
-
"[bake] optional build step and we don't want to add transitive deps to",
|
|
93
|
-
"[bake] every Mado install.",
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
let createViteServer;
|
|
98
|
-
try {
|
|
99
|
-
({ createServer: createViteServer } = await import("vite"));
|
|
100
|
-
} catch {
|
|
101
|
-
fatal(
|
|
102
|
-
"[bake] package 'vite' is required.",
|
|
103
|
-
"[bake] Install it as a dev dependency in this project:",
|
|
104
|
-
"[bake] npm i -D vite linkedom",
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ---------- DOM polyfills for Node ----------
|
|
109
|
-
//
|
|
110
|
-
// router.ts / component.ts / css.ts touch window/document/location/customElements
|
|
111
|
-
// at module top-level. Node has none, so install linkedom stubs before importing
|
|
112
|
-
// the app graph.
|
|
113
|
-
|
|
114
|
-
const baseHtml = await readFile(TEMPLATE, "utf8");
|
|
115
|
-
const { window: linkedomWindow } = parseHTML(baseHtml);
|
|
116
|
-
|
|
117
|
-
globalThis.window = linkedomWindow;
|
|
118
|
-
globalThis.document = linkedomWindow.document;
|
|
119
|
-
globalThis.location = new URL("http://localhost/");
|
|
120
|
-
globalThis.history = { pushState: () => { }, replaceState: () => { } };
|
|
121
|
-
globalThis.customElements = {
|
|
122
|
-
define: () => { },
|
|
123
|
-
get: () => undefined,
|
|
124
|
-
whenDefined: () => Promise.resolve(),
|
|
125
|
-
};
|
|
126
|
-
globalThis.HTMLElement = linkedomWindow.HTMLElement ?? class { };
|
|
127
|
-
globalThis.CSSStyleSheet = globalThis.CSSStyleSheet ?? class {
|
|
128
|
-
cssRules = [];
|
|
129
|
-
replaceSync() { }
|
|
130
|
-
};
|
|
131
|
-
globalThis.matchMedia = () => ({
|
|
132
|
-
matches: false,
|
|
133
|
-
addEventListener: () => { },
|
|
134
|
-
removeEventListener: () => { },
|
|
135
|
-
});
|
|
136
|
-
if (!globalThis.queueMicrotask) {
|
|
137
|
-
globalThis.queueMicrotask = (fn) => Promise.resolve().then(fn);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// ---------- Load the routes module through Vite ----------
|
|
141
|
-
//
|
|
142
|
-
// In repo-mode the framework dogfoods its own source; alias `@madojs/mado`
|
|
143
|
-
// to ./src/index.ts. In app-mode the package is resolved from node_modules.
|
|
144
|
-
|
|
145
|
-
const aliases = CONTEXT === "repo"
|
|
146
|
-
? { "@madojs/mado": resolve(PROJECT_ROOT, "src/index.ts") }
|
|
147
|
-
: {};
|
|
148
|
-
|
|
149
|
-
const viteServer = await createViteServer({
|
|
150
|
-
root: PROJECT_ROOT,
|
|
151
|
-
logLevel: "error",
|
|
152
|
-
server: { middlewareMode: true },
|
|
153
|
-
appType: "custom",
|
|
154
|
-
resolve: { alias: aliases },
|
|
155
|
-
});
|
|
156
|
-
|
|
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
|
-
}
|
|
163
|
-
const routeApi = routesModule.default;
|
|
164
|
-
|
|
165
|
-
if (!routeApi) {
|
|
166
|
-
await closeAndFatal(`[bake] ${ENTRY} must default-export routes({...})`);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Bake needs the source manifest (not the runtime RouterApi).
|
|
170
|
-
// routes.ts must therefore also `export const manifest = {...}`.
|
|
171
|
-
const manifest = routesModule.manifest;
|
|
172
|
-
if (!manifest) {
|
|
173
|
-
await closeAndFatal(
|
|
174
|
-
`[bake] ${ENTRY} must also \`export const manifest = {...}\` ` +
|
|
175
|
-
"(the same object passed to routes()).",
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// ---------- Main loop ----------
|
|
180
|
-
|
|
181
|
-
await mkdir(OUT_DIR, { recursive: true });
|
|
182
|
-
|
|
183
|
-
// Re-read template (already loaded for DOM polyfills, but we want a fresh copy
|
|
184
|
-
// per generated page so meta/link tags don't accumulate across iterations).
|
|
185
|
-
const TEMPLATE_HTML = baseHtml;
|
|
186
|
-
|
|
187
|
-
const sitemapEntries = [];
|
|
188
|
-
let total = 0;
|
|
189
|
-
let bakedErrors = 0;
|
|
190
|
-
let bakeablePages = 0;
|
|
191
|
-
const skippedNoBake = [];
|
|
192
|
-
|
|
193
|
-
for (const [pattern, entry] of Object.entries(manifest)) {
|
|
194
|
-
if (pattern === "*") continue;
|
|
195
|
-
|
|
196
|
-
const pg = await resolvePage(entry);
|
|
197
|
-
if (!pg) continue;
|
|
198
|
-
if (!pg.bake) {
|
|
199
|
-
skippedNoBake.push(pattern);
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
bakeablePages++;
|
|
203
|
-
|
|
204
|
-
console.log(`[bake] ${pattern}`);
|
|
205
|
-
|
|
206
|
-
let allParams;
|
|
207
|
-
try {
|
|
208
|
-
allParams = await pg.bake.paths();
|
|
209
|
-
} catch (err) {
|
|
210
|
-
error(`[bake] ${pattern}: bake.paths() failed:`, err.message);
|
|
211
|
-
bakedErrors++;
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
for (const params of allParams) {
|
|
216
|
-
const pathname = applyParams(pattern, params);
|
|
217
|
-
let data;
|
|
218
|
-
try {
|
|
219
|
-
data = await pg.bake.data(params);
|
|
220
|
-
} catch (err) {
|
|
221
|
-
error(`[bake] ${pathname}: bake.data() failed:`, err.message);
|
|
222
|
-
bakedErrors++;
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
const headMeta = pg.head ? pg.head(params, data) : {};
|
|
226
|
-
|
|
227
|
-
// Ensure <title> is always set in baked HTML. page.title is the primary
|
|
228
|
-
// source (string or function of params); head().title overrides it.
|
|
229
|
-
if (!headMeta.title && pg.title) {
|
|
230
|
-
headMeta.title = typeof pg.title === "function" ? pg.title(params) : pg.title;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const tpl = pg.view({
|
|
234
|
-
params,
|
|
235
|
-
data,
|
|
236
|
-
path: () => pathname,
|
|
237
|
-
child: null,
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
let bodyHtml;
|
|
241
|
-
try {
|
|
242
|
-
bodyHtml = renderTemplate(tpl, { route: pattern });
|
|
243
|
-
} catch (err) {
|
|
244
|
-
error(`[bake] ${pathname}: render failed:`, err.message);
|
|
245
|
-
bakedErrors++;
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const finalHtml = buildHtml({
|
|
250
|
-
template: TEMPLATE_HTML,
|
|
251
|
-
bodyHtml,
|
|
252
|
-
head: headMeta,
|
|
253
|
-
bakedData: data,
|
|
254
|
-
revalidate: pg.bake.revalidate,
|
|
255
|
-
canonical: headMeta.canonical ?? `${BASE_URL}${pathname}`,
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
const file = join(
|
|
259
|
-
OUT_DIR,
|
|
260
|
-
pathname === "/" ? "/index.html" : `${pathname}/index.html`,
|
|
261
|
-
);
|
|
262
|
-
await mkdir(dirname(file), { recursive: true });
|
|
263
|
-
await writeFile(file, finalHtml);
|
|
264
|
-
total++;
|
|
265
|
-
sitemapEntries.push({ loc: `${BASE_URL}${pathname}`, changefreq: "weekly" });
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// ---------- Sitemap ----------
|
|
270
|
-
|
|
271
|
-
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
|
|
272
|
-
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
273
|
-
${sitemapEntries
|
|
274
|
-
.map(
|
|
275
|
-
(e) =>
|
|
276
|
-
` <url><loc>${escapeXml(e.loc)}</loc><changefreq>${e.changefreq}</changefreq></url>`,
|
|
277
|
-
)
|
|
278
|
-
.join("\n")}
|
|
279
|
-
</urlset>
|
|
280
|
-
`;
|
|
281
|
-
await writeFile(join(OUT_DIR, "sitemap.xml"), sitemap);
|
|
282
|
-
|
|
283
|
-
console.log(`[bake] done: ${total} pages + sitemap.xml → ${OUT_DIR}`);
|
|
284
|
-
if (bakedErrors > 0) {
|
|
285
|
-
await closeAndFatal(`[bake] ${bakedErrors} route(s) failed; see errors above.`);
|
|
286
|
-
}
|
|
287
|
-
// Loud diagnostic when the manifest exists but no page declares `bake`.
|
|
288
|
-
// Previously bake silently produced 0 pages + an empty sitemap and exited
|
|
289
|
-
// 0, which made `mado release` look successful while shipping no static
|
|
290
|
-
// HTML for crawlers. Fail loudly so the user notices.
|
|
291
|
-
if (bakeablePages === 0) {
|
|
292
|
-
error("");
|
|
293
|
-
error(
|
|
294
|
-
`[bake] WARNING: no page in ${ENTRY} declares \`bake: { paths, data }\`.`,
|
|
295
|
-
);
|
|
296
|
-
error(
|
|
297
|
-
`[bake] ${skippedNoBake.length} route(s) skipped: ${skippedNoBake
|
|
298
|
-
.slice(0, 6)
|
|
299
|
-
.join(", ")}${skippedNoBake.length > 6 ? ", …" : ""}`,
|
|
300
|
-
);
|
|
301
|
-
error("[bake] Add `bake` to at least one page (e.g. your landing route):");
|
|
302
|
-
error("[bake] export default page({");
|
|
303
|
-
error("[bake] view: …,");
|
|
304
|
-
error("[bake] bake: { paths: () => [{}], data: () => ({}) },");
|
|
305
|
-
error("[bake] });");
|
|
306
|
-
error(
|
|
307
|
-
"[bake] Without bake the build ships only the SPA shell — search engines",
|
|
308
|
-
);
|
|
309
|
-
error("[bake] and link previews see an empty <body>.");
|
|
310
|
-
// Exit non-zero so `mado release` halts and the user is forced to address
|
|
311
|
-
// it. If you intentionally have an SPA-only deploy, drop `mado bake` from
|
|
312
|
-
// the release pipeline (or set MADO_BAKE_ALLOW_EMPTY=1).
|
|
313
|
-
if (process.env.MADO_BAKE_ALLOW_EMPTY !== "1") {
|
|
314
|
-
await viteServer.close();
|
|
315
|
-
process.exit(1);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
await viteServer.close();
|
|
320
|
-
|
|
321
|
-
// ---------- Helpers ----------
|
|
322
|
-
|
|
323
|
-
async function resolvePage(entry) {
|
|
324
|
-
if (entry && entry._page === true) return entry;
|
|
325
|
-
if (typeof entry === "function") {
|
|
326
|
-
try {
|
|
327
|
-
const mod = await entry();
|
|
328
|
-
return mod?.default;
|
|
329
|
-
} catch (e) {
|
|
330
|
-
console.warn("[bake] failed to load route:", e.message);
|
|
331
|
-
return null;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
return null;
|
|
335
|
-
}
|
|
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
|
-
|
|
352
|
-
function applyParams(pattern, params) {
|
|
353
|
-
return pattern.replace(/:([\w]+)/g, (_, k) => {
|
|
354
|
-
const v = params[k];
|
|
355
|
-
if (v == null) throw new Error(`[bake] missing param :${k} for ${pattern}`);
|
|
356
|
-
return encodeURIComponent(String(v));
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// ---------- Render TemplateResult → HTML string ----------
|
|
361
|
-
//
|
|
362
|
-
// Tiny SSR for TemplateResult. Supports the same shapes as html.ts but
|
|
363
|
-
// without events (@click is stripped) and without live signals (function
|
|
364
|
-
// values are called once).
|
|
365
|
-
|
|
366
|
-
function renderTemplate(tpl, ctx) {
|
|
367
|
-
if (tpl == null || tpl === false || tpl === true) return "";
|
|
368
|
-
if (typeof tpl === "string") return escapeHtml(tpl);
|
|
369
|
-
if (typeof tpl === "number") return String(tpl);
|
|
370
|
-
if (Array.isArray(tpl)) return tpl.map((x) => renderTemplate(x, ctx)).join("");
|
|
371
|
-
if (tpl && tpl._mado === true) return renderMadoTemplate(tpl, ctx);
|
|
372
|
-
// Unknown shapes (e.g. each() directive results) must NOT silently render
|
|
373
|
-
// as "[object Object]". Either each() unwraps to an array here, or we
|
|
374
|
-
// throw with a meaningful location.
|
|
375
|
-
if (tpl && typeof tpl === "object") {
|
|
376
|
-
throw new Error(
|
|
377
|
-
`bake cannot render value of type "${tpl?._type ?? tpl?.constructor?.name ?? "object"}" ` +
|
|
378
|
-
`in route ${ctx?.route ?? "?"}. ` +
|
|
379
|
-
"Hint: each() and other directives are not yet supported in bake. " +
|
|
380
|
-
"Use a plain array (items.map(render)) in baked views, or render this " +
|
|
381
|
-
"section only on the client.",
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
return "";
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
function renderMadoTemplate(tpl, ctx) {
|
|
388
|
-
const { strings, values } = tpl;
|
|
389
|
-
let html = "";
|
|
390
|
-
for (let i = 0; i < strings.length; i++) {
|
|
391
|
-
html += strings[i];
|
|
392
|
-
if (i < strings.length - 1) {
|
|
393
|
-
html += renderValue(values[i], inAttributeContext(html), ctx);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
// Strip event markers and normalize property / boolean-attribute forms
|
|
397
|
-
// (mirrors the runtime bindings in src/html/bindings.ts).
|
|
398
|
-
return html
|
|
399
|
-
.replace(/\s+@[\w-]+="[^"]*"/g, "")
|
|
400
|
-
.replace(/\s+\.([\w-]+)="([^"]*)"/g, ' $1="$2"')
|
|
401
|
-
.replace(/\s+\?([\w-]+)="(true|on|1)"/g, ' $1=""')
|
|
402
|
-
.replace(/\s+\?[\w-]+="(false|off|0|)"/g, "");
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function inAttributeContext(html) {
|
|
406
|
-
const lastOpen = html.lastIndexOf("<");
|
|
407
|
-
const lastClose = html.lastIndexOf(">");
|
|
408
|
-
return lastOpen > lastClose;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
function renderValue(v, inAttr, ctx) {
|
|
412
|
-
if (v == null || v === false) return "";
|
|
413
|
-
if (v === true) return "";
|
|
414
|
-
if (typeof v === "function") {
|
|
415
|
-
try {
|
|
416
|
-
return renderValue(v(), inAttr, ctx);
|
|
417
|
-
} catch {
|
|
418
|
-
return "";
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
if (Array.isArray(v)) return v.map((x) => renderValue(x, inAttr, ctx)).join("");
|
|
422
|
-
if (v && v._mado === true) return renderMadoTemplate(v, ctx);
|
|
423
|
-
if (v && typeof v === "object" && typeof v._madoDirective === "string") {
|
|
424
|
-
return renderDirective(v, inAttr, ctx);
|
|
425
|
-
}
|
|
426
|
-
if (v && typeof v === "object") {
|
|
427
|
-
// Same defense as renderTemplate(): never silently coerce to "[object Object]".
|
|
428
|
-
throw new Error(
|
|
429
|
-
`bake cannot serialize value of type "${v?._type ?? v?.constructor?.name ?? "object"}" ` +
|
|
430
|
-
`in route ${ctx?.route ?? "?"}. ` +
|
|
431
|
-
"Hint: each() is not yet supported in bake. Use a plain array in baked views.",
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
if (inAttr) return escapeAttr(String(v));
|
|
435
|
-
return escapeHtml(String(v));
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
function renderDirective(v, inAttr, ctx) {
|
|
439
|
-
switch (v._madoDirective) {
|
|
440
|
-
case "unsafeHTML": {
|
|
441
|
-
if (inAttr) {
|
|
442
|
-
throw new Error(
|
|
443
|
-
`bake cannot render unsafeHTML() inside an attribute in route ${ctx?.route ?? "?"}.`,
|
|
444
|
-
);
|
|
445
|
-
}
|
|
446
|
-
return String(v.value ?? "");
|
|
447
|
-
}
|
|
448
|
-
case "classMap": {
|
|
449
|
-
const value = Object.entries(v.value ?? {})
|
|
450
|
-
.filter(([, enabled]) => !!enabled)
|
|
451
|
-
.map(([className]) => className)
|
|
452
|
-
.join(" ");
|
|
453
|
-
return inAttr ? escapeAttr(value) : escapeHtml(value);
|
|
454
|
-
}
|
|
455
|
-
case "styleMap": {
|
|
456
|
-
const value = Object.entries(v.value ?? {})
|
|
457
|
-
.filter(([, raw]) => raw != null && raw !== false)
|
|
458
|
-
.map(([name, raw]) => `${toCssPropertyName(name)}:${String(raw)}`)
|
|
459
|
-
.join(";");
|
|
460
|
-
return inAttr ? escapeAttr(value) : escapeHtml(value);
|
|
461
|
-
}
|
|
462
|
-
case "ref":
|
|
463
|
-
return "";
|
|
464
|
-
default:
|
|
465
|
-
throw new Error(
|
|
466
|
-
`bake cannot render directive "${v._madoDirective}" in route ${ctx?.route ?? "?"}.`,
|
|
467
|
-
);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
function toCssPropertyName(name) {
|
|
472
|
-
if (name.startsWith("--")) return name;
|
|
473
|
-
return name.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function escapeHtml(s) {
|
|
477
|
-
return s
|
|
478
|
-
.replace(/&/g, "&")
|
|
479
|
-
.replace(/</g, "<")
|
|
480
|
-
.replace(/>/g, ">");
|
|
481
|
-
}
|
|
482
|
-
function escapeAttr(s) {
|
|
483
|
-
return escapeHtml(s).replace(/"/g, """);
|
|
484
|
-
}
|
|
485
|
-
function escapeXml(s) {
|
|
486
|
-
return s
|
|
487
|
-
.replace(/&/g, "&")
|
|
488
|
-
.replace(/</g, "<")
|
|
489
|
-
.replace(/>/g, ">")
|
|
490
|
-
.replace(/"/g, """);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// ---------- Final HTML with head + baked data ----------
|
|
494
|
-
|
|
495
|
-
function buildHtml({ template, bodyHtml, head, bakedData, revalidate, canonical }) {
|
|
496
|
-
const { document } = parseHTML(template);
|
|
497
|
-
|
|
498
|
-
if (head.title) document.title = head.title;
|
|
499
|
-
if (head.description) {
|
|
500
|
-
setMeta(document, { name: "description", content: head.description });
|
|
501
|
-
}
|
|
502
|
-
if (canonical) {
|
|
503
|
-
setLink(document, { rel: "canonical", href: canonical });
|
|
504
|
-
}
|
|
505
|
-
if (head.og) {
|
|
506
|
-
for (const [k, v] of Object.entries(head.og)) {
|
|
507
|
-
if (!v) continue;
|
|
508
|
-
setMeta(document, { property: `og:${k}`, content: String(v) });
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
if (head.twitter || head.og) {
|
|
512
|
-
const tw = head.twitter ?? {};
|
|
513
|
-
const og = head.og ?? {};
|
|
514
|
-
setMeta(document, { name: "twitter:card", content: tw.card ?? "summary" });
|
|
515
|
-
if (tw.title ?? og.title)
|
|
516
|
-
setMeta(document, { name: "twitter:title", content: tw.title ?? og.title });
|
|
517
|
-
if (tw.description ?? og.description)
|
|
518
|
-
setMeta(document, { name: "twitter:description", content: tw.description ?? og.description });
|
|
519
|
-
if (tw.image ?? og.image)
|
|
520
|
-
setMeta(document, { name: "twitter:image", content: tw.image ?? og.image });
|
|
521
|
-
}
|
|
522
|
-
for (const m of head.meta ?? []) setMeta(document, m);
|
|
523
|
-
for (const l of head.link ?? []) setLink(document, l);
|
|
524
|
-
|
|
525
|
-
if (head.jsonLd != null) {
|
|
526
|
-
const s = document.createElement("script");
|
|
527
|
-
s.setAttribute("type", "application/ld+json");
|
|
528
|
-
s.setAttribute("data-mado-head", "baked");
|
|
529
|
-
s.textContent = JSON.stringify(head.jsonLd);
|
|
530
|
-
document.head.appendChild(s);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
if (revalidate) {
|
|
534
|
-
setMeta(document, { name: "bake-revalidate", content: String(revalidate) });
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
if (bakedData !== undefined) {
|
|
538
|
-
const s = document.createElement("script");
|
|
539
|
-
s.setAttribute("type", "application/json");
|
|
540
|
-
s.id = "bake";
|
|
541
|
-
s.textContent = JSON.stringify(bakedData);
|
|
542
|
-
document.body.appendChild(s);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
const app = document.getElementById("app");
|
|
546
|
-
if (app) {
|
|
547
|
-
app.setAttribute("data-mado-baked", "");
|
|
548
|
-
app.innerHTML = bodyHtml;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
return "<!doctype html>\n" + document.documentElement.outerHTML;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
function setMeta(doc, attrs) {
|
|
555
|
-
const m = doc.createElement("meta");
|
|
556
|
-
if (attrs.name) m.setAttribute("name", attrs.name);
|
|
557
|
-
if (attrs.property) m.setAttribute("property", attrs.property);
|
|
558
|
-
m.setAttribute("content", attrs.content);
|
|
559
|
-
m.setAttribute("data-mado-head", "baked");
|
|
560
|
-
doc.head.appendChild(m);
|
|
561
|
-
}
|
|
562
|
-
function setLink(doc, attrs) {
|
|
563
|
-
const l = doc.createElement("link");
|
|
564
|
-
l.setAttribute("rel", attrs.rel);
|
|
565
|
-
l.setAttribute("href", attrs.href);
|
|
566
|
-
if (attrs.hreflang) l.setAttribute("hreflang", attrs.hreflang);
|
|
567
|
-
l.setAttribute("data-mado-head", "baked");
|
|
568
|
-
doc.head.appendChild(l);
|
|
569
|
-
}
|
|
3
|
+
console.error("[mado] `mado bake` was removed.");
|
|
4
|
+
console.error("Use `mado static`, or run the complete pipeline with `mado release`.");
|
|
5
|
+
process.exit(1);
|
package/scripts/cli/generate.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import { access, mkdir, writeFile } from "node:fs/promises";
|
|
2
3
|
import { dirname, join, relative } from "node:path";
|
|
3
4
|
|
|
@@ -13,6 +14,35 @@ const KINDS = [
|
|
|
13
14
|
"layout",
|
|
14
15
|
];
|
|
15
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Generators support two starter shapes:
|
|
19
|
+
*
|
|
20
|
+
* modular — src/modules/<name>/<...> (default behaviour)
|
|
21
|
+
* universal— src/pages, src/components, src/content (the default starter)
|
|
22
|
+
*
|
|
23
|
+
* Detection is structural: a project with `src/modules/` is treated as
|
|
24
|
+
* modular; otherwise the universal layout is assumed. This lets users of
|
|
25
|
+
* either starter run `mado new` without configuring anything, and keeps
|
|
26
|
+
* the modular contract intact for projects that opt into it.
|
|
27
|
+
*/
|
|
28
|
+
function detectStarterShape(ctx) {
|
|
29
|
+
if (existsSync(join(ctx.projectRoot, "src/modules"))) return "modular";
|
|
30
|
+
if (existsSync(join(ctx.projectRoot, "src/pages"))) return "universal";
|
|
31
|
+
// No explicit hint — fall back to modular, which matches the
|
|
32
|
+
// historical behaviour and is documented in the generator help.
|
|
33
|
+
return "modular";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Generators that only make sense inside the modular starter. */
|
|
37
|
+
const MODULAR_ONLY = new Set([
|
|
38
|
+
"module",
|
|
39
|
+
"connector",
|
|
40
|
+
"resource",
|
|
41
|
+
"service",
|
|
42
|
+
"form",
|
|
43
|
+
"guard",
|
|
44
|
+
]);
|
|
45
|
+
|
|
16
46
|
export async function runNew(ctx, args) {
|
|
17
47
|
const [kind, target] = args;
|
|
18
48
|
if (!kind || !target) {
|
|
@@ -20,6 +50,20 @@ export async function runNew(ctx, args) {
|
|
|
20
50
|
process.exit(1);
|
|
21
51
|
}
|
|
22
52
|
|
|
53
|
+
const shape = detectStarterShape(ctx);
|
|
54
|
+
if (shape === "universal" && MODULAR_ONLY.has(kind)) {
|
|
55
|
+
console.error(
|
|
56
|
+
`[mado] '${kind}' is a modular-starter generator and is not ` +
|
|
57
|
+
"available in the universal starter.",
|
|
58
|
+
);
|
|
59
|
+
console.error(
|
|
60
|
+
"[mado] Either move this project to the modular layout " +
|
|
61
|
+
"(create src/modules/<name>/) or scaffold a fresh modular " +
|
|
62
|
+
"project with: mado init my-app --starter modular",
|
|
63
|
+
);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
23
67
|
const generators = {
|
|
24
68
|
module: scaffoldModule,
|
|
25
69
|
page: scaffoldPage,
|
|
@@ -39,7 +83,7 @@ export async function runNew(ctx, args) {
|
|
|
39
83
|
process.exit(1);
|
|
40
84
|
}
|
|
41
85
|
|
|
42
|
-
await fn(ctx, normalizeTarget(target));
|
|
86
|
+
await fn(ctx, normalizeTarget(target), shape);
|
|
43
87
|
}
|
|
44
88
|
|
|
45
89
|
function printUsage() {
|
|
@@ -86,8 +130,11 @@ async function scaffoldModule(ctx, name) {
|
|
|
86
130
|
);
|
|
87
131
|
}
|
|
88
132
|
|
|
89
|
-
async function scaffoldPage(ctx, target) {
|
|
90
|
-
const file =
|
|
133
|
+
async function scaffoldPage(ctx, target, shape) {
|
|
134
|
+
const file =
|
|
135
|
+
shape === "universal"
|
|
136
|
+
? join(srcDir(ctx), "pages", `${leafName(target)}.page.ts`)
|
|
137
|
+
: moduleFile(ctx, target, ".page.ts");
|
|
91
138
|
const name = leafName(target);
|
|
92
139
|
await writeOnce(
|
|
93
140
|
file,
|
|
@@ -225,8 +272,11 @@ export const ${useName} = () =>
|
|
|
225
272
|
);
|
|
226
273
|
}
|
|
227
274
|
|
|
228
|
-
async function scaffoldComponent(ctx, target) {
|
|
229
|
-
const file =
|
|
275
|
+
async function scaffoldComponent(ctx, target, shape) {
|
|
276
|
+
const file =
|
|
277
|
+
shape === "universal"
|
|
278
|
+
? join(srcDir(ctx), "components", `${leafName(target)}.component.ts`)
|
|
279
|
+
: moduleFile(ctx, target, ".component.ts");
|
|
230
280
|
const name = leafName(target);
|
|
231
281
|
const tag = name.includes("-") ? name : `x-${name}`;
|
|
232
282
|
await writeOnce(
|
package/scripts/cli/help.mjs
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
export function printHelp(ctx) {
|
|
2
2
|
const mode = ctx.isRepo ? "repo-mode (framework repository)" : "app-mode";
|
|
3
|
+
const buildLine = ctx.isRepo
|
|
4
|
+
? " mado build tsc package compile (writes internal dist/)"
|
|
5
|
+
: " mado build Vite production SPA build -> out/";
|
|
3
6
|
console.log(`mado commands (${mode}):
|
|
4
7
|
|
|
5
8
|
Project lifecycle:
|
|
6
9
|
mado init <name> [--starter default] [--force]
|
|
7
10
|
scaffold a new app
|
|
8
11
|
mado dev Vite dev server
|
|
9
|
-
|
|
12
|
+
${buildLine}
|
|
10
13
|
mado typecheck tsc --noEmit
|
|
11
14
|
mado test run unit tests
|
|
12
15
|
|
|
13
16
|
Production:
|
|
14
|
-
mado
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
mado static [--entry <file>] [--out <dir>] [--base-url <url>]
|
|
18
|
+
[--browser-channel chrome | --browser-path <path>]
|
|
19
|
+
browser-snapshot static routes -> out/
|
|
20
|
+
(low-level; prefer \`mado release\`)
|
|
21
|
+
mado release typecheck + vite build + static snapshots
|
|
22
|
+
+ deployment files -> out/
|
|
23
|
+
mado preview serve exactly out/ locally (production-like)
|
|
18
24
|
|
|
19
25
|
Generators:
|
|
20
26
|
mado new <module|page|connector|resource|service|form|component|guard|layout> <path>
|