Files
quixos-nix-helpers/browser-source.mjs

70 lines
3.1 KiB
JavaScript

import { createRequire } from "node:module";
import path from "node:path";
// Resolve build dependencies from the package's locked node_modules, not from
// the helper's own Nix-store location. This program runs only during the build.
const require = createRequire(path.join(process.cwd(), "package.json"));
const { build } = require("esbuild");
const options = JSON.parse(process.argv[2]);
const platform = new Map([
["react", "/__quixos/platform/react/v18.mjs"],
["react/jsx-runtime", "/__quixos/platform/react-jsx-runtime/v18.mjs"],
["react/jsx-dev-runtime", "/__quixos/platform/react-jsx-dev-runtime/v18.mjs"],
["@quixos/web-studio-react-runtime", "/__quixos/platform/web-studio-react-runtime/v1.mjs"],
]);
await build({
...options,
plugins: [
{
name: "quixos-browser-source",
setup(api) {
api.onResolve({ filter: /\?browser-source$/ }, async ({ path: specifier, resolveDir }) => {
if (!specifier.startsWith("./") && !specifier.startsWith("../"))
throw new Error("Browser source imports must name a relative compiled module");
const resolved = await api.resolve(specifier.slice(0, -"?browser-source".length), {
resolveDir,
kind: "import-statement",
});
if (resolved.errors.length) return { errors: resolved.errors };
return { path: resolved.path, namespace: "browser-source" };
});
api.onLoad({ filter: /.*/, namespace: "browser-source" }, async ({ path: entry }) => {
const browser = await build({
entryPoints: [entry],
bundle: true,
write: false,
outfile: "component.mjs",
format: "esm",
platform: "browser",
target: "es2022",
metafile: true,
plugins: [
{
name: "quixos-platform",
setup(browserApi) {
browserApi.onResolve({ filter: /.*/ }, ({ path: name }) =>
platform.has(name) ? { path: platform.get(name), external: true } : undefined,
);
},
},
],
});
const js = browser.outputFiles.find((file) => file.path.endsWith(".mjs"));
if (!js) throw new Error("Browser source build produced no JavaScript");
const css = browser.outputFiles.find((file) => file.path.endsWith(".css"));
if (css && Object.values(browser.metafile.outputs).some((output) => output.exports?.includes("styles")))
throw new Error("Use either imported CSS or an explicit styles export, not both");
if (browser.outputFiles.some((file) => !file.path.endsWith(".mjs") && !file.path.endsWith(".css")))
throw new Error("Browser assets must be embedded, not emitted as unserved files");
const source = js.text + (css ? `\nexport const styles = ${JSON.stringify(css.text)};\n` : "");
return {
contents: `export default ${JSON.stringify(source)};`,
loader: "js",
watchFiles: Object.keys(browser.metafile.inputs),
};
});
},
},
],
});