@madojs/mado 0.5.1 → 0.6.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.
Files changed (107) hide show
  1. package/AGENTS.md +26 -0
  2. package/CHANGELOG.md +265 -0
  3. package/MADO_V1_PLAN.md +179 -0
  4. package/README.md +31 -13
  5. package/ROADMAP.md +28 -7
  6. package/TODO.md +72 -0
  7. package/dist/src/forms.d.ts +37 -4
  8. package/dist/src/forms.js +331 -57
  9. package/dist/src/forms.js.map +1 -1
  10. package/dist/src/html/bindings.d.ts +41 -0
  11. package/dist/src/html/bindings.js +163 -6
  12. package/dist/src/html/bindings.js.map +1 -1
  13. package/dist/src/html.d.ts +2 -0
  14. package/dist/src/html.js +1 -0
  15. package/dist/src/html.js.map +1 -1
  16. package/dist/src/index.d.ts +6 -6
  17. package/dist/src/index.js +2 -2
  18. package/dist/src/index.js.map +1 -1
  19. package/dist/src/page.d.ts +56 -0
  20. package/dist/src/page.js +17 -0
  21. package/dist/src/page.js.map +1 -1
  22. package/dist/src/resource.js +11 -0
  23. package/dist/src/resource.js.map +1 -1
  24. package/dist/src/router/manifest.d.ts +16 -1
  25. package/dist/src/router/manifest.js +210 -40
  26. package/dist/src/router/manifest.js.map +1 -1
  27. package/dist/src/router/match.d.ts +7 -2
  28. package/dist/src/router/match.js +14 -4
  29. package/dist/src/router/match.js.map +1 -1
  30. package/dist/src/router/navigation.d.ts +10 -0
  31. package/dist/src/router/navigation.js +71 -3
  32. package/dist/src/router/navigation.js.map +1 -1
  33. package/dist/src/signal.d.ts +15 -1
  34. package/dist/src/signal.js +112 -16
  35. package/dist/src/signal.js.map +1 -1
  36. package/docs/en/02-project-layout.md +99 -40
  37. package/docs/en/10-app-architecture.md +141 -0
  38. package/docs/en/11-layouts.md +115 -0
  39. package/docs/en/12-auth-and-api.md +217 -0
  40. package/docs/en/13-deployment.md +192 -0
  41. package/docs/en/14-testing.md +82 -0
  42. package/docs/en/15-error-handling.md +100 -0
  43. package/docs/en/16-bake-cookbook.md +93 -0
  44. package/docs/en/README.md +7 -0
  45. package/docs/fr/10-app-architecture.md +61 -0
  46. package/docs/fr/11-layouts.md +35 -0
  47. package/docs/fr/12-auth-and-api.md +35 -0
  48. package/docs/fr/13-deployment.md +39 -0
  49. package/docs/fr/14-testing.md +41 -0
  50. package/docs/fr/15-error-handling.md +50 -0
  51. package/docs/fr/16-bake-cookbook.md +35 -0
  52. package/docs/fr/README.md +7 -0
  53. package/docs/ru/10-app-architecture.md +100 -0
  54. package/docs/ru/11-layouts.md +47 -0
  55. package/docs/ru/12-auth-and-api.md +53 -0
  56. package/docs/ru/13-deployment.md +60 -0
  57. package/docs/ru/14-testing.md +50 -0
  58. package/docs/ru/15-error-handling.md +56 -0
  59. package/docs/ru/16-bake-cookbook.md +55 -0
  60. package/docs/ru/README.md +7 -0
  61. package/docs/uk/10-app-architecture.md +56 -0
  62. package/docs/uk/11-layouts.md +34 -0
  63. package/docs/uk/12-auth-and-api.md +34 -0
  64. package/docs/uk/13-deployment.md +39 -0
  65. package/docs/uk/14-testing.md +34 -0
  66. package/docs/uk/15-error-handling.md +32 -0
  67. package/docs/uk/16-bake-cookbook.md +36 -0
  68. package/docs/uk/README.md +7 -0
  69. package/llms.txt +9 -1
  70. package/package.json +3 -1
  71. package/scripts/_config.mjs +224 -0
  72. package/scripts/bake.mjs +266 -121
  73. package/scripts/bundle.mjs +133 -67
  74. package/scripts/cli.mjs +195 -27
  75. package/scripts/preview.mjs +125 -21
  76. package/server/serve.mjs +161 -10
  77. package/starters/admin/README.md +63 -0
  78. package/starters/admin/index.html +28 -0
  79. package/starters/admin/mado.config.json +22 -0
  80. package/starters/admin/package.json +24 -0
  81. package/starters/admin/public/favicon.svg +4 -0
  82. package/starters/admin/src/components/x-button.ts +55 -0
  83. package/starters/admin/src/components/x-input.ts +74 -0
  84. package/starters/admin/src/layouts/app.ts +101 -0
  85. package/starters/admin/src/layouts/auth.ts +41 -0
  86. package/starters/admin/src/lib/api.ts +133 -0
  87. package/starters/admin/src/lib/auth.ts +83 -0
  88. package/starters/admin/src/main.ts +15 -0
  89. package/starters/admin/src/pages/admin/dashboard.ts +48 -0
  90. package/starters/admin/src/pages/admin/order-detail.ts +80 -0
  91. package/starters/admin/src/pages/admin/orders.ts +117 -0
  92. package/starters/admin/src/pages/home.ts +34 -0
  93. package/starters/admin/src/pages/login.ts +70 -0
  94. package/starters/admin/src/pages/not-found.ts +12 -0
  95. package/starters/admin/src/routes.ts +40 -0
  96. package/starters/admin/src/styles/global.ts +86 -0
  97. package/starters/admin/tsconfig.json +15 -0
  98. package/starters/crud/index.html +12 -4
  99. package/starters/crud/mado.config.json +20 -0
  100. package/starters/crud/package.json +9 -3
  101. package/starters/crud/src/pages/home.ts +16 -0
  102. package/starters/crud/src/routes.ts +4 -2
  103. package/starters/minimal/index.html +12 -4
  104. package/starters/minimal/mado.config.json +20 -0
  105. package/starters/minimal/package.json +9 -3
  106. package/starters/minimal/src/pages/home.ts +17 -0
  107. package/starters/minimal/src/routes.ts +4 -2
