Date sitemap lastmod from the build pipeline; warn on skipped pages
- bunext build records when each page's client bundle hash last changed
in .bunext/page-lastmod.json (sitemap.lastmod_file to relocate). The
hash covers the page's whole import graph, so component edits re-date
every page that uses them. The sitemap uses these dates as lastmod
- Add sitemap.default_lastmod
- BREAKING: use_file_lastmod now defaults to false (file mtimes are reset
by checkouts); it is a fallback after the build record
- Warn when a page or __root.tsx is skipped because its default export
is not at the start of a line (e.g. export { Page as default })
- Replace leaking module mocks in tests (log, grab-root-file-path) with
spies and fixtures
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||
import path from "path";
|
||||
import type { BundlerCTXMap, BunextPageLastmodRecord } from "../types";
|
||||
import grabDirNames from "./grab-dir-names";
|
||||
import { log } from "./log";
|
||||
|
||||
/**
|
||||
* # Path of the page last-modified record
|
||||
*/
|
||||
export function grabPageLastmodFile(lastmod_file?: string) {
|
||||
const { ROOT_DIR, PAGE_LASTMOD_JSON } = grabDirNames();
|
||||
const target = lastmod_file || global.BUNEXT_CONFIG?.sitemap?.lastmod_file;
|
||||
|
||||
return target ? path.resolve(ROOT_DIR, target) : PAGE_LASTMOD_JSON;
|
||||
}
|
||||
|
||||
export function readPageLastmodRecord(
|
||||
lastmod_file?: string,
|
||||
): BunextPageLastmodRecord | undefined {
|
||||
const file = grabPageLastmodFile(lastmod_file);
|
||||
if (!existsSync(file)) return;
|
||||
|
||||
try {
|
||||
const record = JSON.parse(readFileSync(file, "utf-8"));
|
||||
if (record?.pages && typeof record.pages == "object") return record;
|
||||
} catch {}
|
||||
|
||||
log.warn(`Ignoring invalid page lastmod record ${file}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* # Record when each page's client bundle last changed
|
||||
* A page's bundle hash covers its whole client import graph (including
|
||||
* the root layout), so a component edit re-dates every page that uses it.
|
||||
* Unchanged pages keep their previous date; removed pages are dropped.
|
||||
*/
|
||||
export function updatePageLastmodRecord({
|
||||
ctx_map,
|
||||
build_date = new Date(),
|
||||
lastmod_file,
|
||||
}: {
|
||||
ctx_map: { [local_path: string]: BundlerCTXMap };
|
||||
build_date?: Date;
|
||||
lastmod_file?: string;
|
||||
}): BunextPageLastmodRecord {
|
||||
const prior = readPageLastmodRecord(lastmod_file);
|
||||
const record: BunextPageLastmodRecord = { pages: {} };
|
||||
|
||||
// Sorted so a committed record produces minimal diffs
|
||||
const local_paths = Object.keys(ctx_map).sort();
|
||||
|
||||
for (const local_path of local_paths) {
|
||||
const artifact = ctx_map[local_path];
|
||||
if (!artifact?.hash) continue;
|
||||
|
||||
const fingerprint = `${artifact.hash}:${artifact.css_path || ""}`;
|
||||
const prior_page = prior?.pages[local_path];
|
||||
|
||||
record.pages[local_path] =
|
||||
prior_page?.fingerprint == fingerprint
|
||||
? prior_page
|
||||
: { fingerprint, lastmod: build_date.toISOString() };
|
||||
}
|
||||
|
||||
const file = grabPageLastmodFile(lastmod_file);
|
||||
mkdirSync(path.dirname(file), { recursive: true });
|
||||
writeFileSync(file, JSON.stringify(record, null, 4), "utf-8");
|
||||
|
||||
return record;
|
||||
}
|
||||
Reference in New Issue
Block a user