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();
const { root_file_path } = grabRootFilePath();
// const target_path = pagePathTransform({ page_path: page_local_path });
// const target_root_path = root_file_path
// ? pagePathTransform({ page_path: root_file_path })
// : undefined;
if (!existsSync(page_local_path)) {
return undefined;
}
if (root_file_path) {
if (!existsSync(root_file_path)) {
return undefined;
}
const root_content = await Bun.file(root_file_path).text();
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 (!hasBundlableDefaultExport({
file_path: page_local_path,
content: page_content,
})) {
return undefined;
}
let txt = ``;
txt += `import { hydrateRoot } from "${ROOT_DIR}/node_modules/react-dom/client.js";\n`;
// txt += `import react from "${ROOT_DIR}/node_modules/react/index.js";\n`;
if (root_file_path) {
txt += `import Root from "${root_file_path}";\n`;
}
txt += `import Page from "${page_local_path}";\n\n`;
txt += `const pageProps = window.${ClientWindowPagePropsName} || {};\n`;
if (root_file_path) {
txt += `const component = \n`;
}
else {
txt += `const component = \n`;
}
txt += `if (window.${ClientRootComponentWindowName}?.render) {\n`;
txt += ` window.${ClientRootComponentWindowName}.render(component);\n`;
txt += `} else {\n`;
txt += ` const root = hydrateRoot(document.getElementById("${ClientRootElementIDName}"), component, { onRecoverableError: () => {\n\n`;
txt += ` console.log(\`Hydration Error.\`)\n\n`;
txt += ` } });\n\n`;
txt += ` window.${ClientRootComponentWindowName} = root;\n`;
txt += ` window.__BUNEXT_RERENDER__ = (NewPage) => {\n`;
txt += ` const props = window.${ClientWindowPagePropsName} || {};\n`;
txt += ` root.render();\n`;
txt += ` };\n`;
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;
}