@@ -0,0 +1,224 @@
1
+ // Mado configuration loader.
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/
18
+
19
+ import { existsSync, readFileSync } from "node:fs";
20
+ import { dirname, join, resolve } from "node:path";
21
+ import { fileURLToPath } from "node:url";
22
+
23
+ const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
24
+
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
+ /**
55
+ * Detect whether we are inside the framework repository or inside a user app.
56
+ *
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
+ * @param {string} projectRoot
61
+ * @returns {"app"|"repo"}
62
+ */
63
+ export function detectContext(projectRoot) {
64
+ const looksLikeRepo =
65
+ existsSync(join(projectRoot, "src/index.ts")) &&
66
+ existsSync(join(projectRoot, "examples")) &&
67
+ existsSync(join(projectRoot, "package.json")) &&
68
+ safeReadJson(join(projectRoot, "package.json"))?.name === "@madojs/mado";
69
+ return looksLikeRepo ? "repo" : "app";
70
+ }
71
+
72
+ /**
73
+ * Built-in defaults per context.
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.
111
+ *
112
+ * @param {Object} [opts]
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
139
+ * @param {string} p
140
+ * @returns {string}
141
+ */
142
+ export function resolveProjectPath(cfg, p) {
143
+ if (!p) return cfg.projectRoot;
144
+ return resolve(cfg.projectRoot, p);
145
+ }
146
+
147
+ /**
148
+ * @param {MadoConfig} cfg
149
+ * @returns {string}
150
+ */
151
+ export function getPackageRoot() {
152
+ return PACKAGE_ROOT;
153
+ }
154
+
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
+ /**
191
+ * Tiny argv parser shared by CLI subcommands.
192
+ *
193
+ * Recognises `--key=value`, `--key value`, and `--flag` (boolean).
194
+ * Unknown leading positionals are returned in `positional`.
195
+ *
196
+ * @param {string[]} argv
197
+ * @returns {{ flags: Record<string, string|boolean>, positional: string[] }}
198
+ */
199
+ export function parseFlags(argv) {
200
+ const flags = {};
201
+ const positional = [];
202
+ for (let i = 0; i < argv.length; i++) {
203
+ const a = argv[i];
204
+ if (a === "--") continue;
205
+ if (a.startsWith("--")) {
206
+ const eq = a.indexOf("=");
207
+ if (eq > -1) {
208
+ flags[a.slice(2, eq)] = a.slice(eq + 1);
209
+ } else {
210
+ const name = a.slice(2);
211
+ const next = argv[i + 1];
212
+ if (next !== undefined && !next.startsWith("-")) {
213
+ flags[name] = next;
214
+ i++;
215
+ } else {
216
+ flags[name] = true;
217
+ }
218
+ }
219
+ } else {
220
+ positional.push(a);
221
+ }
222
+ }
223
+ return { flags, positional };
224
+ }