bunext/dist/functions/bundler/plugins/chunk-react.js

98 lines
3.2 KiB
JavaScript

// plugins/react-vendor-chunk-plugin.ts
import * as esbuild from "esbuild";
import path from "path";
import grabDirNames from "../../../utils/grab-dir-names";
const { BUNEXT_VENDOR_DIR } = grabDirNames();
const REACT_MODULES = new Set([
"react",
"react-dom",
"react-dom/client",
"react/jsx-runtime",
"react/jsx-dev-runtime",
]);
const VENDOR_BASE = "/.bunext/public/vendor";
const REACT_ENTRIES = {
react: `
import React from "react";
export const {
Children, Component, Fragment, Profiler, PureComponent, StrictMode,
Suspense, cloneElement, createContext, createElement, createFactory,
createRef, forwardRef, isValidElement, lazy, memo, startTransition,
useCallback, useContext, useDebugValue, useDeferredValue, useEffect,
useId, useImperativeHandle, useInsertionEffect, useLayoutEffect,
useMemo, useReducer, useRef, useState, useSyncExternalStore,
useTransition, version,
} = React;
export default React;
`,
"react-dom": `
import ReactDOM from "react-dom";
export const {
createPortal, flushSync, findDOMNode, hydrate, render,
unmountComponentAtNode, version,
} = ReactDOM;
export default ReactDOM;
`,
"react-dom/client": `
import ReactDOMClient from "react-dom/client";
export const { createRoot, hydrateRoot } = ReactDOMClient;
export default ReactDOMClient;
`,
"react/jsx-runtime": `
import JSXRuntime from "react/jsx-runtime";
export const { jsx, jsxs, Fragment } = JSXRuntime;
export default JSXRuntime;
`,
"react/jsx-dev-runtime": `
import JSXDevRuntime from "react/jsx-dev-runtime";
export const { jsxDEV, Fragment } = JSXDevRuntime;
export default JSXDevRuntime;
`,
};
// Map bare specifier -> browser path
function vendorPath(specifier) {
const filename = specifier.replace(/\//g, "_") + ".js";
return `${VENDOR_BASE}/${filename}`;
}
function vendorOutfile(specifier) {
const filename = specifier.replace(/\//g, "_") + ".js";
return path.join(BUNEXT_VENDOR_DIR, filename);
}
export default function reactVendorChunkPlugin() {
let vendorReady;
return {
name: "react-vendor-chunk",
setup(build) {
vendorReady ??= buildAllVendorChunks(build.initialOptions);
build.onResolve({ filter: /^react(-dom)?(\/.*)?$/ }, async (args) => {
const bare = args.path.replace(/\/index(\.m?js)?$/, "");
if (!(bare in REACT_ENTRIES))
return;
await vendorReady;
return {
path: vendorPath(bare),
external: true,
};
});
},
};
}
async function buildAllVendorChunks(parentOptions) {
await Promise.all(Object.entries(REACT_ENTRIES).map(([specifier, contents]) => esbuild.build({
stdin: {
contents,
resolveDir: process.cwd(),
loader: "tsx",
},
outfile: vendorOutfile(specifier),
bundle: true,
minify: parentOptions.minify,
format: "esm",
target: parentOptions.target,
platform: "browser",
define: parentOptions.define,
mainFields: ["module", "main"],
conditions: ["import", "default"],
})));
}