spark-html-bun 0.1.1 → 0.1.2
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 +2 -2
- package/src/index.js +23 -9
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spark-html-bun",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Dev server, build, and preview for spark-html apps
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Dev server, build, and preview for spark-html apps \u2014 built entirely on Bun. Scoped component HMR over a plain WebSocket, import-map dev serving (no bundling in dev), Bun.build for the app shell, and an explicit post-build pipeline (prerender, image, font, manifest, offline, sri). Zero dependencies.",
|
|
5
5
|
"homepage": "https://wilkinnovo.github.io/spark",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./src/index.js",
|
package/src/index.js
CHANGED
|
@@ -60,6 +60,13 @@ function decodePath(pathname) {
|
|
|
60
60
|
try { return decodeURIComponent(pathname); } catch { return null; }
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
// One stat instead of existsSync + statSync — this runs per request candidate
|
|
64
|
+
// on the dev/preview hot path.
|
|
65
|
+
function isFile(p) {
|
|
66
|
+
const s = statSync(p, { throwIfNoEntry: false });
|
|
67
|
+
return s !== undefined && s.isFile();
|
|
68
|
+
}
|
|
69
|
+
|
|
63
70
|
const MIME = {
|
|
64
71
|
'.html': 'text/html', '.js': 'text/javascript', '.mjs': 'text/javascript',
|
|
65
72
|
'.css': 'text/css', '.json': 'application/json', '.svg': 'image/svg+xml',
|
|
@@ -253,7 +260,7 @@ export async function dev(overrides = {}) {
|
|
|
253
260
|
let file = null;
|
|
254
261
|
for (const b of [projectRoot, pub]) {
|
|
255
262
|
const c = safeJoin(b, rel);
|
|
256
|
-
if (c &&
|
|
263
|
+
if (c && isFile(c)) { file = c; break; }
|
|
257
264
|
}
|
|
258
265
|
|
|
259
266
|
// SPA fallback: extensionless paths serve the app shell (the router
|
|
@@ -343,12 +350,17 @@ export async function build(overrides = {}) {
|
|
|
343
350
|
const file = clean.startsWith('/') ? join(projectRoot, clean.slice(1)) : join(entryDir, clean);
|
|
344
351
|
// Only bundle files that live in the PROJECT (src/…) — anything served
|
|
345
352
|
// from publicDir ships verbatim and its URL already works.
|
|
346
|
-
if (existsSync(file) && !file.startsWith(pub +
|
|
353
|
+
if (existsSync(file) && !file.startsWith(pub + sep)) found.push({ url, file });
|
|
347
354
|
}
|
|
348
355
|
|
|
349
356
|
if (found.length) {
|
|
357
|
+
// Bun.build DEDUPES duplicate entrypoints (the same file listed twice —
|
|
358
|
+
// or reached via two URL spellings — yields ONE output), so bundle the
|
|
359
|
+
// UNIQUE files and map file → hashed name; never map outputs back by
|
|
360
|
+
// found-index, which would splice the wrong asset URL into the page.
|
|
361
|
+
const files = [...new Set(found.map((f) => f.file))];
|
|
350
362
|
const result = await Bun.build({
|
|
351
|
-
entrypoints:
|
|
363
|
+
entrypoints: files,
|
|
352
364
|
outdir: join(outDir, 'assets'),
|
|
353
365
|
minify: true,
|
|
354
366
|
splitting: true,
|
|
@@ -362,17 +374,19 @@ export async function build(overrides = {}) {
|
|
|
362
374
|
throw new Error(`[spark] build failed:\n${msgs}`);
|
|
363
375
|
}
|
|
364
376
|
// Entry outputs come back in entrypoint order (verified for Bun's
|
|
365
|
-
// splitting output, incl. same-basename entries) — map each
|
|
377
|
+
// splitting output, incl. same-basename entries) — map each unique
|
|
378
|
+
// file to its hashed name, then rewrite every tag that referenced it.
|
|
366
379
|
const entryOuts = result.outputs.filter((o) => o.kind === 'entry-point');
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
380
|
+
const outName = new Map(files.map((file, i) => [file, entryOuts[i] && basename(entryOuts[i].path)]));
|
|
381
|
+
for (const f of found) {
|
|
382
|
+
const name = outName.get(f.file);
|
|
383
|
+
if (!name) continue;
|
|
370
384
|
const to = `${base}assets/${name}`;
|
|
371
385
|
// Replace only the quoted attribute value, so a longer URL that ends
|
|
372
386
|
// with this one (e.g. /lib/app.js vs /app.js) is never corrupted the
|
|
373
387
|
// way a bare replaceAll(url) would corrupt it.
|
|
374
388
|
for (const q of ['"', "'"]) html = html.split(`${q}${f.url}${q}`).join(`${q}${to}${q}`);
|
|
375
|
-
}
|
|
389
|
+
}
|
|
376
390
|
}
|
|
377
391
|
await Bun.write(join(outDir, config.entry.split('/').pop()), html);
|
|
378
392
|
}
|
|
@@ -411,7 +425,7 @@ export async function preview(overrides = {}) {
|
|
|
411
425
|
rel !== '' && rel.endsWith('/') ? safeJoin(outDir, join(rel, 'index.html')) : null,
|
|
412
426
|
].filter(Boolean);
|
|
413
427
|
for (const f of tryFiles) {
|
|
414
|
-
if (
|
|
428
|
+
if (isFile(f)) {
|
|
415
429
|
return new Response(Bun.file(f), { headers: { 'Content-Type': mime(f) } });
|
|
416
430
|
}
|
|
417
431
|
}
|