diff options
Diffstat (limited to 'bs5/packages/extract-client-components/esbuild-plugin.mjs')
-rw-r--r-- | bs5/packages/extract-client-components/esbuild-plugin.mjs | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/bs5/packages/extract-client-components/esbuild-plugin.mjs b/bs5/packages/extract-client-components/esbuild-plugin.mjs new file mode 100644 index 0000000..cbc819c --- /dev/null +++ b/bs5/packages/extract-client-components/esbuild-plugin.mjs @@ -0,0 +1,89 @@ +import Fs from "node:fs/promises"; +import Path from "node:path"; +import { execSync } from "node:child_process"; + +async function generateBootstrapFile(output, content) { + let previousContent = undefined; + try { + previousContent = await Fs.readFile(output, "utf8"); + } catch (e) { + if (e.code !== "ENOENT") { + throw e; + } + } + const contentHasChanged = previousContent !== content; + if (contentHasChanged) { + await Fs.writeFile(output, content, "utf8"); + } +} + +export function plugin(config) { + return { + name: "extract-client-components", + setup(build) { + if ( + config.bootstrapOutput && + typeof config.bootstrapOutput !== "string" + ) { + console.error("bootstrapOutput must be a string"); + return; + } + const bootstrapOutput = config.bootstrapOutput || "./bootstrap.js"; + + if (!config.target) { + console.error("target is required"); + return; + } + if (typeof config.target !== "string") { + console.error("target must be a string"); + return; + } + + build.onStart(async () => { + try { + /* TODO: Make sure `server_reason_react.extract_client_components` is available in $PATH */ + const bootstrapContent = execSync( + `server_reason_react.extract_client_components ${config.target}`, + { encoding: "utf8" }, + ); + await generateBootstrapFile(bootstrapOutput, bootstrapContent); + } catch (e) { + console.log("Extraction of client components failed:"); + console.error(e); + return; + } + }); + + build.onResolve({ filter: /.*/ }, (args) => { + const isEntryPoint = args.kind === "entry-point"; + + if (isEntryPoint) { + return { + path: args.path, + namespace: "entrypoint", + }; + } + return null; + }); + + build.onLoad({ filter: /.*/, namespace: "entrypoint" }, async (args) => { + const filePath = args.path.replace(/^entrypoint:/, ""); + const entryPointContents = await Fs.readFile(filePath, "utf8"); + const relativeBootstrapOutput = Path.relative( + Path.dirname(filePath), + bootstrapOutput, + ); + + const contents = ` +require("./${relativeBootstrapOutput}"); +${entryPointContents}`; + + return { + loader: "jsx", + contents, + resolveDir: Path.dirname(Path.resolve(process.cwd(), filePath)), + }; + }); + }, + }; +} |