@withl5e/l5e 1.0.1 → 1.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2 -2
- package/dist/island/compact-runtime.js +81 -0
- package/dist/island/compact-runtime.js.map +1 -0
- package/dist/island/runtime.js +7 -3
- package/dist/island/runtime.js.map +1 -1
- package/dist/{server-DXNRPyGJ.js → server-BOdEiJoN.js} +213 -115
- package/dist/server-BOdEiJoN.js.map +1 -0
- package/dist/server.js +1 -1
- package/dist/vite-plugin-R3LFxv9a.js +497 -0
- package/dist/vite-plugin-R3LFxv9a.js.map +1 -0
- package/dist/vite-plugin.js +2 -333
- package/package.json +7 -1
- package/src/core/bundler.ts +77 -7
- package/src/core/chunking.ts +256 -0
- package/src/core/script-bundle-policy.ts +57 -2
- package/src/core/server.ts +109 -28
- package/src/core/vite-plugin.ts +93 -8
- package/src/island/compact-runtime.ts +121 -0
- package/src/island/runtime.ts +22 -8
- package/dist/server-DXNRPyGJ.js.map +0 -1
- package/dist/vite-plugin.js.map +0 -1
package/dist/vite-plugin.js
CHANGED
|
@@ -1,333 +1,2 @@
|
|
|
1
|
-
import { t as e } from "./
|
|
2
|
-
|
|
3
|
-
import { transform as c } from "esbuild";
|
|
4
|
-
import { dirname as l, join as u, relative as d, resolve as f } from "path";
|
|
5
|
-
//#region src/core/vite-plugin.ts
|
|
6
|
-
var p = "virtual:l5e-views", m = "virtual:l5e-route", h = "virtual:l5e-ssr-entry", g = "virtual:l5e-global-loader", _ = "virtual:l5e-island-strategies", v = "virtual:l5e-islands", y = "virtual:l5e-actions", b = "virtual:l5e-middleware", x = /* @__PURE__ */ new Set([
|
|
7
|
-
"MODE",
|
|
8
|
-
"BASE_URL",
|
|
9
|
-
"PROD",
|
|
10
|
-
"DEV",
|
|
11
|
-
"SSR",
|
|
12
|
-
"LEGACY"
|
|
13
|
-
]);
|
|
14
|
-
function S(e, t = []) {
|
|
15
|
-
let n = i(e);
|
|
16
|
-
for (let r of n) {
|
|
17
|
-
let n = u(e, r);
|
|
18
|
-
if (o(n).isDirectory()) {
|
|
19
|
-
if (r === "node_modules" || r === "dist" || r === ".git") continue;
|
|
20
|
-
S(n, t);
|
|
21
|
-
} else (r.endsWith(".tsx") || r.endsWith(".ts")) && t.push(n);
|
|
22
|
-
}
|
|
23
|
-
return t;
|
|
24
|
-
}
|
|
25
|
-
function C(e) {
|
|
26
|
-
let t = 0;
|
|
27
|
-
for (let n = 0; n < e.length; n++) t = (t << 5) - t + e.charCodeAt(n), t &= t;
|
|
28
|
-
return Math.abs(t).toString(16).slice(0, 4);
|
|
29
|
-
}
|
|
30
|
-
function w(e) {
|
|
31
|
-
let t = e.split("/"), n = t[t.length - 1];
|
|
32
|
-
return n = n.replace(/\.(tsx?|jsx?)$/, ""), n;
|
|
33
|
-
}
|
|
34
|
-
function T(e) {
|
|
35
|
-
return `${w(e)}_${C(e)}`;
|
|
36
|
-
}
|
|
37
|
-
function E(e, n) {
|
|
38
|
-
let r = e.replace(/^\//, ""), i = f(n, r);
|
|
39
|
-
for (let e of [
|
|
40
|
-
".tsx",
|
|
41
|
-
".ts",
|
|
42
|
-
".jsx",
|
|
43
|
-
".js"
|
|
44
|
-
]) if (t(i + e)) return r + e;
|
|
45
|
-
return t(i) ? r : null;
|
|
46
|
-
}
|
|
47
|
-
function D(e, t) {
|
|
48
|
-
let n = r(e, "utf-8"), i = [], a = /<ClientIsland\s[^>]*?from\s*=\s*"([^"]+)"/g, o = /* @__PURE__ */ new Set(), s;
|
|
49
|
-
for (; (s = a.exec(n)) !== null;) {
|
|
50
|
-
let n = s[1], r;
|
|
51
|
-
if (n.startsWith("~/")) r = "/src/" + n.substring(2);
|
|
52
|
-
else if (n.startsWith("/src/")) r = n;
|
|
53
|
-
else if (n.startsWith("./") || n.startsWith("../")) {
|
|
54
|
-
let i = f(e, "..", n);
|
|
55
|
-
r = "/" + d(t, i).replace(/\\/g, "/");
|
|
56
|
-
} else r = n;
|
|
57
|
-
let a = E(r, t);
|
|
58
|
-
if (!a) {
|
|
59
|
-
console.warn(`[l5e] Island component not found: ${r} (from ${n} in ${e})`);
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
let c = T(r);
|
|
63
|
-
o.has(c) || (o.add(c), i.push({
|
|
64
|
-
component: w(r),
|
|
65
|
-
resolvedPath: r,
|
|
66
|
-
src: a,
|
|
67
|
-
key: c
|
|
68
|
-
}));
|
|
69
|
-
}
|
|
70
|
-
return i;
|
|
71
|
-
}
|
|
72
|
-
function O(e, t) {
|
|
73
|
-
let n = r(e, "utf-8"), i = [], a = [], o = /useCss\s*\(\s*['"]([^'"]+)['"]\s*,?\s*\)/g, s = /useClientJs\s*\(\s*['"]([^'"]+)['"]\s*,?\s*\)/g, c;
|
|
74
|
-
for (; (c = o.exec(n)) !== null;) {
|
|
75
|
-
let n = c[1];
|
|
76
|
-
if (n.startsWith("/src/")) i.push(n);
|
|
77
|
-
else if (n.startsWith("./") || n.startsWith("../")) {
|
|
78
|
-
let r = f(e, "..", n), a = "/" + d(t, r).replace(/\\/g, "/");
|
|
79
|
-
i.push(a);
|
|
80
|
-
} else i.push(n);
|
|
81
|
-
}
|
|
82
|
-
for (; (c = s.exec(n)) !== null;) {
|
|
83
|
-
let n = c[1];
|
|
84
|
-
if (n.startsWith("/src/")) a.push(n);
|
|
85
|
-
else if (n.startsWith("./") || n.startsWith("../")) {
|
|
86
|
-
let r = f(e, "..", n), i = "/" + d(t, r).replace(/\\/g, "/");
|
|
87
|
-
a.push(i);
|
|
88
|
-
} else a.push(n);
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
css: i,
|
|
92
|
-
js: a
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function k(n) {
|
|
96
|
-
let i = {}, a = u(n, "src"), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Map(), m = /* @__PURE__ */ new Map();
|
|
97
|
-
try {
|
|
98
|
-
if (!o(a).isDirectory()) return {
|
|
99
|
-
input: i,
|
|
100
|
-
islandRegistry: s,
|
|
101
|
-
pathToKey: c,
|
|
102
|
-
keyToSrc: p,
|
|
103
|
-
actionRegistry: m
|
|
104
|
-
};
|
|
105
|
-
let h = u(n, "src", "client.global.ts");
|
|
106
|
-
t(h) && (i.global = h, console.log("[l5e] Detected src/client.global.ts and added as entry"));
|
|
107
|
-
let g = e(n);
|
|
108
|
-
g && (i["global-style"] = g, console.log("[l5e] Detected src/global.css and added as global stylesheet"));
|
|
109
|
-
let _ = S(a), v = /* @__PURE__ */ new Set(), y = /* @__PURE__ */ new Set();
|
|
110
|
-
for (let e of _) {
|
|
111
|
-
let { css: t, js: i } = O(e, n);
|
|
112
|
-
t.forEach((e) => v.add(e)), i.forEach((e) => y.add(e));
|
|
113
|
-
let o = D(e, n);
|
|
114
|
-
for (let e of o) s.set(e.key, e.resolvedPath), c.set(e.resolvedPath, e.key), p.set(e.key, e.src);
|
|
115
|
-
let u = e.replace(/\\/g, "/");
|
|
116
|
-
if (/\/actions\.(ts|tsx)$/.test(u)) {
|
|
117
|
-
let t = r(e, "utf-8"), n = /export\s+const\s+(\w+)\s*=\s*defineAction\s*\(/g, i, o = d(a, l(e)).replace(/\\/g, "/") || ".";
|
|
118
|
-
for (; (i = n.exec(t)) !== null;) {
|
|
119
|
-
let e = i[1], t = `${e}_${C(o)}`;
|
|
120
|
-
m.set(t, {
|
|
121
|
-
modulePath: o,
|
|
122
|
-
actionName: e
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
for (let e of v) {
|
|
128
|
-
let r = e.startsWith("/src/") || e.startsWith("/") ? e.substring(1) : e, a = f(n, r);
|
|
129
|
-
if (g && f(a) === f(g)) continue;
|
|
130
|
-
if (!t(a)) {
|
|
131
|
-
console.warn(`[l5e] CSS file not found: ${a} (from ${e})`);
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
let o = r.replace(/^src\//, "").replace(/\.css$/, "").replace(/\//g, "-").replace(/\\/g, "-");
|
|
135
|
-
i[o] = a;
|
|
136
|
-
}
|
|
137
|
-
for (let e of y) {
|
|
138
|
-
let r = e.startsWith("/src/") || e.startsWith("/") ? e.substring(1) : e, a = f(n, r);
|
|
139
|
-
if (!t(a)) {
|
|
140
|
-
console.warn(`[l5e] JS/TS file not found: ${a} (from ${e})`);
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
let o = r.replace(/^src\//, "").replace(/\.(ts|tsx|js|jsx)$/, "").replace(/\//g, "-").replace(/\\/g, "-");
|
|
144
|
-
i[o] = a;
|
|
145
|
-
}
|
|
146
|
-
for (let [e, t] of p) {
|
|
147
|
-
let r = f(n, t);
|
|
148
|
-
i[`island-${e}`] = r;
|
|
149
|
-
}
|
|
150
|
-
s.size > 0 && console.log(`[l5e] Detected ${s.size} island(s)`), m.size > 0 && console.log(`[l5e] Detected ${m.size} action(s)`);
|
|
151
|
-
} catch (e) {
|
|
152
|
-
console.warn("[l5e] Failed to discover bundler input:", e);
|
|
153
|
-
}
|
|
154
|
-
return {
|
|
155
|
-
input: i,
|
|
156
|
-
islandRegistry: s,
|
|
157
|
-
pathToKey: c,
|
|
158
|
-
keyToSrc: p,
|
|
159
|
-
actionRegistry: m
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
function A(e, t, n, r, i) {
|
|
163
|
-
let a = [], o = 0, s = /ClientIsland\s*,\s*\{/g, c;
|
|
164
|
-
for (; (c = s.exec(e)) !== null;) {
|
|
165
|
-
let s = c.index + c[0].length, l = e.substring(s, s + 500), u = /from:\s*"([^"]+)"/.exec(l);
|
|
166
|
-
if (!u) continue;
|
|
167
|
-
let p = u[1], m;
|
|
168
|
-
if (p.startsWith("~/")) m = "/src/" + p.substring(2);
|
|
169
|
-
else if (p.startsWith("/src/")) m = p;
|
|
170
|
-
else if (p.startsWith("./") || p.startsWith("../")) {
|
|
171
|
-
let e = f(t, "..", p);
|
|
172
|
-
m = "/" + d(n, e).replace(/\\/g, "/");
|
|
173
|
-
} else m = p;
|
|
174
|
-
let h = r.get(m);
|
|
175
|
-
if (!h) continue;
|
|
176
|
-
let g = i.get(h);
|
|
177
|
-
if (!g) continue;
|
|
178
|
-
let _ = s + u.index + u[0].length;
|
|
179
|
-
a.push(e.substring(o, _)), a.push(`, __key: "${h}", __src: "${g}"`), o = _;
|
|
180
|
-
}
|
|
181
|
-
return a.push(e.substring(o)), a.join("");
|
|
182
|
-
}
|
|
183
|
-
function j() {
|
|
184
|
-
let e = process.cwd(), r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map();
|
|
185
|
-
return {
|
|
186
|
-
name: "l5e-jsx-classic",
|
|
187
|
-
enforce: "pre",
|
|
188
|
-
configResolved(t) {
|
|
189
|
-
e = t.root || process.cwd();
|
|
190
|
-
},
|
|
191
|
-
handleHotUpdate({ file: t, server: n }) {
|
|
192
|
-
let r = d(e, t);
|
|
193
|
-
if (/actions\.(ts|tsx)$/.test(t)) {
|
|
194
|
-
o = k(e).actionRegistry;
|
|
195
|
-
let t = n.moduleGraph.getModuleById("\0virtual:l5e-actions");
|
|
196
|
-
t && n.moduleGraph.invalidateModule(t), console.log(`[l5e] Action file changed: ${r} — re-scanned ${o.size} action(s)`);
|
|
197
|
-
}
|
|
198
|
-
if (t.endsWith(".css")) {
|
|
199
|
-
console.log(`[l5e] CSS changed: ${r} - using Vite HMR`);
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
if (t.includes("client.ts")) {
|
|
203
|
-
console.log(`[l5e] Client JS changed: ${r} - using Vite HMR`);
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
if (t.includes("/src/") || t.includes("\\src\\")) return console.log(`[l5e] SSR file changed: ${r} - triggering full reload`), n.ws.send({
|
|
207
|
-
type: "full-reload",
|
|
208
|
-
path: "*"
|
|
209
|
-
}), [];
|
|
210
|
-
},
|
|
211
|
-
buildEnd() {
|
|
212
|
-
let n = u(e, ".l5e-temp");
|
|
213
|
-
if (t(n)) try {
|
|
214
|
-
a(n, {
|
|
215
|
-
recursive: !0,
|
|
216
|
-
force: !0
|
|
217
|
-
}), console.log("[l5e] Cleaned up temporary files");
|
|
218
|
-
} catch (e) {
|
|
219
|
-
console.warn("[l5e] Failed to cleanup temporary files:", e);
|
|
220
|
-
}
|
|
221
|
-
},
|
|
222
|
-
writeBundle(r, i) {
|
|
223
|
-
if (o.size > 0) {
|
|
224
|
-
let r = u(e, "dist", "server");
|
|
225
|
-
t(r) || n(r, { recursive: !0 });
|
|
226
|
-
let i = Object.fromEntries(o);
|
|
227
|
-
s(u(r, "action-registry.json"), JSON.stringify(i), "utf-8"), console.log(`[l5e] Wrote action-registry.json (${o.size} actions)`);
|
|
228
|
-
}
|
|
229
|
-
},
|
|
230
|
-
config(e) {
|
|
231
|
-
let t = k(e.root || process.cwd());
|
|
232
|
-
t.islandRegistry, r = t.pathToKey, i = t.keyToSrc, o = t.actionRegistry;
|
|
233
|
-
let n = e.build?.rolldownOptions?.input || {}, a = typeof n == "object" && !Array.isArray(n) ? {
|
|
234
|
-
...t.input,
|
|
235
|
-
...n
|
|
236
|
-
} : t.input;
|
|
237
|
-
return { build: { rolldownOptions: {
|
|
238
|
-
...e.build?.rolldownOptions,
|
|
239
|
-
input: Object.keys(a).length > 0 ? a : void 0,
|
|
240
|
-
preserveEntrySignatures: "exports-only"
|
|
241
|
-
} } };
|
|
242
|
-
},
|
|
243
|
-
resolveId(e) {
|
|
244
|
-
return e === p ? "\0virtual:l5e-views" : e === m ? "\0virtual:l5e-route" : e === h ? "\0virtual:l5e-ssr-entry" : e === g ? "\0virtual:l5e-global-loader" : e === _ ? "\0virtual:l5e-island-strategies" : e === v ? "\0virtual:l5e-islands" : e === y ? "\0virtual:l5e-actions" : e === b ? "\0virtual:l5e-middleware" : null;
|
|
245
|
-
},
|
|
246
|
-
async transform(t, n, a) {
|
|
247
|
-
if (n.replace(/\\/g, "/").endsWith("src/client.global.ts") && !a?.ssr) return {
|
|
248
|
-
code: `import '@withl5e/l5e/island/runtime';\n${t}`,
|
|
249
|
-
map: null
|
|
250
|
-
};
|
|
251
|
-
if (!a?.ssr) {
|
|
252
|
-
let e = n.replace(/\\/g, "/").match(/\/src\/(.+)\/actions\.(ts|tsx)$/);
|
|
253
|
-
if (e) {
|
|
254
|
-
let n = e[1], r = /export\s+const\s+(\w+)\s*=\s*defineAction\s*\(/g, i = [], a;
|
|
255
|
-
for (; (a = r.exec(t)) !== null;) {
|
|
256
|
-
let e = a[1], n = t.substring(a.index, a.index + 500).match(/method:\s*['"](\w+)['"]/), r = n ? n[1].toUpperCase() : "GET";
|
|
257
|
-
i.push({
|
|
258
|
-
name: e,
|
|
259
|
-
method: r
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
if (i.length > 0) return {
|
|
263
|
-
code: i.map(({ name: e, method: t }) => {
|
|
264
|
-
let r = `${e}_${C(n)}`;
|
|
265
|
-
return t === "GET" ? `export async function ${e}(params) {
|
|
266
|
-
const res = await fetch('/_l5e/action/${r}?' + new URLSearchParams(params));
|
|
267
|
-
if (!res.ok) throw Object.assign(new Error('HTTP ' + res.status), { status: res.status });
|
|
268
|
-
return res;
|
|
269
|
-
}` : `export async function ${e}(body) {
|
|
270
|
-
const res = await fetch('/_l5e/action/${r}', {
|
|
271
|
-
method: '${t}',
|
|
272
|
-
headers: { 'Content-Type': 'application/json' },
|
|
273
|
-
body: JSON.stringify(body),
|
|
274
|
-
});
|
|
275
|
-
if (!res.ok) throw Object.assign(new Error('HTTP ' + res.status), { status: res.status });
|
|
276
|
-
return res;
|
|
277
|
-
}`;
|
|
278
|
-
}).join("\n\n"),
|
|
279
|
-
map: null
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
if (n.includes("node_modules") || n.includes("/react/") || n.includes("\\react\\")) return null;
|
|
284
|
-
if (/\.(tsx|jsx)$/.test(n)) try {
|
|
285
|
-
let a = `import { Fragment as __Fragment, jsxFactory } from "@withl5e/l5e/jsx-runtime"\n${t}`, o = await c(a, {
|
|
286
|
-
loader: n.endsWith(".tsx") ? "tsx" : "jsx",
|
|
287
|
-
jsx: "transform",
|
|
288
|
-
jsxFactory: "jsxFactory",
|
|
289
|
-
jsxFragment: "__Fragment",
|
|
290
|
-
sourcemap: !0,
|
|
291
|
-
sourcefile: n,
|
|
292
|
-
target: "es2020"
|
|
293
|
-
}), s = o.code;
|
|
294
|
-
return s.includes("ClientIsland") && (s = A(s, n, e, r, i)), {
|
|
295
|
-
code: s,
|
|
296
|
-
map: o.map || null
|
|
297
|
-
};
|
|
298
|
-
} catch (e) {
|
|
299
|
-
throw console.error(`[l5e] Failed to transform JSX in ${n}:`, e), e;
|
|
300
|
-
}
|
|
301
|
-
if (a?.ssr) {
|
|
302
|
-
let e = t, n = !1;
|
|
303
|
-
if (e = e.replace(/import\.meta\.env\.([a-zA-Z_][a-zA-Z0-9_]*)/g, (e, t) => x.has(t) ? e : (n = !0, `process.env.${t}`)), e = e.replace(/import\.meta\.env\[(['"`])([^'"`]+)\1\]/g, (e, t, r) => x.has(r) ? e : (n = !0, `process.env[${t}${r}${t}]`)), n) return {
|
|
304
|
-
code: e,
|
|
305
|
-
map: null
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
return null;
|
|
309
|
-
},
|
|
310
|
-
load(n) {
|
|
311
|
-
if (n === "\0virtual:l5e-views") return "\nexport const viewLoaders = import.meta.glob('/src/views/*/loader.{ts,tsx}');\nexport const viewComponents = import.meta.glob('/src/views/*/index.tsx');\n";
|
|
312
|
-
if (n === "\0virtual:l5e-route") return "export default function routeHandler(requestInfo) {\n return import('/src/route.ts').then((mod) => mod.default(requestInfo));\n}";
|
|
313
|
-
if (n === "\0virtual:l5e-ssr-entry") return "export { render } from '@withl5e/l5e/entry-server';\nexport { viewActions } from 'virtual:l5e-actions';";
|
|
314
|
-
if (n === "\0virtual:l5e-global-loader") return "export const globalLoader = import.meta.glob('/src/global-loader.{ts,tsx}', { eager: false });";
|
|
315
|
-
if (n === "\0virtual:l5e-actions") {
|
|
316
|
-
let e = Object.fromEntries(o);
|
|
317
|
-
return `export const viewActions = import.meta.glob('/src/**/actions.{ts,tsx}');
|
|
318
|
-
export const actionRegistry = ${JSON.stringify(e)};`;
|
|
319
|
-
}
|
|
320
|
-
if (n === "\0virtual:l5e-middleware") return "\nconst middlewareModules = import.meta.glob([\n '/src/middleware.{ts,tsx,js,jsx}',\n '/src/middleware/index.{ts,tsx,js,jsx}',\n]);\n\nconst middlewarePaths = [\n '/src/middleware.ts',\n '/src/middleware.tsx',\n '/src/middleware.js',\n '/src/middleware.jsx',\n '/src/middleware/index.ts',\n '/src/middleware/index.tsx',\n '/src/middleware/index.js',\n '/src/middleware/index.jsx',\n];\n\nexport async function loadMiddleware() {\n const middlewarePath = middlewarePaths.find((path) => middlewareModules[path]);\n if (!middlewarePath) return undefined;\n\n const mod = await middlewareModules[middlewarePath]();\n return mod.onRequest;\n}\n";
|
|
321
|
-
if (n === "\0virtual:l5e-islands") return "export const islandModules = import.meta.glob('/src/**/react/*.{tsx,jsx}');";
|
|
322
|
-
if (n === "\0virtual:l5e-island-strategies") {
|
|
323
|
-
let n = u(e, "src", "island-strategies.ts");
|
|
324
|
-
return t(n) ? "import '/src/island-strategies.ts';" : "/* no custom island strategies */";
|
|
325
|
-
}
|
|
326
|
-
return null;
|
|
327
|
-
}
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
//#endregion
|
|
331
|
-
export { j as coreVite, j as default };
|
|
332
|
-
|
|
333
|
-
//# sourceMappingURL=vite-plugin.js.map
|
|
1
|
+
import { t as e } from "./vite-plugin-R3LFxv9a.js";
|
|
2
|
+
export { e as coreVite, e as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@withl5e/l5e",
|
|
3
|
-
"version": "1.0.1",
|
|
3
|
+
"version": "1.1.0-alpha.1",
|
|
4
4
|
"description": "HTML-first SSR MPA framework with loaders, middleware, islands, actions, swap, SEO and cache controls.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
"sideEffects": [
|
|
17
17
|
"**/island/runtime.ts",
|
|
18
18
|
"**/island/runtime.js",
|
|
19
|
+
"**/island/compact-runtime.ts",
|
|
20
|
+
"**/island/compact-runtime.js",
|
|
19
21
|
"**/*.css"
|
|
20
22
|
],
|
|
21
23
|
"exports": {
|
|
@@ -79,6 +81,10 @@
|
|
|
79
81
|
"types": "./src/island/runtime.ts",
|
|
80
82
|
"default": "./dist/island/runtime.js"
|
|
81
83
|
},
|
|
84
|
+
"./island/compact-runtime": {
|
|
85
|
+
"types": "./src/island/compact-runtime.ts",
|
|
86
|
+
"default": "./dist/island/compact-runtime.js"
|
|
87
|
+
},
|
|
82
88
|
"./router": {
|
|
83
89
|
"types": "./src/router/index.ts",
|
|
84
90
|
"default": "./dist/router.js"
|
package/src/core/bundler.ts
CHANGED
|
@@ -43,6 +43,16 @@ interface BundleResult {
|
|
|
43
43
|
content: string;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
export interface ScriptBundleOptions {
|
|
47
|
+
compact?: boolean;
|
|
48
|
+
islands?: Array<{ key: string; script: string }>;
|
|
49
|
+
renderer?: string;
|
|
50
|
+
name?: 'global' | 'bundle' | 'shared';
|
|
51
|
+
sharedUrl?: string;
|
|
52
|
+
globalUrl?: string;
|
|
53
|
+
exportScripts?: string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
46
56
|
const EMPTY_RESULT: BundleResult = { hash: '', filename: '', content: '' };
|
|
47
57
|
|
|
48
58
|
// Memory map để lưu bundled files
|
|
@@ -120,22 +130,78 @@ function preserveBuildChunksPlugin(policy: ScriptBundlePolicy): Plugin {
|
|
|
120
130
|
};
|
|
121
131
|
}
|
|
122
132
|
|
|
133
|
+
function compactBuildPlugin(
|
|
134
|
+
policy: ScriptBundlePolicy,
|
|
135
|
+
sharedUrl?: string,
|
|
136
|
+
globalUrl?: string,
|
|
137
|
+
): Plugin {
|
|
138
|
+
return {
|
|
139
|
+
name: 'compact-build-graph',
|
|
140
|
+
resolveId(source, importer) {
|
|
141
|
+
if (!importer || source === VIRTUAL_ENTRY_ID) return null;
|
|
142
|
+
const file = path.isAbsolute(source)
|
|
143
|
+
? path.normalize(source)
|
|
144
|
+
: source.startsWith('/')
|
|
145
|
+
? policy.fileForAssetUrl(source)
|
|
146
|
+
: source.startsWith('.')
|
|
147
|
+
? path.resolve(path.dirname(importer), source)
|
|
148
|
+
: null;
|
|
149
|
+
if (!file) return null;
|
|
150
|
+
return policy.isShared(file) && sharedUrl
|
|
151
|
+
? { id: sharedUrl, external: 'absolute' }
|
|
152
|
+
: policy.isGlobalOwned(file) && globalUrl
|
|
153
|
+
? { id: globalUrl, external: 'absolute' }
|
|
154
|
+
: { id: file };
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
123
159
|
async function runScriptBundle(
|
|
124
160
|
uniquePaths: string[],
|
|
125
161
|
policy: ScriptBundlePolicy,
|
|
162
|
+
options: ScriptBundleOptions = {},
|
|
126
163
|
): Promise<BundleResult> {
|
|
127
164
|
const roots = uniquePaths.map((p) => policy.fileForScript(p));
|
|
165
|
+
const compact = options.compact && policy.compact;
|
|
128
166
|
const suffix = policy.inlineableRoots(roots);
|
|
129
167
|
const prefixChunks = new Map(
|
|
130
168
|
roots.flatMap((file, index) =>
|
|
131
169
|
!policy.isPreserved(file) && !suffix.has(file) ? [[file, `entry-${index}`] as const] : [],
|
|
132
170
|
),
|
|
133
171
|
);
|
|
134
|
-
const
|
|
172
|
+
const islands = (options.islands || []).map(({ key, script }) => ({
|
|
173
|
+
key,
|
|
174
|
+
file: policy.fileForScript(script),
|
|
175
|
+
}));
|
|
176
|
+
const renderer = options.renderer ? policy.fileForScript(options.renderer) : undefined;
|
|
177
|
+
const exportFiles = (options.exportScripts || []).map((script) => policy.fileForScript(script));
|
|
178
|
+
const entryContent = [
|
|
179
|
+
...roots.map((filePath) =>
|
|
180
|
+
options.name === 'shared'
|
|
181
|
+
? `export * from ${JSON.stringify(filePath)};`
|
|
182
|
+
: `import ${JSON.stringify(filePath)};`,
|
|
183
|
+
),
|
|
184
|
+
...exportFiles.map((filePath) => `export * from ${JSON.stringify(filePath)};`),
|
|
185
|
+
...(islands.length
|
|
186
|
+
? [
|
|
187
|
+
'globalThis.__L5E_ISLANDS__ ||= {};',
|
|
188
|
+
...islands.map(
|
|
189
|
+
({ key, file }) =>
|
|
190
|
+
`globalThis.__L5E_ISLANDS__[${JSON.stringify(key)}] = async () => { const [renderer, module] = await Promise.all([import(${JSON.stringify(renderer)}), import(${JSON.stringify(file)})]); return { reactDomClient: renderer.reactDomClient, createElement: renderer.createElement, module }; };`,
|
|
191
|
+
),
|
|
192
|
+
'globalThis.__L5E_BOOT_ISLANDS__?.();',
|
|
193
|
+
]
|
|
194
|
+
: []),
|
|
195
|
+
].join('\n');
|
|
135
196
|
|
|
136
197
|
const rolldownOptions: InputOptions = {
|
|
137
198
|
input: VIRTUAL_ENTRY_ID,
|
|
138
|
-
plugins: [
|
|
199
|
+
plugins: [
|
|
200
|
+
virtualEntryPlugin(entryContent),
|
|
201
|
+
compact
|
|
202
|
+
? compactBuildPlugin(policy, options.sharedUrl, options.globalUrl)
|
|
203
|
+
: preserveBuildChunksPlugin(policy),
|
|
204
|
+
],
|
|
139
205
|
platform: 'neutral',
|
|
140
206
|
tsconfig: false,
|
|
141
207
|
// Runtime scripts are imported for their side effects. Do not let an app's
|
|
@@ -156,9 +222,12 @@ async function runScriptBundle(
|
|
|
156
222
|
|
|
157
223
|
const outputOptions: OutputOptions = {
|
|
158
224
|
format: 'es',
|
|
159
|
-
codeSplitting:
|
|
225
|
+
codeSplitting: compact
|
|
226
|
+
? false
|
|
227
|
+
: { groups: [{ name: (id) => prefixChunks.get(id) ?? null }] },
|
|
160
228
|
minify: false,
|
|
161
|
-
entryFileNames: 'bundle-[hash].js
|
|
229
|
+
entryFileNames: `${options.name || 'bundle'}-[hash].js`,
|
|
230
|
+
strictExecutionOrder: compact,
|
|
162
231
|
chunkFileNames: `bundle-${generateHash(JSON.stringify(roots))}-[hash].js`,
|
|
163
232
|
};
|
|
164
233
|
|
|
@@ -206,6 +275,7 @@ export async function bundleScripts(
|
|
|
206
275
|
scriptPaths: string[],
|
|
207
276
|
distClientDir: string,
|
|
208
277
|
policy?: ScriptBundlePolicy,
|
|
278
|
+
options: ScriptBundleOptions = {},
|
|
209
279
|
): Promise<BundleResult> {
|
|
210
280
|
if (scriptPaths.length === 0) {
|
|
211
281
|
return EMPTY_RESULT;
|
|
@@ -216,11 +286,11 @@ export async function bundleScripts(
|
|
|
216
286
|
return EMPTY_RESULT;
|
|
217
287
|
}
|
|
218
288
|
|
|
219
|
-
const uniquePaths = [...new Set(scriptPaths)]
|
|
220
|
-
const cacheKey = `scripts:${policy.cacheKey}:${JSON.stringify(uniquePaths)}`;
|
|
289
|
+
const uniquePaths = [...new Set(scriptPaths)];
|
|
290
|
+
const cacheKey = `scripts:${policy.cacheKey}:${JSON.stringify([uniquePaths, options])}`;
|
|
221
291
|
|
|
222
292
|
try {
|
|
223
|
-
return await dedupe(cacheKey, () => runScriptBundle(uniquePaths, policy));
|
|
293
|
+
return await dedupe(cacheKey, () => runScriptBundle(uniquePaths, policy, options));
|
|
224
294
|
} catch (error) {
|
|
225
295
|
console.error('[bundler] Error bundling scripts:', error);
|
|
226
296
|
return EMPTY_RESULT;
|