@react-router/dev
Version:
Dev tools and CLI for React Router
1,516 lines (1,467 loc) • 212 kB
JavaScript
/**
* @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