UNPKG

@react-router/dev

Version:

Dev tools and CLI for React Router

1,516 lines (1,467 loc) • 212 kB
/** * @react-router/dev v7.10.1 * * Copyright (c) Remix Software Inc. * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */ "use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // vite.ts var vite_exports = {}; __export(vite_exports, { reactRouter: () => reactRouterVitePlugin, unstable_reactRouterRSC: () => reactRouterRSCVitePlugin }); module.exports = __toCommonJS(vite_exports); // vite/plugin.ts var import_node_crypto = require("crypto"); var import_node_fs2 = require("fs"); var import_promises2 = require("fs/promises"); var path6 = __toESM(require("path")); var url = __toESM(require("url")); var babel = __toESM(require("@babel/core")); var import_node_fetch_server2 = require("@remix-run/node-fetch-server"); var import_react_router2 = require("react-router"); var import_es_module_lexer = require("es-module-lexer"); var import_pick3 = __toESM(require("lodash/pick")); var import_jsesc = __toESM(require("jsesc")); var import_picocolors4 = __toESM(require("picocolors")); var import_kebabCase = __toESM(require("lodash/kebabCase")); // typegen/index.ts var import_promises = __toESM(require("fs/promises")); var Path4 = __toESM(require("pathe")); var import_picocolors2 = require("picocolors"); // config/config.ts var import_node_fs = __toESM(require("fs")); var import_node_child_process = require("child_process"); // vite/vite.ts var import_pathe2 = __toESM(require("pathe")); // invariant.ts function invariant(value, message) { if (value === false || value === null || typeof value === "undefined") { console.error( "The following error is a bug in React Router; please open an issue! https://github.com/remix-run/react-router/issues/new/choose" ); throw new Error(message); } } // config/is-react-router-repo.ts var import_pathe = __toESM(require("pathe")); function isReactRouterRepo() { let serverRuntimePath = import_pathe.default.dirname( require.resolve("@react-router/node/package.json") ); let serverRuntimeParentDir = import_pathe.default.basename( import_pathe.default.resolve(serverRuntimePath, "..") ); return serverRuntimeParentDir === "packages"; } // vite/vite.ts var vite; var viteImportSpecifier = isReactRouterRepo() ? ( // Support testing against different versions of Vite by ensuring that Vite // is resolved from the current working directory when running within this // repo. If we don't do this, Vite will always be imported relative to this // file, which means that it will always resolve to Vite 6. `file:///${import_pathe2.default.normalize( require.resolve("vite/package.json", { paths: [process.cwd()] }) ).replace("package.json", "dist/node/index.js")}` ) : "vite"; async function preloadVite() { vite = await import(viteImportSpecifier); } function getVite() { invariant(vite, "getVite() called before preloadVite()"); return vite; } // vite/ssr-externals.ts var ssrExternals = isReactRouterRepo() ? [ // This is only needed within this repo because these packages // are linked to a directory outside of node_modules so Vite // treats them as internal code by default. "react-router", "react-router-dom", "@react-router/architect", "@react-router/cloudflare", "@react-router/dev", "@react-router/express", "@react-router/node", "@react-router/serve" ] : void 0; // vite/vite-node.ts async function createContext({ root, mode, customLogger }) { await preloadVite(); const vite2 = getVite(); const [{ ViteNodeServer }, { ViteNodeRunner }, { installSourcemapsSupport }] = await Promise.all([ import("vite-node/server"), import("vite-node/client"), import("vite-node/source-map") ]); const devServer = await vite2.createServer({ root, mode, customLogger, server: { preTransformRequests: false, hmr: false, watch: null }, ssr: { external: ssrExternals }, optimizeDeps: { noDiscovery: true }, css: { // This empty PostCSS config object prevents the PostCSS config file from // being loaded. We don't need it in a React Router config context, and // there's also an issue in Vite 5 when using a .ts PostCSS config file in // an ESM project: https://github.com/vitejs/vite/issues/15869. Consumers // can work around this in their own Vite config file, but they can't // configure this internal usage of vite-node. postcss: {} }, configFile: false, envFile: false, plugins: [] }); await devServer.pluginContainer.buildStart({}); const server = new ViteNodeServer(devServer); installSourcemapsSupport({ getSourceMap: (source) => server.getSourceMap(source) }); const runner = new ViteNodeRunner({ root: devServer.config.root, base: devServer.config.base, fetchModule(id) { return server.fetchModule(id); }, resolveId(id, importer) { return server.resolveId(id, importer); } }); return { devServer, server, runner }; } // config/config.ts var import_pathe3 = __toESM(require("pathe")); var import_chokidar = __toESM(require("chokidar")); var import_picocolors = __toESM(require("picocolors")); var import_pick2 = __toESM(require("lodash/pick")); var import_omit = __toESM(require("lodash/omit")); var import_cloneDeep = __toESM(require("lodash/cloneDeep")); var import_isEqual = __toESM(require("lodash/isEqual")); // config/routes.ts var Path = __toESM(require("pathe")); var v = __toESM(require("valibot")); var import_pick = __toESM(require("lodash/pick")); function setAppDirectory(directory) { globalThis.__reactRouterAppDirectory = directory; } var routeConfigEntrySchema = v.pipe( v.custom((value) => { return !(typeof value === "object" && value !== null && "then" in value && "catch" in value); }, "Invalid type: Expected object but received a promise. Did you forget to await?"), v.object({ id: v.optional( v.pipe( v.string(), v.notValue("root", "A route cannot use the reserved id 'root'.") ) ), path: v.optional(v.string()), index: v.optional(v.boolean()), caseSensitive: v.optional(v.boolean()), file: v.string(), children: v.optional(v.array(v.lazy(() => routeConfigEntrySchema))) }) ); var resolvedRouteConfigSchema = v.array(routeConfigEntrySchema); function validateRouteConfig({ routeConfigFile, routeConfig }) { if (!routeConfig) { return { valid: false, message: `Route config must be the default export in "${routeConfigFile}".` }; } if (!Array.isArray(routeConfig)) { return { valid: false, message: `Route config in "${routeConfigFile}" must be an array.` }; } let { issues } = v.safeParse(resolvedRouteConfigSchema, routeConfig); if (issues?.length) { let { root, nested } = v.flatten(issues); return { valid: false, message: [ `Route config in "${routeConfigFile}" is invalid.`, root ? `${root}` : [], nested ? Object.entries(nested).map( ([path9, message]) => `Path: routes.${path9} ${message}` ) : [] ].flat().join("\n\n") }; } return { valid: true, routeConfig }; } function configRoutesToRouteManifest(appDirectory, routes) { let routeManifest = {}; function walk(route, parentId) { let id = route.id || createRouteId(route.file); let manifestItem = { id, parentId, file: Path.isAbsolute(route.file) ? Path.relative(appDirectory, route.file) : route.file, path: route.path, index: route.index, caseSensitive: route.caseSensitive }; if (routeManifest.hasOwnProperty(id)) { throw new Error( `Unable to define routes with duplicate route id: "${id}"` ); } routeManifest[id] = manifestItem; if (route.children) { for (let child of route.children) { walk(child, id); } } } for (let route of routes) { walk(route); } return routeManifest; } function createRouteId(file) { return Path.normalize(stripFileExtension(file)); } function stripFileExtension(file) { return file.replace(/\.[a-z0-9]+$/i, ""); } // cli/detectPackageManager.ts var detectPackageManager = () => { let { npm_config_user_agent } = process.env; if (!npm_config_user_agent) return void 0; try { let pkgManager = npm_config_user_agent.split("/")[0]; if (pkgManager === "npm") return "npm"; if (pkgManager === "pnpm") return "pnpm"; if (pkgManager === "yarn") return "yarn"; if (pkgManager === "bun") return "bun"; return void 0; } catch { return void 0; } }; // config/config.ts var excludedConfigPresetKeys = ["presets"]; var branchRouteProperties = [ "id", "path", "file", "index" ]; var configRouteToBranchRoute = (configRoute) => (0, import_pick2.default)(configRoute, branchRouteProperties); var mergeReactRouterConfig = (...configs) => { let reducer = (configA, configB) => { let mergeRequired = (key) => configA[key] !== void 0 && configB[key] !== void 0; return { ...configA, ...configB, ...mergeRequired("buildEnd") ? { buildEnd: async (...args) => { await Promise.all([ configA.buildEnd?.(...args), configB.buildEnd?.(...args) ]); } } : {}, ...mergeRequired("future") ? { future: { ...configA.future, ...configB.future } } : {}, ...mergeRequired("presets") ? { presets: [...configA.presets ?? [], ...configB.presets ?? []] } : {} }; }; return configs.reduce(reducer, {}); }; var deepFreeze = (o) => { Object.freeze(o); let oIsFunction = typeof o === "function"; let hasOwnProp = Object.prototype.hasOwnProperty; Object.getOwnPropertyNames(o).forEach(function(prop) { if (hasOwnProp.call(o, prop) && (oIsFunction ? prop !== "caller" && prop !== "callee" && prop !== "arguments" : true) && o[prop] !== null && (typeof o[prop] === "object" || typeof o[prop] === "function") && !Object.isFrozen(o[prop])) { deepFreeze(o[prop]); } }); return o; }; function ok(value) { return { ok: true, value }; } function err(error) { return { ok: false, error }; } async function resolveConfig({ root, viteNodeContext, reactRouterConfigFile, skipRoutes, validateConfig }) { let reactRouterUserConfig = {}; if (reactRouterConfigFile) { try { if (!import_node_fs.default.existsSync(reactRouterConfigFile)) { return err(`${reactRouterConfigFile} no longer exists`); } let configModule = await viteNodeContext.runner.executeFile( reactRouterConfigFile ); if (configModule.default === void 0) { return err(`${reactRouterConfigFile} must provide a default export`); } if (typeof configModule.default !== "object") { return err(`${reactRouterConfigFile} must export a config`); } reactRouterUserConfig = configModule.default; if (validateConfig) { const error = validateConfig(reactRouterUserConfig); if (error) { return err(error); } } } catch (error) { return err(`Error loading ${reactRouterConfigFile}: ${error}`); } } reactRouterUserConfig = deepFreeze((0, import_cloneDeep.default)(reactRouterUserConfig)); let presets = (await Promise.all( (reactRouterUserConfig.presets ?? []).map(async (preset) => { if (!preset.name) { throw new Error( "React Router presets must have a `name` property defined." ); } if (!preset.reactRouterConfig) { return null; } let configPreset = (0, import_omit.default)( await preset.reactRouterConfig({ reactRouterUserConfig }), excludedConfigPresetKeys ); return configPreset; }) )).filter(function isNotNull(value) { return value !== null; }); let defaults = { basename: "/", buildDirectory: "build", serverBuildFile: "index.js", serverModuleFormat: "esm", ssr: true }; let userAndPresetConfigs = mergeReactRouterConfig( ...presets, reactRouterUserConfig ); let { appDirectory: userAppDirectory, basename: basename3, buildDirectory: userBuildDirectory, buildEnd, prerender, routeDiscovery: userRouteDiscovery, serverBuildFile, serverBundles, serverModuleFormat, ssr } = { ...defaults, // Default values should be completely overridden by user/preset config, not merged ...userAndPresetConfigs }; if (!ssr && serverBundles) { serverBundles = void 0; } if (prerender) { let isValidPrerenderPathsConfig = (p) => typeof p === "boolean" || typeof p === "function" || Array.isArray(p); let isValidPrerenderConfig = isValidPrerenderPathsConfig(prerender) || typeof prerender === "object" && "paths" in prerender && isValidPrerenderPathsConfig(prerender.paths); if (!isValidPrerenderConfig) { return err( "The `prerender`/`prerender.paths` config must be a boolean, an array of string paths, or a function returning a boolean or array of string paths." ); } let isValidConcurrencyConfig = typeof prerender != "object" || !("unstable_concurrency" in prerender) || typeof prerender.unstable_concurrency === "number" && Number.isInteger(prerender.unstable_concurrency) && prerender.unstable_concurrency > 0; if (!isValidConcurrencyConfig) { return err( "The `prerender.unstable_concurrency` config must be a positive integer if specified." ); } } let routeDiscovery; if (userRouteDiscovery == null) { if (ssr) { routeDiscovery = { mode: "lazy", manifestPath: "/__manifest" }; } else { routeDiscovery = { mode: "initial" }; } } else if (userRouteDiscovery.mode === "initial") { routeDiscovery = userRouteDiscovery; } else if (userRouteDiscovery.mode === "lazy") { if (!ssr) { return err( 'The `routeDiscovery.mode` config cannot be set to "lazy" when setting `ssr:false`' ); } let { manifestPath } = userRouteDiscovery; if (manifestPath != null && !manifestPath.startsWith("/")) { return err( 'The `routeDiscovery.manifestPath` config must be a root-relative pathname beginning with a slash (i.e., "/__manifest")' ); } routeDiscovery = userRouteDiscovery; } let appDirectory = import_pathe3.default.resolve(root, userAppDirectory || "app"); let buildDirectory = import_pathe3.default.resolve(root, userBuildDirectory); let rootRouteFile = findEntry(appDirectory, "root", { absolute: true }); if (!rootRouteFile) { let rootRouteDisplayPath = import_pathe3.default.relative( root, import_pathe3.default.join(appDirectory, "root.tsx") ); return err( `Could not find a root route module in the app directory as "${rootRouteDisplayPath}"` ); } let routes; let routeConfig = []; if (skipRoutes) { routes = {}; } else { let routeConfigFile = findEntry(appDirectory, "routes"); try { if (!routeConfigFile) { let routeConfigDisplayPath = import_pathe3.default.relative( root, import_pathe3.default.join(appDirectory, "routes.ts") ); return err( `Route config file not found at "${routeConfigDisplayPath}".` ); } setAppDirectory(appDirectory); let routeConfigExport = (await viteNodeContext.runner.executeFile( import_pathe3.default.join(appDirectory, routeConfigFile) )).default; let result = validateRouteConfig({ routeConfigFile, routeConfig: await routeConfigExport }); if (!result.valid) { return err(result.message); } routeConfig = [ { id: "root", path: "", file: import_pathe3.default.relative(appDirectory, rootRouteFile), children: result.routeConfig } ]; routes = configRoutesToRouteManifest(appDirectory, routeConfig); } catch (error) { return err( [ import_picocolors.default.red(`Route config in "${routeConfigFile}" is invalid.`), "", error.loc?.file && error.loc?.column && error.frame ? [ import_pathe3.default.relative(appDirectory, error.loc.file) + ":" + error.loc.line + ":" + error.loc.column, error.frame.trim?.() ] : error.stack ].flat().join("\n") ); } } let futureConfig = userAndPresetConfigs.future; if (futureConfig?.unstable_splitRouteModules !== void 0) { return err( 'The "future.unstable_splitRouteModules" flag has been stabilized as "future.v8_splitRouteModules"' ); } if (futureConfig?.unstable_viteEnvironmentApi !== void 0) { return err( 'The "future.unstable_viteEnvironmentApi" flag has been stabilized as "future.v8_viteEnvironmentApi"' ); } let future = { unstable_optimizeDeps: userAndPresetConfigs.future?.unstable_optimizeDeps ?? false, unstable_subResourceIntegrity: userAndPresetConfigs.future?.unstable_subResourceIntegrity ?? false, v8_middleware: userAndPresetConfigs.future?.v8_middleware ?? false, v8_splitRouteModules: userAndPresetConfigs.future?.v8_splitRouteModules ?? false, v8_viteEnvironmentApi: userAndPresetConfigs.future?.v8_viteEnvironmentApi ?? false }; let reactRouterConfig = deepFreeze({ appDirectory, basename: basename3, buildDirectory, buildEnd, future, prerender, routes, routeDiscovery, serverBuildFile, serverBundles, serverModuleFormat, ssr, unstable_routeConfig: routeConfig }); for (let preset of reactRouterUserConfig.presets ?? []) { await preset.reactRouterConfigResolved?.({ reactRouterConfig }); } return ok(reactRouterConfig); } async function createConfigLoader({ rootDirectory: root, watch: watch2, mode, skipRoutes, validateConfig }) { root = import_pathe3.default.normalize(root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd()); let vite2 = await import("vite"); let viteNodeContext = await createContext({ root, mode, // Filter out any info level logs from vite-node customLogger: vite2.createLogger("warn", { prefix: "[react-router]" }) }); let reactRouterConfigFile; let updateReactRouterConfigFile = () => { reactRouterConfigFile = findEntry(root, "react-router.config", { absolute: true }); }; updateReactRouterConfigFile(); let getConfig = () => resolveConfig({ root, viteNodeContext, reactRouterConfigFile, skipRoutes, validateConfig }); let appDirectory; let initialConfigResult = await getConfig(); if (!initialConfigResult.ok) { throw new Error(initialConfigResult.error); } appDirectory = import_pathe3.default.normalize(initialConfigResult.value.appDirectory); let currentConfig = initialConfigResult.value; let fsWatcher; let changeHandlers = []; return { getConfig, onChange: (handler) => { if (!watch2) { throw new Error( "onChange is not supported when watch mode is disabled" ); } changeHandlers.push(handler); if (!fsWatcher) { fsWatcher = import_chokidar.default.watch([root, appDirectory], { ignoreInitial: true, ignored: (path9) => { let dirname5 = import_pathe3.default.dirname(path9); return !dirname5.startsWith(appDirectory) && // Ensure we're only watching files outside of the app directory // that are at the root level, not nested in subdirectories path9 !== root && // Watch the root directory itself dirname5 !== root; } }); fsWatcher.on("all", async (...args) => { let [event, rawFilepath] = args; let filepath = import_pathe3.default.normalize(rawFilepath); let fileAddedOrRemoved = event === "add" || event === "unlink"; let appFileAddedOrRemoved = fileAddedOrRemoved && filepath.startsWith(import_pathe3.default.normalize(appDirectory)); let rootRelativeFilepath = import_pathe3.default.relative(root, filepath); let configFileAddedOrRemoved = fileAddedOrRemoved && isEntryFile("react-router.config", rootRelativeFilepath); if (configFileAddedOrRemoved) { updateReactRouterConfigFile(); } let moduleGraphChanged = configFileAddedOrRemoved || Boolean( viteNodeContext.devServer?.moduleGraph.getModuleById(filepath) ); if (!moduleGraphChanged && !appFileAddedOrRemoved) { return; } viteNodeContext.devServer?.moduleGraph.invalidateAll(); viteNodeContext.runner?.moduleCache.clear(); let result = await getConfig(); let prevAppDirectory = appDirectory; appDirectory = import_pathe3.default.normalize( (result.value ?? currentConfig).appDirectory ); if (appDirectory !== prevAppDirectory) { fsWatcher.unwatch(prevAppDirectory); fsWatcher.add(appDirectory); } let configCodeChanged = configFileAddedOrRemoved || reactRouterConfigFile !== void 0 && isEntryFileDependency( viteNodeContext.devServer.moduleGraph, reactRouterConfigFile, filepath ); let routeConfigFile = !skipRoutes ? findEntry(appDirectory, "routes", { absolute: true }) : void 0; let routeConfigCodeChanged = routeConfigFile !== void 0 && isEntryFileDependency( viteNodeContext.devServer.moduleGraph, routeConfigFile, filepath ); let configChanged = result.ok && !(0, import_isEqual.default)(omitRoutes(currentConfig), omitRoutes(result.value)); let routeConfigChanged = result.ok && !(0, import_isEqual.default)(currentConfig?.routes, result.value.routes); for (let handler2 of changeHandlers) { handler2({ result, configCodeChanged, routeConfigCodeChanged, configChanged, routeConfigChanged, path: filepath, event }); } if (result.ok) { currentConfig = result.value; } }); } return () => { changeHandlers = changeHandlers.filter( (changeHandler) => changeHandler !== handler ); }; }, close: async () => { changeHandlers = []; await viteNodeContext.devServer.close(); await fsWatcher?.close(); } }; } async function resolveEntryFiles({ rootDirectory, reactRouterConfig }) { let { appDirectory } = reactRouterConfig; let defaultsDirectory = import_pathe3.default.resolve( import_pathe3.default.dirname(require.resolve("@react-router/dev/package.json")), "dist", "config", "defaults" ); let userEntryClientFile = findEntry(appDirectory, "entry.client"); let userEntryServerFile = findEntry(appDirectory, "entry.server"); let entryServerFile; let entryClientFile = userEntryClientFile || "entry.client.tsx"; if (userEntryServerFile) { entryServerFile = userEntryServerFile; } else { let packageJsonPath = findEntry(rootDirectory, "package", { extensions: [".json"], absolute: true, walkParents: true }); if (!packageJsonPath) { throw new Error( `Could not find package.json in ${rootDirectory} or any of its parent directories. Please add a package.json, or provide a custom entry.server.tsx/jsx file in your app directory.` ); } let { readPackageJSON, sortPackage, updatePackage } = await import("pkg-types"); let packageJsonDirectory = import_pathe3.default.dirname(packageJsonPath); let pkgJson = await readPackageJSON(packageJsonDirectory); let deps = pkgJson.dependencies ?? {}; if (!deps["@react-router/node"]) { throw new Error( `Could not determine server runtime. Please install @react-router/node, or provide a custom entry.server.tsx/jsx file in your app directory.` ); } if (!deps["isbot"]) { console.log( "adding `isbot@5` to your package.json, you should commit this change" ); await updatePackage(packageJsonPath, (pkg) => { pkg.dependencies ??= {}; pkg.dependencies.isbot = "^5"; sortPackage(pkg); }); let packageManager = detectPackageManager() ?? "npm"; (0, import_node_child_process.execSync)(`${packageManager} install`, { cwd: packageJsonDirectory, stdio: "inherit" }); } entryServerFile = `entry.server.node.tsx`; } let entryClientFilePath = userEntryClientFile ? import_pathe3.default.resolve(reactRouterConfig.appDirectory, userEntryClientFile) : import_pathe3.default.resolve(defaultsDirectory, entryClientFile); let entryServerFilePath = userEntryServerFile ? import_pathe3.default.resolve(reactRouterConfig.appDirectory, userEntryServerFile) : import_pathe3.default.resolve(defaultsDirectory, entryServerFile); return { entryClientFilePath, entryServerFilePath }; } function omitRoutes(config) { return { ...config, routes: {} }; } var entryExts = [".js", ".jsx", ".ts", ".tsx", ".mjs", ".mts"]; function isEntryFile(entryBasename, filename2) { return entryExts.some((ext) => filename2 === `${entryBasename}${ext}`); } function findEntry(dir, basename3, options) { let currentDir = import_pathe3.default.resolve(dir); let { root } = import_pathe3.default.parse(currentDir); while (true) { for (let ext of options?.extensions ?? entryExts) { let file = import_pathe3.default.resolve(currentDir, basename3 + ext); if (import_node_fs.default.existsSync(file)) { return options?.absolute ?? false ? file : import_pathe3.default.relative(dir, file); } } if (!options?.walkParents) { return void 0; } let parentDir = import_pathe3.default.dirname(currentDir); if (currentDir === root || parentDir === currentDir) { return void 0; } currentDir = parentDir; } } function isEntryFileDependency(moduleGraph, entryFilepath, filepath, visited = /* @__PURE__ */ new Set()) { entryFilepath = import_pathe3.default.normalize(entryFilepath); filepath = import_pathe3.default.normalize(filepath); if (visited.has(filepath)) { return false; } visited.add(filepath); if (filepath === entryFilepath) { return true; } let mod = moduleGraph.getModuleById(filepath); if (!mod) { return false; } for (let importer of mod.importers) { if (!importer.id) { continue; } if (importer.id === entryFilepath || isEntryFileDependency(moduleGraph, entryFilepath, importer.id, visited)) { return true; } } return false; } // typegen/context.ts async function createContext2({ rootDirectory, watch: watch2, mode, rsc }) { const configLoader = await createConfigLoader({ rootDirectory, mode, watch: watch2 }); const configResult = await configLoader.getConfig(); if (!configResult.ok) { throw new Error(configResult.error); } const config = configResult.value; return { configLoader, rootDirectory, config, rsc }; } // typegen/generate.ts var import_dedent = __toESM(require("dedent")); var Path3 = __toESM(require("pathe")); var Pathe = __toESM(require("pathe/utils")); // vite/babel.ts var babel_exports = {}; __export(babel_exports, { generate: () => generate, parse: () => import_parser.parse, t: () => t, traverse: () => traverse }); var import_parser = require("@babel/parser"); var t = __toESM(require("@babel/types")); var traverse = require("@babel/traverse").default; var generate = require("@babel/generator").default; // typegen/params.ts function parse2(fullpath2) { const result = {}; let segments = fullpath2.split("/"); segments.forEach((segment) => { const match = segment.match(/^:([\w-]+)(\?)?/); if (!match) return; const param = match[1]; const isRequired = match[2] === void 0; result[param] ||= isRequired; return; }); const hasSplat = segments.at(-1) === "*"; if (hasSplat) result["*"] = true; return result; } // typegen/route.ts function lineage(routes, route) { const result = []; while (route) { result.push(route); if (!route.parentId) break; route = routes[route.parentId]; } result.reverse(); return result; } function fullpath(lineage2) { const route = lineage2.at(-1); if (lineage2.length === 1 && route?.id === "root") return "/"; const isLayout = route && route.index !== true && route.path === void 0; if (isLayout) return void 0; return "/" + lineage2.map((route2) => route2.path?.replace(/^\//, "")?.replace(/\/$/, "")).filter((path9) => path9 !== void 0 && path9 !== "").join("/"); } // typegen/generate.ts function typesDirectory(ctx) { return Path3.join(ctx.rootDirectory, ".react-router/types"); } function generateFuture(ctx) { const filename2 = Path3.join(typesDirectory(ctx), "+future.ts"); const content = import_dedent.default` // Generated by React Router import "react-router"; declare module "react-router" { interface Future { v8_middleware: ${ctx.config.future.v8_middleware} } } `; return { filename: filename2, content }; } function generateServerBuild(ctx) { const filename2 = Path3.join(typesDirectory(ctx), "+server-build.d.ts"); const content = import_dedent.default` // Generated by React Router declare module "virtual:react-router/server-build" { import { ServerBuild } from "react-router"; export const assets: ServerBuild["assets"]; export const assetsBuildDirectory: ServerBuild["assetsBuildDirectory"]; export const basename: ServerBuild["basename"]; export const entry: ServerBuild["entry"]; export const future: ServerBuild["future"]; export const isSpaMode: ServerBuild["isSpaMode"]; export const prerender: ServerBuild["prerender"]; export const publicPath: ServerBuild["publicPath"]; export const routeDiscovery: ServerBuild["routeDiscovery"]; export const routes: ServerBuild["routes"]; export const ssr: ServerBuild["ssr"]; export const unstable_getCriticalCss: ServerBuild["unstable_getCriticalCss"]; } `; return { filename: filename2, content }; } var { t: t2 } = babel_exports; function generateRoutes(ctx) { const fileToRoutes = /* @__PURE__ */ new Map(); const lineages = /* @__PURE__ */ new Map(); const allPages = /* @__PURE__ */ new Set(); const routeToPages = /* @__PURE__ */ new Map(); for (const route of Object.values(ctx.config.routes)) { let routeIds = fileToRoutes.get(route.file); if (!routeIds) { routeIds = /* @__PURE__ */ new Set(); fileToRoutes.set(route.file, routeIds); } routeIds.add(route.id); const lineage2 = lineage(ctx.config.routes, route); lineages.set(route.id, lineage2); const fullpath2 = fullpath(lineage2); if (!fullpath2) continue; const pages = expand(fullpath2); pages.forEach((page) => allPages.add(page)); lineage2.forEach(({ id }) => { let routePages = routeToPages.get(id); if (!routePages) { routePages = /* @__PURE__ */ new Set(); routeToPages.set(id, routePages); } pages.forEach((page) => routePages.add(page)); }); } const routesTs = { filename: Path3.join(typesDirectory(ctx), "+routes.ts"), content: import_dedent.default` // Generated by React Router import "react-router" declare module "react-router" { interface Register { pages: Pages routeFiles: RouteFiles routeModules: RouteModules } } ` + "\n\n" + generate(pagesType(allPages)).code + "\n\n" + generate(routeFilesType({ fileToRoutes, routeToPages })).code + "\n\n" + generate(routeModulesType(ctx)).code }; const allAnnotations = Array.from(fileToRoutes.entries()).filter(([file]) => isInAppDirectory(ctx, file)).map( ([file, routeIds]) => getRouteAnnotations({ ctx, file, routeIds, lineages }) ); return [routesTs, ...allAnnotations]; } function pagesType(pages) { return t2.tsTypeAliasDeclaration( t2.identifier("Pages"), null, t2.tsTypeLiteral( Array.from(pages).map((page) => { return t2.tsPropertySignature( t2.stringLiteral(page), t2.tsTypeAnnotation( t2.tsTypeLiteral([ t2.tsPropertySignature( t2.identifier("params"), t2.tsTypeAnnotation(paramsType(page)) ) ]) ) ); }) ) ); } function routeFilesType({ fileToRoutes, routeToPages }) { return t2.tsTypeAliasDeclaration( t2.identifier("RouteFiles"), null, t2.tsTypeLiteral( Array.from(fileToRoutes).map( ([file, routeIds]) => t2.tsPropertySignature( t2.stringLiteral(file), t2.tsTypeAnnotation( t2.tsUnionType( Array.from(routeIds).map((routeId) => { const pages = routeToPages.get(routeId) ?? /* @__PURE__ */ new Set(); return t2.tsTypeLiteral([ t2.tsPropertySignature( t2.identifier("id"), t2.tsTypeAnnotation( t2.tsLiteralType(t2.stringLiteral(routeId)) ) ), t2.tsPropertySignature( t2.identifier("page"), t2.tsTypeAnnotation( pages ? t2.tsUnionType( Array.from(pages).map( (page) => t2.tsLiteralType(t2.stringLiteral(page)) ) ) : t2.tsNeverKeyword() ) ) ]); }) ) ) ) ) ) ); } function routeModulesType(ctx) { return t2.tsTypeAliasDeclaration( t2.identifier("RouteModules"), null, t2.tsTypeLiteral( Object.values(ctx.config.routes).map( (route) => t2.tsPropertySignature( t2.stringLiteral(route.id), t2.tsTypeAnnotation( isInAppDirectory(ctx, route.file) ? t2.tsTypeQuery( t2.tsImportType( t2.stringLiteral( `./${Path3.relative(ctx.rootDirectory, ctx.config.appDirectory)}/${route.file}` ) ) ) : t2.tsUnknownKeyword() ) ) ) ) ); } function isInAppDirectory(ctx, routeFile) { const path9 = Path3.resolve(ctx.config.appDirectory, routeFile); return path9.startsWith(ctx.config.appDirectory); } function getRouteAnnotations({ ctx, file, routeIds, lineages }) { const filename2 = Path3.join( typesDirectory(ctx), Path3.relative(ctx.rootDirectory, ctx.config.appDirectory), Path3.dirname(file), "+types", Pathe.filename(file) + ".ts" ); const matchesType = t2.tsTypeAliasDeclaration( t2.identifier("Matches"), null, t2.tsUnionType( Array.from(routeIds).map((routeId) => { const lineage2 = lineages.get(routeId); return t2.tsTupleType( lineage2.map( (route) => t2.tsTypeLiteral([ t2.tsPropertySignature( t2.identifier("id"), t2.tsTypeAnnotation(t2.tsLiteralType(t2.stringLiteral(route.id))) ), t2.tsPropertySignature( t2.identifier("module"), t2.tsTypeAnnotation( t2.tsTypeQuery( t2.tsImportType( t2.stringLiteral( relativeImportSource( rootDirsPath(ctx, filename2), Path3.resolve(ctx.config.appDirectory, route.file) ) ) ) ) ) ) ]) ) ); }) ) ); const routeImportSource = relativeImportSource( rootDirsPath(ctx, filename2), Path3.resolve(ctx.config.appDirectory, file) ); const content = import_dedent.default` // Generated by React Router import type { GetInfo, GetAnnotations } from "react-router/internal"; type Module = typeof import("${routeImportSource}") type Info = GetInfo<{ file: "${file}", module: Module }> ` + "\n\n" + generate(matchesType).code + "\n\n" + import_dedent.default` type Annotations = GetAnnotations<Info & { module: Module, matches: Matches }, ${ctx.rsc}>; export namespace Route { // links export type LinkDescriptors = Annotations["LinkDescriptors"]; export type LinksFunction = Annotations["LinksFunction"]; // meta export type MetaArgs = Annotations["MetaArgs"]; export type MetaDescriptors = Annotations["MetaDescriptors"]; export type MetaFunction = Annotations["MetaFunction"]; // headers export type HeadersArgs = Annotations["HeadersArgs"]; export type HeadersFunction = Annotations["HeadersFunction"]; // middleware export type MiddlewareFunction = Annotations["MiddlewareFunction"]; // clientMiddleware export type ClientMiddlewareFunction = Annotations["ClientMiddlewareFunction"]; // loader export type LoaderArgs = Annotations["LoaderArgs"]; // clientLoader export type ClientLoaderArgs = Annotations["ClientLoaderArgs"]; // action export type ActionArgs = Annotations["ActionArgs"]; // clientAction export type ClientActionArgs = Annotations["ClientActionArgs"]; // HydrateFallback export type HydrateFallbackProps = Annotations["HydrateFallbackProps"]; // Component export type ComponentProps = Annotations["ComponentProps"]; // ErrorBoundary export type ErrorBoundaryProps = Annotations["ErrorBoundaryProps"]; } `; return { filename: filename2, content }; } function relativeImportSource(from, to) { let path9 = Path3.relative(Path3.dirname(from), to); let extension = Path3.extname(path9); path9 = Path3.join(Path3.dirname(path9), Pathe.filename(path9)); if (!path9.startsWith("../")) path9 = "./" + path9; if (!extension || /\.(js|ts)x?$/.test(extension)) { extension = ".js"; } return path9 + extension; } function rootDirsPath(ctx, typesPath) { const rel = Path3.relative(typesDirectory(ctx), typesPath); return Path3.join(ctx.rootDirectory, rel); } function paramsType(path9) { const params = parse2(path9); return t2.tsTypeLiteral( Object.entries(params).map(([param, isRequired]) => { const property = t2.tsPropertySignature( t2.stringLiteral(param), t2.tsTypeAnnotation(t2.tsStringKeyword()) ); property.optional = !isRequired; return property; }) ); } function expand(fullpath2) { function recurse(segments2, index) { if (index === segments2.length) return [""]; const segment = segments2[index]; const isOptional = segment.endsWith("?"); const isDynamic = segment.startsWith(":"); const required = segment.replace(/\?$/, ""); const keep = !isOptional || isDynamic; const kept = isDynamic ? segment : required; const withoutSegment = recurse(segments2, index + 1); const withSegment = withoutSegment.map((rest) => [kept, rest].join("/")); if (keep) return withSegment; return [...withoutSegment, ...withSegment]; } const segments = fullpath2.split("/"); const expanded = /* @__PURE__ */ new Set(); for (let result of recurse(segments, 0)) { if (result !== "/") result = result.replace(/\/$/, ""); expanded.add(result); } return expanded; } // typegen/index.ts async function clearRouteModuleAnnotations(ctx) { await import_promises.default.rm( Path4.join(typesDirectory(ctx), Path4.basename(ctx.config.appDirectory)), { recursive: true, force: true } ); } async function write(...files) { return Promise.all( files.map(async ({ filename: filename2, content }) => { await import_promises.default.mkdir(Path4.dirname(filename2), { recursive: true }); await import_promises.default.writeFile(filename2, content); }) ); } async function watch(rootDirectory, { mode, logger, rsc }) { const ctx = await createContext2({ rootDirectory, mode, rsc, watch: true }); await import_promises.default.rm(typesDirectory(ctx), { recursive: true, force: true }); await write( generateFuture(ctx), generateServerBuild(ctx), ...generateRoutes(ctx) ); logger?.info((0, import_picocolors2.green)("generated types"), { timestamp: true, clear: true }); ctx.configLoader.onChange( async ({ result, configChanged, routeConfigChanged }) => { if (!result.ok) { logger?.error((0, import_picocolors2.red)(result.error), { timestamp: true, clear: true }); return; } ctx.config = result.value; if (configChanged) { await write(generateFuture(ctx)); logger?.info((0, import_picocolors2.green)("regenerated types"), { timestamp: true, clear: true }); } if (routeConfigChanged) { await clearRouteModuleAnnotations(ctx); await write(...generateRoutes(ctx)); logger?.info((0, import_picocolors2.green)("regenerated types"), { timestamp: true, clear: true }); } } ); return { close: async () => await ctx.configLoader.close() }; } // vite/node-adapter.ts var import_node_fetch_server = require("@remix-run/node-fetch-server"); function fromNodeRequest(nodeReq, nodeRes) { invariant( nodeReq.originalUrl, "Expected `nodeReq.originalUrl` to be defined" ); nodeReq.url = nodeReq.originalUrl; return (0, import_node_fetch_server.createRequest)(nodeReq, nodeRes); } // vite/styles.ts var path4 = __toESM(require("path")); var import_react_router = require("react-router"); // vite/resolve-file-url.ts var path3 = __toESM(require("path")); var resolveFileUrl = ({ rootDirectory }, filePath) => { let vite2 = getVite(); let relativePath = path3.relative(rootDirectory, filePath); let isWithinRoot = !relativePath.startsWith("..") && !path3.isAbsolute(relativePath); if (!isWithinRoot) { return path3.posix.join("/@fs", vite2.normalizePath(filePath)); } return "/" + vite2.normalizePath(relativePath); }; // vite/styles.ts var cssFileRegExp = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/; var cssModulesRegExp = new RegExp(`\\.module${cssFileRegExp.source}`); var isCssFile = (file) => cssFileRegExp.test(file); var isCssModulesFile = (file) => cssModulesRegExp.test(file); var cssUrlParamsWithoutSideEffects = ["url", "inline", "raw", "inline-css"]; var isCssUrlWithoutSideEffects = (url2) => { let queryString = url2.split("?")[1]; if (!queryString) { return false; } let params = new URLSearchParams(queryString); for (let paramWithoutSideEffects of cssUrlParamsWithoutSideEffects) { if ( // Parameter is blank and not explicitly set, i.e. "?url", not "?url=" params.get(paramWithoutSideEffects) === "" && !url2.includes(`?${paramWithoutSideEffects}=`) && !url2.includes(`&${paramWithoutSideEffects}=`) ) { return true; } } return false; }; var getStylesForFiles = async ({ viteDevServer, rootDirectory, loadCssContents, files }) => { let styles = {}; let deps = /* @__PURE__ */ new Set(); try { for (let file of files) { let normalizedPath = path4.resolve(rootDirectory, file).replace(/\\/g, "/"); let node = await viteDevServer.moduleGraph.getModuleById(normalizedPath); if (!node) { try { await viteDevServer.transformRequest( resolveFileUrl({ rootDirectory }, normalizedPath) ); } catch (err2) { console.error(err2); } node = await viteDevServer.moduleGraph.getModuleById(normalizedPath); } if (!node) { console.log(`Could not resolve module for file: ${file}`); continue; } await findDeps(viteDevServer, node, deps); } } catch (err2) { console.error(err2); } for (let dep of deps) { if (dep.file && isCssFile(dep.file) && !isCssUrlWithoutSideEffects(dep.url)) { try { styles[dep.url] = await loadCssContents(viteDevServer, dep); } catch { console.warn(`Failed to load CSS for ${dep.file}`); } } } return Object.entries(styles).map(([fileName, css], i) => [ ` /* ${fileName.replace(/\/\*/g, "/\\*").replace(/\*\//g, "*\\/")} */`, css ]).flat().join("\n") || void 0; }; var findDeps = async (vite2, node, deps) => { let branches = []; async function addFromNode(node2) { if (!deps.has(node2)) { deps.add(node2); await findDeps(vite2, node2, deps); } } async function addFromUrl(url2) { let node2 = await vite2.moduleGraph.getModuleByUrl(url2); if (node2) { await addFromNode(node2); } } if (node.ssrTransformResult) { if (node.ssrTransformResult.deps) { node.ssrTransformResult.deps.forEach( (url2) => branches.push(addFromUrl(url2)) ); } } else { node.importedModules.forEach((node2) => branches.push(addFromNode(node2))); } await Promise.all(branches); }; var groupRoutesByParentId = (manifest) => { let routes = {}; Object.values(manifest).forEach((route) => { if (route) { let parentId = route.parentId || ""; if (!routes[parentId]) { routes[parentId] = []; } routes[parentId].push(route); } }); return routes; }; var createRoutesWithChildren = (manifest, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) => { return (routesByParentId[parentId] || []).map((route) => ({ ...route, ...route.index ? { index: true } : { index: false, children: createRoutesWithChildren( manifest, route.id, routesByParentId ) } })); }; var getStylesForPathname = async ({ viteDevServer, rootDirectory, reactRouterConfig, entryClientFilePath, loadCssContents, pathname }) => { if (pathname === void 0 || pathname.includes("?_data=")) { return void 0; } let routesWithChildren = createRoutesWithChildren(reactRouterConfig.routes); let appPath = path4.relative(process.cwd(), reactRouterConfig.appDirectory); let documentRouteFiles = (0, import_react_router.matchRoutes)(routesWithChildren, pathname, reactRouterConfig.basename)?.map( (match) => path4.resolve(appPath, reactRouterConfig.routes[match.route.id].file) ) ?? []; let styles = await getStylesForFiles({ viteDevServer, rootDirectory, loadCssContents, files: [ // Always include the client entry file when crawling the module graph for CSS path4.relative(rootDirectory, entryClientFilePath), // Then include any styles from the matched routes ...documentRouteFiles ] }); return styles; }; var getCssStringFromViteDevModuleCode = (code) => { let cssContent = void 0; const ast = import_parser.parse(code, { sourceType: "module" }); traverse(ast, { VariableDeclaration(path9) { const declaration = path9.node.declarations[0]; if (declaration?.id?.type === "Identifier" && declaration.id.name === "__vite__css" && declaration.init?.type === "StringLiteral") { cssContent = declaration.init.value; path9.stop(); } } }); return cssContent; }; // vite/virtual-module.ts function create(name) { let id = `virtual:react-router/${name}`; return { id, resolvedId: `\0${id}`, url: `/@id/__x00__${id}` }; } // vite/resolve-relative-route-file-path.ts var import_pathe4 = __toESM(require("pathe")); function resolveRelativeRouteFilePath(route, reactRouterConfig) { let vite2 = getVite(); let file = route.file; let fullPath = import_pathe4.default.resolve(reactRouterConfig.appDirectory, file); return vite2.normalizePath(fullPath); } // vite/combine-urls.ts function combineURLs(baseURL, relativeURL) { return relativeURL ? baseURL.replace(/\/+$/, "") + "/" + relativeURL.replace(/^\/+/, "") : baseURL; } // vite/remove-exports.ts var impo