@teamscala/service-boot 0.23.496 → 0.23.498
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 +561 -561
- package/src/build-client-assets/hydrate-bundle.ts +38 -6
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
// be silently bypassed; a valid local-export chunk carries a
|
|
56
56
|
// class/function/const/var/let/import, a valid re-export carries `from`).
|
|
57
57
|
|
|
58
|
-
import { mkdir, readdir,
|
|
58
|
+
import { mkdir, readdir, rm } from "node:fs/promises";
|
|
59
59
|
import { join } from "node:path";
|
|
60
60
|
import { getAppLogger } from "@teamscala/logger/app-loggers";
|
|
61
61
|
import { fileHash, removeStaleHashedFiles } from "./hashed-output.ts";
|
|
@@ -79,7 +79,6 @@ export async function buildHydrateBundle(
|
|
|
79
79
|
): Promise<HydrateBundleResult | null> {
|
|
80
80
|
const entry = join(cwd, "src", "entry-client.ts");
|
|
81
81
|
const outDir = join(cwd, "dist", "client");
|
|
82
|
-
const tmpFile = join(outDir, `${HYDRATE_BASE}.js`);
|
|
83
82
|
|
|
84
83
|
if (!(await Bun.file(entry).exists())) {
|
|
85
84
|
log.warn(
|
|
@@ -123,7 +122,26 @@ export async function buildHydrateBundle(
|
|
|
123
122
|
entrypoints: [entry],
|
|
124
123
|
target: "browser",
|
|
125
124
|
outdir: outDir,
|
|
126
|
-
|
|
125
|
+
// THE BUNDLER OWNS THE HASH, because it also writes the references.
|
|
126
|
+
// Under `splitting: true` + `publicPath`, Bun bakes the entry's
|
|
127
|
+
// build-time name into every chunk that imports it. Naming the entry
|
|
128
|
+
// `hydrate.js` here and RENAMING it to `hydrate-<hash>.js` afterwards
|
|
129
|
+
// moved the file but could not rewrite the specifiers already emitted
|
|
130
|
+
// into the chunks — so every chunk imported `/assets/hydrate.js`, which
|
|
131
|
+
// no longer existed. 404 on the module, the importing block dies, and
|
|
132
|
+
// the page still returns 200 with every guard green.
|
|
133
|
+
//
|
|
134
|
+
// MEASURED 2026-09-08 on quiz.scala.business: `AssessmentFlowBlock`
|
|
135
|
+
// failed with "Failed to fetch dynamically imported module"; crawling
|
|
136
|
+
// the served graph (142 level-1 + 76 level-2 assets) found exactly one
|
|
137
|
+
// broken URL — `/assets/hydrate.js` — imported by the entry itself and
|
|
138
|
+
// by most chunks. Hash applied OUTSIDE the generator is two artefacts
|
|
139
|
+
// that must agree with nothing making them (`one-generator-pattern`).
|
|
140
|
+
naming: {
|
|
141
|
+
entry: `${HYDRATE_BASE}-[hash].[ext]`,
|
|
142
|
+
chunk: "chunk-[hash].[ext]",
|
|
143
|
+
asset: "[name]-[hash].[ext]",
|
|
144
|
+
},
|
|
127
145
|
minifyWhitespace: process.env.NODE_ENV === "production",
|
|
128
146
|
minifySyntax: process.env.NODE_ENV === "production",
|
|
129
147
|
minifyIdentifiers: false,
|
|
@@ -232,10 +250,24 @@ export async function buildHydrateBundle(
|
|
|
232
250
|
);
|
|
233
251
|
}
|
|
234
252
|
|
|
235
|
-
|
|
236
|
-
|
|
253
|
+
// Bun emitted the hashed entry itself, so the name it wrote into every
|
|
254
|
+
// chunk IS the name on disk. Read it back from the build's own output
|
|
255
|
+
// list rather than reconstructing it — a reconstructed name is a second
|
|
256
|
+
// copy of a fact the bundler already owns, which is the defect this
|
|
257
|
+
// change removes (`a-mutating-verb-reads-back-what-it-claims`).
|
|
258
|
+
const entryOutput = result.outputs.find(
|
|
259
|
+
(o) => o.kind === "entry-point",
|
|
260
|
+
);
|
|
261
|
+
if (!entryOutput) {
|
|
262
|
+
throw new Error(
|
|
263
|
+
"[build-client-assets] Bun.build reported success but emitted no entry-point output — refusing to ship a bundle with no entry.",
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
const hashedName = entryOutput.path.split("/").pop() as string;
|
|
237
267
|
const hashedPath = join(outDir, hashedName);
|
|
238
|
-
|
|
268
|
+
const hash =
|
|
269
|
+
/^hydrate-([A-Za-z0-9]+)\.js$/.exec(hashedName)?.[1] ??
|
|
270
|
+
(await fileHash(hashedPath));
|
|
239
271
|
removeStaleHashedFiles(outDir, HYDRATE_BASE, "js", hashedName);
|
|
240
272
|
|
|
241
273
|
// Splitting emits one chunk per lazy import() alongside the entry. Keep the
|