mnfst-publish 0.1.6 → 0.1.7
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/manifest.publish.mjs +41 -3
- package/package.json +1 -1
package/manifest.publish.mjs
CHANGED
|
@@ -285,6 +285,44 @@ export function collectFiles(root) {
|
|
|
285
285
|
return rels.filter((r) => !isExcludedPath(r) && !publishIgnored(r));
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
// --- Component version stamp ------------------------------------------------
|
|
289
|
+
|
|
290
|
+
// Stamp each shipped manifest.json (project root, and the prerender output copy)
|
|
291
|
+
// with `deployment`: a content hash of its component HTML files. The components
|
|
292
|
+
// plugin appends it as ?v= to component fetches, so browser caches bust exactly
|
|
293
|
+
// when component markup changes and persist when it doesn't. Purely publish-time:
|
|
294
|
+
// the on-disk manifest.json is never modified.
|
|
295
|
+
export function stampManifests(root, rels) {
|
|
296
|
+
const overrides = new Map();
|
|
297
|
+
const relSet = new Set(rels);
|
|
298
|
+
const outDir = prerenderOutputDir(root);
|
|
299
|
+
for (const mfRel of ['manifest.json', outDir + '/manifest.json']) {
|
|
300
|
+
if (!relSet.has(mfRel)) continue;
|
|
301
|
+
let mf;
|
|
302
|
+
try {
|
|
303
|
+
mf = JSON.parse(readFileSync(join(root, mfRel), 'utf8'));
|
|
304
|
+
} catch {
|
|
305
|
+
continue; // invalid JSON — ship as-is
|
|
306
|
+
}
|
|
307
|
+
const dir = mfRel.includes('/') ? mfRel.slice(0, mfRel.lastIndexOf('/') + 1) : '';
|
|
308
|
+
const paths = [...(mf.preloadedComponents || []), ...(mf.components || [])]
|
|
309
|
+
.filter((p) => typeof p === 'string' && !p.startsWith('http'))
|
|
310
|
+
.map((p) => dir + p.replace(/^\/+/, ''))
|
|
311
|
+
.filter((p) => relSet.has(p))
|
|
312
|
+
.sort();
|
|
313
|
+
if (!paths.length) continue;
|
|
314
|
+
const hash = createHash('sha256');
|
|
315
|
+
for (const p of paths) {
|
|
316
|
+
hash.update(p + '\0');
|
|
317
|
+
hash.update(readFileSync(join(root, p)));
|
|
318
|
+
hash.update('\0');
|
|
319
|
+
}
|
|
320
|
+
mf.deployment = hash.digest('hex').slice(0, 12);
|
|
321
|
+
overrides.set(mfRel, Buffer.from(JSON.stringify(mf, null, 2) + '\n', 'utf8'));
|
|
322
|
+
}
|
|
323
|
+
return overrides;
|
|
324
|
+
}
|
|
325
|
+
|
|
288
326
|
// --- Minimal ZIP writer (DEFLATE), pure Node, no deps ----------------------
|
|
289
327
|
|
|
290
328
|
function crc32(buf) {
|
|
@@ -296,7 +334,7 @@ function crc32(buf) {
|
|
|
296
334
|
return (~c) >>> 0;
|
|
297
335
|
}
|
|
298
336
|
|
|
299
|
-
function buildZip(root, rels) {
|
|
337
|
+
function buildZip(root, rels, overrides = new Map()) {
|
|
300
338
|
// This packer writes classic (non-Zip64) ZIP records: file count is a uint16
|
|
301
339
|
// and offsets are uint32. Fail loudly rather than emit a silently-corrupt
|
|
302
340
|
// archive past those limits.
|
|
@@ -310,7 +348,7 @@ function buildZip(root, rels) {
|
|
|
310
348
|
if (offset > 0xffffffff) {
|
|
311
349
|
fail('project is too large to package (>4 GB). Contact support.');
|
|
312
350
|
}
|
|
313
|
-
const data = readFileSync(join(root, rel));
|
|
351
|
+
const data = overrides.get(rel) ?? readFileSync(join(root, rel));
|
|
314
352
|
const nameBuf = Buffer.from(rel, 'utf8');
|
|
315
353
|
const crc = crc32(data);
|
|
316
354
|
const deflated = deflateRawSync(data);
|
|
@@ -448,7 +486,7 @@ export async function main() {
|
|
|
448
486
|
|
|
449
487
|
const rels = collectFiles(root);
|
|
450
488
|
if (!rels.length) fail('nothing to publish (no files found).');
|
|
451
|
-
const zip = buildZip(root, rels);
|
|
489
|
+
const zip = buildZip(root, rels, stampManifests(root, rels));
|
|
452
490
|
log(`Uploading ${rels.length} files (${(zip.length / 1048576).toFixed(1)} MB)…`);
|
|
453
491
|
|
|
454
492
|
const up = await fetchRetry(
|