bosia 0.6.17 → 0.6.19
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/package.json +1 -1
- package/src/core/build.ts +1 -1
- package/src/core/client/hydrate.ts +39 -0
- package/src/core/html.ts +1 -0
- package/src/core/prerender.ts +18 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bosia",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.19",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A fast, batteries-included fullstack framework — SSR · Svelte 5 Runes · Bun · ElysiaJS. File-based routing inspired by SvelteKit. No Node.js, no Vite, no adapters.",
|
|
6
6
|
"keywords": [
|
package/src/core/build.ts
CHANGED
|
@@ -159,7 +159,7 @@ const clientPromise = Bun.build({
|
|
|
159
159
|
target: "browser",
|
|
160
160
|
conditions: ["svelte"],
|
|
161
161
|
splitting: true,
|
|
162
|
-
naming: { chunk: "[name]-[hash].[ext]" },
|
|
162
|
+
naming: { entry: "[name]-[hash].[ext]", chunk: "[name]-[hash].[ext]" },
|
|
163
163
|
minify: isProduction,
|
|
164
164
|
sourcemap: isProduction ? "none" : "linked",
|
|
165
165
|
define: {
|
|
@@ -136,6 +136,45 @@ main().catch((err) => {
|
|
|
136
136
|
console.error("[bosia] hydration failed", err);
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
+
// ─── Stale-Build Recovery ─────────────────────────────────
|
|
140
|
+
// After a deploy, browsers with the old HTML/entry cached may try to import
|
|
141
|
+
// hashed chunks that no longer exist on the server. The dynamic import()
|
|
142
|
+
// rejects and the router's promise chain has no .catch, so the rejection
|
|
143
|
+
// surfaces here as an unhandledrejection. Trigger a single full reload with a
|
|
144
|
+
// cache-busting query so the browser can't serve the old HTML from disk.
|
|
145
|
+
// A 10s sessionStorage guard prevents an infinite reload loop if the new
|
|
146
|
+
// build is also broken.
|
|
147
|
+
if (typeof window !== "undefined" && process.env.NODE_ENV === "production") {
|
|
148
|
+
const KEY = "bosia:reload-attempt";
|
|
149
|
+
const STALE_CHUNK =
|
|
150
|
+
/Failed to fetch dynamically imported module|Importing a module script failed|error loading dynamically imported module|Loading chunk|ChunkLoadError/i;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const last = Number(sessionStorage.getItem(KEY) ?? 0);
|
|
154
|
+
if (last && Date.now() - last < 10_000) {
|
|
155
|
+
sessionStorage.removeItem(KEY);
|
|
156
|
+
console.error("[bosia] reload guard hit — new build may also be broken.");
|
|
157
|
+
} else {
|
|
158
|
+
window.addEventListener("unhandledrejection", (e) => {
|
|
159
|
+
const msg = String((e.reason as { message?: unknown })?.message ?? e.reason ?? "");
|
|
160
|
+
if (!STALE_CHUNK.test(msg)) return;
|
|
161
|
+
e.preventDefault();
|
|
162
|
+
try {
|
|
163
|
+
sessionStorage.setItem(KEY, String(Date.now()));
|
|
164
|
+
} catch {
|
|
165
|
+
// Storage may be unavailable (private mode) — best effort.
|
|
166
|
+
}
|
|
167
|
+
const sep = window.location.search ? "&" : "?";
|
|
168
|
+
window.location.replace(
|
|
169
|
+
`${window.location.pathname}${window.location.search}${sep}_v=${Date.now()}${window.location.hash}`,
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
} catch {
|
|
174
|
+
// sessionStorage may throw in restricted contexts — give up silently.
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
139
178
|
// ─── Hot Reload (dev only) ────────────────────────────────
|
|
140
179
|
|
|
141
180
|
if (process.env.NODE_ENV !== "production") {
|
package/src/core/html.ts
CHANGED
package/src/core/prerender.ts
CHANGED
|
@@ -295,24 +295,32 @@ export async function prerenderStaticRoutes(manifest: RouteManifest): Promise<vo
|
|
|
295
295
|
// ─── Static Site Output ──────────────────────────────────
|
|
296
296
|
|
|
297
297
|
export function generateStaticSite(): void {
|
|
298
|
-
|
|
299
|
-
|
|
298
|
+
const hasPublic = existsSync("./public");
|
|
299
|
+
const hasPrerender = existsSync(`${OUT_DIR}/prerendered`);
|
|
300
|
+
|
|
301
|
+
// Mirror `public/` → `dist/static/` on every build (not only SSG builds) so
|
|
302
|
+
// production containers can ship dist/ alone. Without this, apps with zero
|
|
303
|
+
// prerendered routes (pure SSR) would lose bosia-tw.css and favicons when
|
|
304
|
+
// public/ is dropped from the image.
|
|
305
|
+
if (!hasPublic && !hasPrerender) {
|
|
306
|
+
console.log("\n⏭️ No public/ or prerendered pages — skipping static site output");
|
|
300
307
|
return;
|
|
301
308
|
}
|
|
302
309
|
|
|
303
310
|
console.log("\n📦 Generating static site...");
|
|
304
311
|
mkdirSync(`${OUT_DIR}/static`, { recursive: true });
|
|
305
312
|
|
|
306
|
-
// 1.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
// 2. Client JS/CSS — preserves /dist/client/... absolute paths used in HTML
|
|
310
|
-
cpSync(`${OUT_DIR}/client`, `${OUT_DIR}/static/dist/client`, { recursive: true });
|
|
311
|
-
|
|
312
|
-
// 3. Public assets (bosia-tw.css, favicon, etc.) — preserves /bosia-tw.css path
|
|
313
|
-
if (existsSync("./public")) {
|
|
313
|
+
// 1. Public assets (bosia-tw.css, favicon, etc.) — preserves /bosia-tw.css path
|
|
314
|
+
if (hasPublic) {
|
|
314
315
|
cpSync("./public", `${OUT_DIR}/static`, { recursive: true });
|
|
315
316
|
}
|
|
316
317
|
|
|
318
|
+
// 2. HTML files from prerendering (SSG output only)
|
|
319
|
+
if (hasPrerender) {
|
|
320
|
+
cpSync(`${OUT_DIR}/prerendered`, `${OUT_DIR}/static`, { recursive: true });
|
|
321
|
+
// 3. Client JS/CSS — preserves /dist/client/... absolute paths used in HTML
|
|
322
|
+
cpSync(`${OUT_DIR}/client`, `${OUT_DIR}/static/dist/client`, { recursive: true });
|
|
323
|
+
}
|
|
324
|
+
|
|
317
325
|
console.log(`✅ Static site generated: ${OUT_DIR}/static/`);
|
|
318
326
|
}
|