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:
+44
-2
@@ -2,6 +2,8 @@ import { existsSync } from "fs";
|
||||
import grabDirNames from "../../utils/grab-dir-names";
|
||||
import grabConstants from "../../utils/grab-constants";
|
||||
import grabRootFilePath from "../server/web-pages/grab-root-file-path";
|
||||
import grabRelativePath from "../../utils/grab-relative-path";
|
||||
import { log } from "../../utils/log";
|
||||
const { ROOT_DIR } = grabDirNames();
|
||||
export default async function grabClientHydrationScript({ page_local_path, }) {
|
||||
const { ClientRootElementIDName, ClientRootComponentWindowName, ClientWindowPagePropsName, } = grabConstants();
|
||||
@@ -18,12 +20,19 @@ export default async function grabClientHydrationScript({ page_local_path, }) {
|
||||
return undefined;
|
||||
}
|
||||
const root_content = await Bun.file(root_file_path).text();
|
||||
if (!root_content.match(/^export default/m)) {
|
||||
if (!hasBundlableDefaultExport({
|
||||
file_path: root_file_path,
|
||||
content: root_content,
|
||||
is_root: true,
|
||||
})) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
const page_content = await Bun.file(page_local_path).text();
|
||||
if (!page_content.match(/^export default/m)) {
|
||||
if (!hasBundlableDefaultExport({
|
||||
file_path: page_local_path,
|
||||
content: page_content,
|
||||
})) {
|
||||
return undefined;
|
||||
}
|
||||
let txt = ``;
|
||||
@@ -54,3 +63,36 @@ export default async function grabClientHydrationScript({ page_local_path, }) {
|
||||
txt += `}\n`;
|
||||
return txt;
|
||||
}
|
||||
// File path -> content hash of the last warning, so rebuilds don't repeat
|
||||
// a warning until the file changes.
|
||||
const warned_files = new Map();
|
||||
/**
|
||||
* # Check a page or root file can be bundled
|
||||
* Files are only bundled when `export default` starts a line. Files with
|
||||
* no default export at all (e.g. a page still being written) are skipped
|
||||
* silently; this warns about the confusing case where one exists
|
||||
* elsewhere, like `import X from "x"; export default X` on one line, or
|
||||
* `export { Page as default }`.
|
||||
*/
|
||||
function hasBundlableDefaultExport({ file_path, content, is_root, }) {
|
||||
if (/^export default/m.test(content)) {
|
||||
warned_files.delete(file_path);
|
||||
return true;
|
||||
}
|
||||
const has_default_export = /\bexport\s+default\b/.test(content) ||
|
||||
/\bas\s+default\b/.test(content);
|
||||
if (!has_default_export) {
|
||||
warned_files.delete(file_path);
|
||||
return false;
|
||||
}
|
||||
const content_hash = Bun.hash(content);
|
||||
if (warned_files.get(file_path) === content_hash)
|
||||
return false;
|
||||
warned_files.set(file_path, content_hash);
|
||||
log.warn(`${grabRelativePath(file_path)} was skipped: ` +
|
||||
`\`export default\` must be at the start of a line. ` +
|
||||
(is_root
|
||||
? `No pages can be bundled until the root layout is fixed.`
|
||||
: `This page will not be bundled.`));
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user