UNPKG

rwsdk

Version:

Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime

180 lines (179 loc) 7.49 kB
import debug from "debug"; import { ROOT_DIR } from "../lib/constants.mjs"; import enhancedResolve from "enhanced-resolve"; import { ensureAliasArray } from "./ensureAliasArray.mjs"; const log = debug("rwsdk:vite:react-conditions-resolver-plugin"); export const ENV_REACT_IMPORTS = { worker: [ "react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime", "react-server-dom-webpack/server.edge", ], ssr: [ "react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime", "react-dom/server.edge", "react-dom/server", "react-server-dom-webpack/client.edge", ], client: [ "react", "react-dom", "react-dom/client", "react/jsx-runtime", "react/jsx-dev-runtime", "react-server-dom-webpack/client.browser", "react-server-dom-webpack/client.edge", ], }; export const ENV_RESOLVERS = { ssr: enhancedResolve.create.sync({ conditionNames: ["workerd", "worker", "edge", "default"], }), worker: enhancedResolve.create.sync({ conditionNames: ["react-server", "workerd", "worker", "edge", "default"], }), client: enhancedResolve.create.sync({ conditionNames: ["browser", "default"], }), }; export const ENV_IMPORT_MAPPINGS = Object.fromEntries(Object.keys(ENV_RESOLVERS).map((env) => [ env, resolveEnvImportMappings(env), ])); function resolveEnvImportMappings(env) { process.env.VERBOSE && log("Resolving environment import mappings for env=%s", env); const mappings = new Map(); const reactImports = ENV_REACT_IMPORTS[env]; for (const importRequest of reactImports) { process.env.VERBOSE && log("Resolving import request=%s for env=%s", importRequest, env); let resolved = false; try { resolved = ENV_RESOLVERS[env](ROOT_DIR, importRequest); process.env.VERBOSE && log("Successfully resolved %s to %s for env=%s", importRequest, resolved, env); } catch { process.env.VERBOSE && log("Failed to resolve %s for env=%s", importRequest, env); } if (resolved) { mappings.set(importRequest, resolved); log("Added mapping for %s -> %s in env=%s", importRequest, resolved, env); } } log("Environment import mappings complete for env=%s: %d mappings", env, mappings.size); return mappings; } function createEsbuildResolverPlugin(envName) { const mappings = ENV_IMPORT_MAPPINGS[envName]; if (!mappings) { return null; } return { name: `rwsdk:react-conditions-resolver-esbuild-${envName}`, setup(build) { build.onResolve({ filter: /.*/ }, (args) => { process.env.VERBOSE && log("ESBuild resolving %s for env=%s, args=%O", args.path, envName, args); const resolved = mappings.get(args.path); if (resolved && args.importer !== "") { process.env.VERBOSE && log("ESBuild resolving %s -> %s for env=%s", args.path, resolved, envName); if (args.path === "react-server-dom-webpack/client.edge") { return; } return { path: resolved, }; } else { process.env.VERBOSE && log("ESBuild no resolution found for %s for env=%s", args.path, envName); } }); }, }; } export const reactConditionsResolverPlugin = () => { log("Initializing react conditions resolver plugin"); let isBuild = false; return [ { name: "rwsdk:react-conditions-resolver:config", enforce: "post", config(config, { command }) { isBuild = command === "build"; log("Configuring plugin for command=%s", command); }, configResolved(config) { log("Setting up resolve aliases and optimizeDeps for each environment"); // Set up aliases and optimizeDeps for each environment for (const [envName, mappings] of Object.entries(ENV_IMPORT_MAPPINGS)) { const reactImports = ENV_REACT_IMPORTS[envName]; // Ensure environment config exists if (!config.environments) { config.environments = {}; } if (!config.environments[envName]) { config.environments[envName] = {}; } const envConfig = config.environments[envName]; const esbuildPlugin = createEsbuildResolverPlugin(envName); if (esbuildPlugin && mappings) { envConfig.optimizeDeps ??= {}; envConfig.optimizeDeps.esbuildOptions ??= {}; envConfig.optimizeDeps.esbuildOptions.define ??= {}; envConfig.optimizeDeps.esbuildOptions.define["process.env.NODE_ENV"] = JSON.stringify(process.env.NODE_ENV); envConfig.optimizeDeps.esbuildOptions.plugins ??= []; envConfig.optimizeDeps.esbuildOptions.plugins.push(esbuildPlugin); envConfig.optimizeDeps.include ??= []; envConfig.optimizeDeps.include.push(...reactImports); log("Added esbuild plugin and optimizeDeps includes for environment: %s", envName); } const aliases = ensureAliasArray(envConfig); for (const [find, replacement] of mappings) { const findRegex = new RegExp(`^${find.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")}$`); aliases.push({ find: findRegex, replacement }); log("Added alias for env=%s: %s -> %s", envName, find, replacement); } log("Environment %s configured with %d aliases and %d optimizeDeps includes", envName, mappings.size, reactImports.length); } }, }, { name: "rwsdk:react-conditions-resolver:resolveId", enforce: "pre", async resolveId(id, importer) { if (!isBuild) { return; } const envName = this.environment?.name; if (!envName) { return; } process.env.VERBOSE && log("Resolving id=%s, environment=%s, importer=%s", id, envName, importer); const mappings = ENV_IMPORT_MAPPINGS[envName]; if (!mappings) { process.env.VERBOSE && log("No mappings found for environment: %s", envName); return; } const resolved = mappings.get(id); if (resolved) { log("Resolved %s -> %s for env=%s", id, resolved, envName); return resolved; } process.env.VERBOSE && log("No resolution found for id=%s in env=%s", id, envName); }, }, ]; };