@react-router/dev 7.1.5 → 7.2.0-pre.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vite.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v7.1.5
2
+ * @react-router/dev v7.2.0-pre.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -52,11 +52,14 @@ var fse = __toESM(require("fs-extra"));
52
52
  var babel = __toESM(require("@babel/core"));
53
53
  var import_react_router2 = require("react-router");
54
54
  var import_es_module_lexer = require("es-module-lexer");
55
+ var import_pick3 = __toESM(require("lodash/pick"));
55
56
  var import_jsesc = __toESM(require("jsesc"));
56
57
  var import_picocolors3 = __toESM(require("picocolors"));
58
+ var import_kebabCase = __toESM(require("lodash/kebabCase"));
57
59
 
58
60
  // typegen/index.ts
59
61
  var import_node_fs2 = __toESM(require("fs"));
62
+ var import_dedent2 = __toESM(require("dedent"));
60
63
  var Path4 = __toESM(require("pathe"));
61
64
  var import_picocolors2 = __toESM(require("picocolors"));
62
65
 
@@ -270,6 +273,13 @@ var detectPackageManager = () => {
270
273
 
271
274
  // config/config.ts
272
275
  var excludedConfigPresetKeys = ["presets"];
276
+ var branchRouteProperties = [
277
+ "id",
278
+ "path",
279
+ "file",
280
+ "index"
281
+ ];
282
+ var configRouteToBranchRoute = (configRoute) => (0, import_pick2.default)(configRoute, branchRouteProperties);
273
283
  var mergeReactRouterConfig = (...configs) => {
274
284
  let reducer = (configA, configB) => {
275
285
  let mergeRequired = (key) => configA[key] !== void 0 && configB[key] !== void 0;
@@ -443,7 +453,9 @@ async function resolveConfig({
443
453
  );
444
454
  }
445
455
  let future = {
446
- unstable_optimizeDeps: reactRouterUserConfig.future?.unstable_optimizeDeps ?? false
456
+ unstable_optimizeDeps: reactRouterUserConfig.future?.unstable_optimizeDeps ?? false,
457
+ unstable_splitRouteModules: reactRouterUserConfig.future?.unstable_splitRouteModules ?? false,
458
+ unstable_viteEnvironmentApi: reactRouterUserConfig.future?.unstable_viteEnvironmentApi ?? false
447
459
  };
448
460
  let reactRouterConfig = deepFreeze({
449
461
  appDirectory,
@@ -622,6 +634,19 @@ function findEntry(dir, basename2, options) {
622
634
  return void 0;
623
635
  }
624
636
 
637
+ // vite/babel.ts
638
+ var babel_exports = {};
639
+ __export(babel_exports, {
640
+ generate: () => generate,
641
+ parse: () => import_parser.parse,
642
+ t: () => t,
643
+ traverse: () => traverse
644
+ });
645
+ var import_parser = require("@babel/parser");
646
+ var t = __toESM(require("@babel/types"));
647
+ var traverse = require("@babel/traverse").default;
648
+ var generate = require("@babel/generator").default;
649
+
625
650
  // typegen/generate.ts
626
651
  var import_dedent = __toESM(require("dedent"));
627
652
  var Path3 = __toESM(require("pathe"));
@@ -642,12 +667,45 @@ function getTypesPath(ctx, route) {
642
667
  );
643
668
  }
644
669
 
670
+ // typegen/params.ts
671
+ function parse2(fullpath2) {
672
+ const result = {};
673
+ let segments = fullpath2.split("/");
674
+ segments.forEach((segment) => {
675
+ const match = segment.match(/^:([\w-]+)(\?)?/);
676
+ if (!match) return;
677
+ const param = match[1];
678
+ const isRequired = match[2] === void 0;
679
+ result[param] ||= isRequired;
680
+ return;
681
+ });
682
+ const hasSplat = segments.at(-1) === "*";
683
+ if (hasSplat) result["*"] = true;
684
+ return result;
685
+ }
686
+
687
+ // typegen/route.ts
688
+ function lineage(routes, route) {
689
+ const result = [];
690
+ while (route) {
691
+ result.push(route);
692
+ if (!route.parentId) break;
693
+ route = routes[route.parentId];
694
+ }
695
+ result.reverse();
696
+ return result;
697
+ }
698
+ function fullpath(lineage2) {
699
+ if (lineage2.length === 1 && lineage2[0].id === "root") return "/";
700
+ return "/" + lineage2.map((route) => route.path?.replace(/^\//, "")?.replace(/\/$/, "")).filter((path7) => path7 !== void 0 && path7 !== "").join("/");
701
+ }
702
+
645
703
  // typegen/generate.ts
646
- function generate(ctx, route) {
647
- const lineage = getRouteLineage(ctx.config.routes, route);
648
- const urlpath = lineage.map((route2) => route2.path).join("/");
704
+ function generate2(ctx, route) {
705
+ const lineage2 = lineage(ctx.config.routes, route);
706
+ const fullpath2 = fullpath(lineage2);
649
707
  const typesPath = getTypesPath(ctx, route);
650
- const parents = lineage.slice(0, -1);
708
+ const parents = lineage2.slice(0, -1);
651
709
  const parentTypeImports = parents.map((parent, i) => {
652
710
  const rel = Path3.relative(
653
711
  Path3.dirname(typesPath),
@@ -674,7 +732,7 @@ function generate(ctx, route) {
674
732
  file: "${route.file}"
675
733
  path: "${route.path}"
676
734
  params: {${formatParamProperties(
677
- urlpath
735
+ fullpath2
678
736
  )}} & { [key: string]: string | undefined }
679
737
  module: Module
680
738
  loaderData: T.CreateLoaderData<Module>
@@ -704,46 +762,13 @@ function generate(ctx, route) {
704
762
  `;
705
763
  }
706
764
  var noExtension = (path7) => Path3.join(Path3.dirname(path7), Pathe2.filename(path7));
707
- function getRouteLineage(routes, route) {
708
- const result = [];
709
- while (route) {
710
- result.push(route);
711
- if (!route.parentId) break;
712
- route = routes[route.parentId];
713
- }
714
- result.reverse();
715
- return result;
716
- }
717
- function formatParamProperties(urlpath) {
718
- const params = parseParams(urlpath);
719
- const properties = Object.entries(params).map(([name, values]) => {
720
- if (values.length === 1) {
721
- const isOptional = values[0];
722
- return isOptional ? `"${name}"?: string` : `"${name}": string`;
723
- }
724
- const items = values.map(
725
- (isOptional) => isOptional ? "string | undefined" : "string"
726
- );
727
- return `"${name}": [${items.join(", ")}]`;
728
- });
765
+ function formatParamProperties(fullpath2) {
766
+ const params = parse2(fullpath2);
767
+ const properties = Object.entries(params).map(
768
+ ([name, isRequired]) => isRequired ? `"${name}": string` : `"${name}"?: string`
769
+ );
729
770
  return properties.join("; ");
730
771
  }
731
- function parseParams(urlpath) {
732
- const result = {};
733
- let segments = urlpath.split("/");
734
- segments.forEach((segment) => {
735
- const match = segment.match(/^:([\w-]+)(\?)?/);
736
- if (!match) return;
737
- const param = match[1];
738
- const isOptional = match[2] !== void 0;
739
- result[param] ??= [];
740
- result[param].push(isOptional);
741
- return;
742
- });
743
- const hasSplat = segments.at(-1) === "*";
744
- if (hasSplat) result["*"] = [false];
745
- return result;
746
- }
747
772
 
748
773
  // typegen/index.ts
749
774
  async function watch(rootDirectory, { logger } = {}) {
@@ -789,17 +814,53 @@ async function writeAll(ctx) {
789
814
  import_node_fs2.default.rmSync(typegenDir, { recursive: true, force: true });
790
815
  Object.values(ctx.config.routes).forEach((route) => {
791
816
  const typesPath = getTypesPath(ctx, route);
792
- const content = generate(ctx, route);
817
+ const content = generate2(ctx, route);
793
818
  import_node_fs2.default.mkdirSync(Path4.dirname(typesPath), { recursive: true });
794
819
  import_node_fs2.default.writeFileSync(typesPath, content);
795
820
  });
821
+ const registerPath = Path4.join(typegenDir, "+register.ts");
822
+ import_node_fs2.default.writeFileSync(registerPath, register(ctx));
796
823
  }
824
+ function register(ctx) {
825
+ const register2 = import_dedent2.default`
826
+ import "react-router";
797
827
 
798
- // vite/babel.ts
799
- var import_parser = require("@babel/parser");
800
- var t = __toESM(require("@babel/types"));
801
- var traverse = require("@babel/traverse").default;
802
- var generate2 = require("@babel/generator").default;
828
+ declare module "react-router" {
829
+ interface Register {
830
+ params: Params;
831
+ }
832
+ }
833
+ `;
834
+ const { t: t2 } = babel_exports;
835
+ const typeParams = t2.tsTypeAliasDeclaration(
836
+ t2.identifier("Params"),
837
+ null,
838
+ t2.tsTypeLiteral(
839
+ Object.values(ctx.config.routes).map((route) => {
840
+ if (route.id !== "root" && !route.path) return void 0;
841
+ const lineage2 = lineage(ctx.config.routes, route);
842
+ const fullpath2 = fullpath(lineage2);
843
+ const params = parse2(fullpath2);
844
+ return t2.tsPropertySignature(
845
+ t2.stringLiteral(fullpath2),
846
+ t2.tsTypeAnnotation(
847
+ t2.tsTypeLiteral(
848
+ Object.entries(params).map(([param, isRequired]) => {
849
+ const property = t2.tsPropertySignature(
850
+ t2.stringLiteral(param),
851
+ t2.tsTypeAnnotation(t2.tsStringKeyword())
852
+ );
853
+ property.optional = !isRequired;
854
+ return property;
855
+ })
856
+ )
857
+ )
858
+ );
859
+ }).filter((x) => x !== void 0)
860
+ )
861
+ );
862
+ return [register2, generate(typeParams).code].join("\n\n");
863
+ }
803
864
 
804
865
  // vite/node-adapter.ts
805
866
  var import_node_events = require("events");
@@ -1159,8 +1220,613 @@ function invalidDestructureError(name) {
1159
1220
  return new Error(`Cannot remove destructured export "${name}"`);
1160
1221
  }
1161
1222
 
1223
+ // vite/cache.ts
1224
+ function getOrSetFromCache(cache, key, version, getValue) {
1225
+ if (!cache) {
1226
+ return getValue();
1227
+ }
1228
+ let entry = cache.get(key);
1229
+ if (entry?.version === version) {
1230
+ return entry.value;
1231
+ }
1232
+ let value = getValue();
1233
+ let newEntry = { value, version };
1234
+ cache.set(key, newEntry);
1235
+ return value;
1236
+ }
1237
+
1238
+ // vite/route-chunks.ts
1239
+ function codeToAst(code, cache, cacheKey) {
1240
+ return structuredClone(
1241
+ getOrSetFromCache(
1242
+ cache,
1243
+ `${cacheKey}::codeToAst`,
1244
+ code,
1245
+ () => (0, import_parser.parse)(code, { sourceType: "module" })
1246
+ )
1247
+ );
1248
+ }
1249
+ function assertNodePath(path7) {
1250
+ invariant(
1251
+ path7 && !Array.isArray(path7),
1252
+ `Expected a Path, but got ${Array.isArray(path7) ? "an array" : path7}`
1253
+ );
1254
+ }
1255
+ function assertNodePathIsStatement(path7) {
1256
+ invariant(
1257
+ path7 && !Array.isArray(path7) && t.isStatement(path7.node),
1258
+ `Expected a Statement path, but got ${Array.isArray(path7) ? "an array" : path7?.node?.type}`
1259
+ );
1260
+ }
1261
+ function assertNodePathIsVariableDeclarator(path7) {
1262
+ invariant(
1263
+ path7 && !Array.isArray(path7) && t.isVariableDeclarator(path7.node),
1264
+ `Expected an Identifier path, but got ${Array.isArray(path7) ? "an array" : path7?.node?.type}`
1265
+ );
1266
+ }
1267
+ function assertNodePathIsPattern(path7) {
1268
+ invariant(
1269
+ path7 && !Array.isArray(path7) && t.isPattern(path7.node),
1270
+ `Expected a Pattern path, but got ${Array.isArray(path7) ? "an array" : path7?.node?.type}`
1271
+ );
1272
+ }
1273
+ function getExportDependencies(code, cache, cacheKey) {
1274
+ return getOrSetFromCache(
1275
+ cache,
1276
+ `${cacheKey}::getExportDependencies`,
1277
+ code,
1278
+ () => {
1279
+ let exportDependencies = /* @__PURE__ */ new Map();
1280
+ let ast = codeToAst(code, cache, cacheKey);
1281
+ function handleExport(exportName, exportPath, identifiersPath = exportPath) {
1282
+ let identifiers = getDependentIdentifiersForPath(identifiersPath);
1283
+ let topLevelStatements = /* @__PURE__ */ new Set([
1284
+ exportPath.node,
1285
+ ...getTopLevelStatementsForPaths(identifiers)
1286
+ ]);
1287
+ let topLevelNonModuleStatements = new Set(
1288
+ Array.from(topLevelStatements).filter(
1289
+ (statement) => !t.isImportDeclaration(statement) && !t.isExportDeclaration(statement)
1290
+ )
1291
+ );
1292
+ let importedIdentifierNames = /* @__PURE__ */ new Set();
1293
+ for (let identifier of identifiers) {
1294
+ if (identifier.parentPath.parentPath?.isImportDeclaration()) {
1295
+ importedIdentifierNames.add(identifier.node.name);
1296
+ }
1297
+ }
1298
+ let exportedVariableDeclarators = /* @__PURE__ */ new Set();
1299
+ for (let identifier of identifiers) {
1300
+ if (identifier.parentPath.isVariableDeclarator() && identifier.parentPath.parentPath.parentPath?.isExportNamedDeclaration()) {
1301
+ exportedVariableDeclarators.add(identifier.parentPath.node);
1302
+ continue;
1303
+ }
1304
+ let isWithinExportDestructuring = Boolean(
1305
+ identifier.findParent(
1306
+ (path7) => Boolean(
1307
+ path7.isPattern() && path7.parentPath?.isVariableDeclarator() && path7.parentPath.parentPath?.parentPath?.isExportNamedDeclaration()
1308
+ )
1309
+ )
1310
+ );
1311
+ if (isWithinExportDestructuring) {
1312
+ let currentPath = identifier;
1313
+ while (currentPath) {
1314
+ if (
1315
+ // Check the identifier is within a variable declaration, and if
1316
+ // so, ensure we're on the left-hand side of the expression
1317
+ // since these identifiers are what make up the export names,
1318
+ // e.g. export const { foo } = { foo: bar }; should pick up
1319
+ // `foo` but not `bar`.
1320
+ currentPath.parentPath?.isVariableDeclarator() && currentPath.parentKey === "id"
1321
+ ) {
1322
+ exportedVariableDeclarators.add(currentPath.parentPath.node);
1323
+ break;
1324
+ }
1325
+ currentPath = currentPath.parentPath;
1326
+ }
1327
+ }
1328
+ }
1329
+ let dependencies = {
1330
+ topLevelStatements,
1331
+ topLevelNonModuleStatements,
1332
+ importedIdentifierNames,
1333
+ exportedVariableDeclarators
1334
+ };
1335
+ exportDependencies.set(exportName, dependencies);
1336
+ }
1337
+ traverse(ast, {
1338
+ ExportDeclaration(exportPath) {
1339
+ let { node } = exportPath;
1340
+ if (t.isExportAllDeclaration(node)) {
1341
+ return;
1342
+ }
1343
+ if (t.isExportDefaultDeclaration(node)) {
1344
+ handleExport("default", exportPath);
1345
+ return;
1346
+ }
1347
+ let { declaration } = node;
1348
+ if (t.isVariableDeclaration(declaration)) {
1349
+ let { declarations } = declaration;
1350
+ for (let i = 0; i < declarations.length; i++) {
1351
+ let declarator = declarations[i];
1352
+ if (t.isIdentifier(declarator.id)) {
1353
+ let declaratorPath = exportPath.get(
1354
+ `declaration.declarations.${i}`
1355
+ );
1356
+ assertNodePathIsVariableDeclarator(declaratorPath);
1357
+ handleExport(declarator.id.name, exportPath, declaratorPath);
1358
+ continue;
1359
+ }
1360
+ if (t.isPattern(declarator.id)) {
1361
+ let exportedPatternPath = exportPath.get(
1362
+ `declaration.declarations.${i}.id`
1363
+ );
1364
+ assertNodePathIsPattern(exportedPatternPath);
1365
+ let identifiers = getIdentifiersForPatternPath(exportedPatternPath);
1366
+ for (let identifier of identifiers) {
1367
+ handleExport(identifier.node.name, exportPath, identifier);
1368
+ }
1369
+ }
1370
+ }
1371
+ return;
1372
+ }
1373
+ if (t.isFunctionDeclaration(declaration) || t.isClassDeclaration(declaration)) {
1374
+ invariant(
1375
+ declaration.id,
1376
+ "Expected exported function or class declaration to have a name when not the default export"
1377
+ );
1378
+ handleExport(declaration.id.name, exportPath);
1379
+ return;
1380
+ }
1381
+ if (t.isExportNamedDeclaration(node)) {
1382
+ for (let specifier of node.specifiers) {
1383
+ if (t.isIdentifier(specifier.exported)) {
1384
+ let name = specifier.exported.name;
1385
+ let specifierPath = exportPath.get("specifiers").find((path7) => path7.node === specifier);
1386
+ invariant(
1387
+ specifierPath,
1388
+ `Expected to find specifier path for ${name}`
1389
+ );
1390
+ handleExport(name, exportPath, specifierPath);
1391
+ }
1392
+ }
1393
+ return;
1394
+ }
1395
+ throw new Error(`Unknown export node type: ${node.type}`);
1396
+ }
1397
+ });
1398
+ return exportDependencies;
1399
+ }
1400
+ );
1401
+ }
1402
+ function getDependentIdentifiersForPath(path7, state) {
1403
+ let { visited, identifiers } = state ?? {
1404
+ visited: /* @__PURE__ */ new Set(),
1405
+ identifiers: /* @__PURE__ */ new Set()
1406
+ };
1407
+ if (visited.has(path7)) {
1408
+ return identifiers;
1409
+ }
1410
+ visited.add(path7);
1411
+ path7.traverse({
1412
+ Identifier(path8) {
1413
+ if (identifiers.has(path8)) {
1414
+ return;
1415
+ }
1416
+ identifiers.add(path8);
1417
+ let binding = path8.scope.getBinding(path8.node.name);
1418
+ if (!binding) {
1419
+ return;
1420
+ }
1421
+ getDependentIdentifiersForPath(binding.path, { visited, identifiers });
1422
+ for (let reference of binding.referencePaths) {
1423
+ if (reference.isExportNamedDeclaration()) {
1424
+ continue;
1425
+ }
1426
+ getDependentIdentifiersForPath(reference, {
1427
+ visited,
1428
+ identifiers
1429
+ });
1430
+ }
1431
+ for (let constantViolation of binding.constantViolations) {
1432
+ getDependentIdentifiersForPath(constantViolation, {
1433
+ visited,
1434
+ identifiers
1435
+ });
1436
+ }
1437
+ }
1438
+ });
1439
+ let topLevelStatement = getTopLevelStatementPathForPath(path7);
1440
+ let withinImportStatement = topLevelStatement.isImportDeclaration();
1441
+ let withinExportStatement = topLevelStatement.isExportDeclaration();
1442
+ if (!withinImportStatement && !withinExportStatement) {
1443
+ getDependentIdentifiersForPath(topLevelStatement, {
1444
+ visited,
1445
+ identifiers
1446
+ });
1447
+ }
1448
+ if (withinExportStatement && path7.isIdentifier() && (t.isPattern(path7.parentPath.node) || // [foo]
1449
+ t.isPattern(path7.parentPath.parentPath?.node))) {
1450
+ let variableDeclarator = path7.findParent((p) => p.isVariableDeclarator());
1451
+ assertNodePath(variableDeclarator);
1452
+ getDependentIdentifiersForPath(variableDeclarator, {
1453
+ visited,
1454
+ identifiers
1455
+ });
1456
+ }
1457
+ return identifiers;
1458
+ }
1459
+ function getTopLevelStatementPathForPath(path7) {
1460
+ let ancestry = path7.getAncestry();
1461
+ let topLevelStatement = ancestry[ancestry.length - 2];
1462
+ assertNodePathIsStatement(topLevelStatement);
1463
+ return topLevelStatement;
1464
+ }
1465
+ function getTopLevelStatementsForPaths(paths) {
1466
+ let topLevelStatements = /* @__PURE__ */ new Set();
1467
+ for (let path7 of paths) {
1468
+ let topLevelStatement = getTopLevelStatementPathForPath(path7);
1469
+ topLevelStatements.add(topLevelStatement.node);
1470
+ }
1471
+ return topLevelStatements;
1472
+ }
1473
+ function getIdentifiersForPatternPath(patternPath, identifiers = /* @__PURE__ */ new Set()) {
1474
+ function walk(currentPath) {
1475
+ if (currentPath.isIdentifier()) {
1476
+ identifiers.add(currentPath);
1477
+ return;
1478
+ }
1479
+ if (currentPath.isObjectPattern()) {
1480
+ let { properties } = currentPath.node;
1481
+ for (let i = 0; i < properties.length; i++) {
1482
+ const property = properties[i];
1483
+ if (t.isObjectProperty(property)) {
1484
+ let valuePath = currentPath.get(`properties.${i}.value`);
1485
+ assertNodePath(valuePath);
1486
+ walk(valuePath);
1487
+ } else if (t.isRestElement(property)) {
1488
+ let argumentPath = currentPath.get(`properties.${i}.argument`);
1489
+ assertNodePath(argumentPath);
1490
+ walk(argumentPath);
1491
+ }
1492
+ }
1493
+ } else if (currentPath.isArrayPattern()) {
1494
+ let { elements } = currentPath.node;
1495
+ for (let i = 0; i < elements.length; i++) {
1496
+ const element = elements[i];
1497
+ if (element) {
1498
+ let elementPath = currentPath.get(`elements.${i}`);
1499
+ assertNodePath(elementPath);
1500
+ walk(elementPath);
1501
+ }
1502
+ }
1503
+ } else if (currentPath.isRestElement()) {
1504
+ let argumentPath = currentPath.get("argument");
1505
+ assertNodePath(argumentPath);
1506
+ walk(argumentPath);
1507
+ }
1508
+ }
1509
+ walk(patternPath);
1510
+ return identifiers;
1511
+ }
1512
+ var getExportedName = (exported) => {
1513
+ return t.isIdentifier(exported) ? exported.name : exported.value;
1514
+ };
1515
+ function setsIntersect(set1, set2) {
1516
+ let smallerSet = set1;
1517
+ let largerSet = set2;
1518
+ if (set1.size > set2.size) {
1519
+ smallerSet = set2;
1520
+ largerSet = set1;
1521
+ }
1522
+ for (let element of smallerSet) {
1523
+ if (largerSet.has(element)) {
1524
+ return true;
1525
+ }
1526
+ }
1527
+ return false;
1528
+ }
1529
+ function hasChunkableExport(code, exportName, cache, cacheKey) {
1530
+ return getOrSetFromCache(
1531
+ cache,
1532
+ `${cacheKey}::hasChunkableExport::${exportName}`,
1533
+ code,
1534
+ () => {
1535
+ let exportDependencies = getExportDependencies(code, cache, cacheKey);
1536
+ let dependencies = exportDependencies.get(exportName);
1537
+ if (!dependencies) {
1538
+ return false;
1539
+ }
1540
+ for (let [currentExportName, currentDependencies] of exportDependencies) {
1541
+ if (currentExportName === exportName) {
1542
+ continue;
1543
+ }
1544
+ if (setsIntersect(
1545
+ currentDependencies.topLevelNonModuleStatements,
1546
+ dependencies.topLevelNonModuleStatements
1547
+ )) {
1548
+ return false;
1549
+ }
1550
+ }
1551
+ if (dependencies.exportedVariableDeclarators.size > 1) {
1552
+ return false;
1553
+ }
1554
+ if (dependencies.exportedVariableDeclarators.size > 0) {
1555
+ for (let [
1556
+ currentExportName,
1557
+ currentDependencies
1558
+ ] of exportDependencies) {
1559
+ if (currentExportName === exportName) {
1560
+ continue;
1561
+ }
1562
+ if (setsIntersect(
1563
+ currentDependencies.exportedVariableDeclarators,
1564
+ dependencies.exportedVariableDeclarators
1565
+ )) {
1566
+ return false;
1567
+ }
1568
+ }
1569
+ }
1570
+ return true;
1571
+ }
1572
+ );
1573
+ }
1574
+ function getChunkedExport(code, exportName, generateOptions = {}, cache, cacheKey) {
1575
+ return getOrSetFromCache(
1576
+ cache,
1577
+ `${cacheKey}::getChunkedExport::${exportName}::${JSON.stringify(
1578
+ generateOptions
1579
+ )}`,
1580
+ code,
1581
+ () => {
1582
+ if (!hasChunkableExport(code, exportName, cache, cacheKey)) {
1583
+ return void 0;
1584
+ }
1585
+ let exportDependencies = getExportDependencies(code, cache, cacheKey);
1586
+ let dependencies = exportDependencies.get(exportName);
1587
+ invariant(dependencies, "Expected export to have dependencies");
1588
+ let topLevelStatementsArray = Array.from(dependencies.topLevelStatements);
1589
+ let exportedVariableDeclaratorsArray = Array.from(
1590
+ dependencies.exportedVariableDeclarators
1591
+ );
1592
+ let ast = codeToAst(code, cache, cacheKey);
1593
+ ast.program.body = ast.program.body.filter(
1594
+ (node) => topLevelStatementsArray.some(
1595
+ (statement) => t.isNodesEquivalent(node, statement)
1596
+ )
1597
+ ).map((node) => {
1598
+ if (!t.isImportDeclaration(node)) {
1599
+ return node;
1600
+ }
1601
+ if (dependencies.importedIdentifierNames.size === 0) {
1602
+ return null;
1603
+ }
1604
+ node.specifiers = node.specifiers.filter(
1605
+ (specifier) => dependencies.importedIdentifierNames.has(specifier.local.name)
1606
+ );
1607
+ invariant(
1608
+ node.specifiers.length > 0,
1609
+ "Expected import statement to have used specifiers"
1610
+ );
1611
+ return node;
1612
+ }).map((node) => {
1613
+ if (!t.isExportDeclaration(node)) {
1614
+ return node;
1615
+ }
1616
+ if (t.isExportAllDeclaration(node)) {
1617
+ return null;
1618
+ }
1619
+ if (t.isExportDefaultDeclaration(node)) {
1620
+ return exportName === "default" ? node : null;
1621
+ }
1622
+ let { declaration } = node;
1623
+ if (t.isVariableDeclaration(declaration)) {
1624
+ declaration.declarations = declaration.declarations.filter(
1625
+ (node2) => exportedVariableDeclaratorsArray.some(
1626
+ (declarator) => t.isNodesEquivalent(node2, declarator)
1627
+ )
1628
+ );
1629
+ if (declaration.declarations.length === 0) {
1630
+ return null;
1631
+ }
1632
+ return node;
1633
+ }
1634
+ if (t.isFunctionDeclaration(node.declaration) || t.isClassDeclaration(node.declaration)) {
1635
+ return node.declaration.id?.name === exportName ? node : null;
1636
+ }
1637
+ if (t.isExportNamedDeclaration(node)) {
1638
+ if (node.specifiers.length === 0) {
1639
+ return null;
1640
+ }
1641
+ node.specifiers = node.specifiers.filter(
1642
+ (specifier) => getExportedName(specifier.exported) === exportName
1643
+ );
1644
+ if (node.specifiers.length === 0) {
1645
+ return null;
1646
+ }
1647
+ return node;
1648
+ }
1649
+ throw new Error(`Unknown export node type: ${node.type}`);
1650
+ }).filter((node) => node !== null);
1651
+ return generate(ast, generateOptions);
1652
+ }
1653
+ );
1654
+ }
1655
+ function omitChunkedExports(code, exportNames, generateOptions = {}, cache, cacheKey) {
1656
+ return getOrSetFromCache(
1657
+ cache,
1658
+ `${cacheKey}::omitChunkedExports::${exportNames.join(
1659
+ ","
1660
+ )}::${JSON.stringify(generateOptions)}`,
1661
+ code,
1662
+ () => {
1663
+ const isChunkable = (exportName) => hasChunkableExport(code, exportName, cache, cacheKey);
1664
+ const isOmitted = (exportName) => exportNames.includes(exportName) && isChunkable(exportName);
1665
+ const isRetained = (exportName) => !isOmitted(exportName);
1666
+ let exportDependencies = getExportDependencies(code, cache, cacheKey);
1667
+ let allExportNames = Array.from(exportDependencies.keys());
1668
+ let omittedExportNames = allExportNames.filter(isOmitted);
1669
+ let retainedExportNames = allExportNames.filter(isRetained);
1670
+ let omittedStatements = /* @__PURE__ */ new Set();
1671
+ let omittedExportedVariableDeclarators = /* @__PURE__ */ new Set();
1672
+ for (let omittedExportName of omittedExportNames) {
1673
+ let dependencies = exportDependencies.get(omittedExportName);
1674
+ invariant(
1675
+ dependencies,
1676
+ `Expected dependencies for ${omittedExportName}`
1677
+ );
1678
+ for (let statement of dependencies.topLevelNonModuleStatements) {
1679
+ omittedStatements.add(statement);
1680
+ }
1681
+ for (let declarator of dependencies.exportedVariableDeclarators) {
1682
+ omittedExportedVariableDeclarators.add(declarator);
1683
+ }
1684
+ }
1685
+ let ast = codeToAst(code, cache, cacheKey);
1686
+ let omittedStatementsArray = Array.from(omittedStatements);
1687
+ let omittedExportedVariableDeclaratorsArray = Array.from(
1688
+ omittedExportedVariableDeclarators
1689
+ );
1690
+ ast.program.body = ast.program.body.filter(
1691
+ (node) => omittedStatementsArray.every(
1692
+ (statement) => !t.isNodesEquivalent(node, statement)
1693
+ )
1694
+ ).map((node) => {
1695
+ if (!t.isImportDeclaration(node)) {
1696
+ return node;
1697
+ }
1698
+ if (node.specifiers.length === 0) {
1699
+ return node;
1700
+ }
1701
+ node.specifiers = node.specifiers.filter((specifier) => {
1702
+ let importedName = specifier.local.name;
1703
+ for (let retainedExportName of retainedExportNames) {
1704
+ let dependencies = exportDependencies.get(retainedExportName);
1705
+ if (dependencies?.importedIdentifierNames?.has(importedName)) {
1706
+ return true;
1707
+ }
1708
+ }
1709
+ for (let omittedExportName of omittedExportNames) {
1710
+ let dependencies = exportDependencies.get(omittedExportName);
1711
+ if (dependencies?.importedIdentifierNames?.has(importedName)) {
1712
+ return false;
1713
+ }
1714
+ }
1715
+ return true;
1716
+ });
1717
+ if (node.specifiers.length === 0) {
1718
+ return null;
1719
+ }
1720
+ return node;
1721
+ }).map((node) => {
1722
+ if (!t.isExportDeclaration(node)) {
1723
+ return node;
1724
+ }
1725
+ if (t.isExportAllDeclaration(node)) {
1726
+ return node;
1727
+ }
1728
+ if (t.isExportDefaultDeclaration(node)) {
1729
+ return isOmitted("default") ? null : node;
1730
+ }
1731
+ if (t.isVariableDeclaration(node.declaration)) {
1732
+ node.declaration.declarations = node.declaration.declarations.filter(
1733
+ (node2) => omittedExportedVariableDeclaratorsArray.every(
1734
+ (declarator) => !t.isNodesEquivalent(node2, declarator)
1735
+ )
1736
+ );
1737
+ if (node.declaration.declarations.length === 0) {
1738
+ return null;
1739
+ }
1740
+ return node;
1741
+ }
1742
+ if (t.isFunctionDeclaration(node.declaration) || t.isClassDeclaration(node.declaration)) {
1743
+ invariant(
1744
+ node.declaration.id,
1745
+ "Expected exported function or class declaration to have a name when not the default export"
1746
+ );
1747
+ return isOmitted(node.declaration.id.name) ? null : node;
1748
+ }
1749
+ if (t.isExportNamedDeclaration(node)) {
1750
+ if (node.specifiers.length === 0) {
1751
+ return node;
1752
+ }
1753
+ node.specifiers = node.specifiers.filter((specifier) => {
1754
+ const exportedName = getExportedName(specifier.exported);
1755
+ return !isOmitted(exportedName);
1756
+ });
1757
+ if (node.specifiers.length === 0) {
1758
+ return null;
1759
+ }
1760
+ return node;
1761
+ }
1762
+ throw new Error(`Unknown node type: ${node.type}`);
1763
+ }).filter((node) => node !== null);
1764
+ if (ast.program.body.length === 0) {
1765
+ return void 0;
1766
+ }
1767
+ return generate(ast, generateOptions);
1768
+ }
1769
+ );
1770
+ }
1771
+ function detectRouteChunks(code, cache, cacheKey) {
1772
+ const hasRouteChunkByExportName = Object.fromEntries(
1773
+ routeChunkExportNames.map((exportName) => [
1774
+ exportName,
1775
+ hasChunkableExport(code, exportName, cache, cacheKey)
1776
+ ])
1777
+ );
1778
+ const chunkedExports = Object.entries(hasRouteChunkByExportName).filter(([, isChunked]) => isChunked).map(([exportName]) => exportName);
1779
+ const hasRouteChunks = chunkedExports.length > 0;
1780
+ return {
1781
+ hasRouteChunks,
1782
+ hasRouteChunkByExportName,
1783
+ chunkedExports
1784
+ };
1785
+ }
1786
+ var routeChunkExportNames = [
1787
+ "clientAction",
1788
+ "clientLoader",
1789
+ "HydrateFallback"
1790
+ ];
1791
+ var mainChunkName = "main";
1792
+ var routeChunkNames = ["main", ...routeChunkExportNames];
1793
+ function getRouteChunkCode(code, chunkName, cache, cacheKey) {
1794
+ if (chunkName === mainChunkName) {
1795
+ return omitChunkedExports(code, routeChunkExportNames, {}, cache, cacheKey);
1796
+ }
1797
+ return getChunkedExport(code, chunkName, {}, cache, cacheKey);
1798
+ }
1799
+ var routeChunkQueryStringPrefix = "?route-chunk=";
1800
+ var routeChunkQueryStrings = {
1801
+ main: `${routeChunkQueryStringPrefix}main`,
1802
+ clientAction: `${routeChunkQueryStringPrefix}clientAction`,
1803
+ clientLoader: `${routeChunkQueryStringPrefix}clientLoader`,
1804
+ HydrateFallback: `${routeChunkQueryStringPrefix}HydrateFallback`
1805
+ };
1806
+ function getRouteChunkModuleId(filePath, chunkName) {
1807
+ return `${filePath}${routeChunkQueryStrings[chunkName]}`;
1808
+ }
1809
+ function isRouteChunkModuleId(id) {
1810
+ return Object.values(routeChunkQueryStrings).some(
1811
+ (queryString) => id.endsWith(queryString)
1812
+ );
1813
+ }
1814
+ function isRouteChunkName(name) {
1815
+ return name === mainChunkName || routeChunkExportNames.includes(name);
1816
+ }
1817
+ function getRouteChunkNameFromModuleId(id) {
1818
+ if (!isRouteChunkModuleId(id)) {
1819
+ return null;
1820
+ }
1821
+ let chunkName = id.split(routeChunkQueryStringPrefix)[1].split("&")[0];
1822
+ if (!isRouteChunkName(chunkName)) {
1823
+ return null;
1824
+ }
1825
+ return chunkName;
1826
+ }
1827
+
1162
1828
  // vite/with-props.ts
1163
- var import_dedent2 = __toESM(require("dedent"));
1829
+ var import_dedent3 = __toESM(require("dedent"));
1164
1830
  var vmod = create("with-props");
1165
1831
  var NAMED_COMPONENT_EXPORTS = ["HydrateFallback", "ErrorBoundary"];
1166
1832
  var plugin = {
@@ -1171,7 +1837,7 @@ var plugin = {
1171
1837
  },
1172
1838
  async load(id) {
1173
1839
  if (id !== vmod.resolvedId) return;
1174
- return import_dedent2.default`
1840
+ return import_dedent3.default`
1175
1841
  import { createElement as h } from "react";
1176
1842
  import { useActionData, useLoaderData, useMatches, useParams, useRouteError } from "react-router";
1177
1843
 
@@ -1191,6 +1857,8 @@ var plugin = {
1191
1857
  return function Wrapped() {
1192
1858
  const props = {
1193
1859
  params: useParams(),
1860
+ loaderData: useLoaderData(),
1861
+ actionData: useActionData(),
1194
1862
  };
1195
1863
  return h(HydrateFallback, props);
1196
1864
  };
@@ -1298,8 +1966,34 @@ var CLIENT_ROUTE_EXPORTS = [
1298
1966
  "shouldRevalidate"
1299
1967
  ];
1300
1968
  var BUILD_CLIENT_ROUTE_QUERY_STRING = "?__react-router-build-client-route";
1969
+ var SSR_BUNDLE_PREFIX = "ssrBundle_";
1970
+ function isSeverBundleEnvironmentName(name) {
1971
+ return name.startsWith(SSR_BUNDLE_PREFIX);
1972
+ }
1973
+ function getServerEnvironmentEntries(record, buildManifest) {
1974
+ return Object.entries(record).filter(
1975
+ ([name]) => buildManifest.serverBundles ? isSeverBundleEnvironmentName(name) : name === "ssr"
1976
+ );
1977
+ }
1978
+ function getServerEnvironmentValues(record, buildManifest) {
1979
+ return getServerEnvironmentEntries(record, buildManifest).map(
1980
+ ([, value]) => value
1981
+ );
1982
+ }
1983
+ var isRouteEntryModuleId = (id) => {
1984
+ return id.endsWith(BUILD_CLIENT_ROUTE_QUERY_STRING);
1985
+ };
1986
+ var isRouteVirtualModule = (id) => {
1987
+ return isRouteEntryModuleId(id) || isRouteChunkModuleId(id);
1988
+ };
1301
1989
  var virtualHmrRuntime = create("hmr-runtime");
1302
1990
  var virtualInjectHmrRuntime = create("inject-hmr-runtime");
1991
+ var normalizeRelativeFilePath = (file, reactRouterConfig) => {
1992
+ let vite2 = getVite();
1993
+ let fullPath = path6.resolve(reactRouterConfig.appDirectory, file);
1994
+ let relativePath = path6.relative(reactRouterConfig.appDirectory, fullPath);
1995
+ return vite2.normalizePath(relativePath).split("?")[0];
1996
+ };
1303
1997
  var resolveRelativeRouteFilePath = (route, reactRouterConfig) => {
1304
1998
  let vite2 = getVite();
1305
1999
  let file = route.file;
@@ -1328,23 +2022,35 @@ var resolveChunk = (ctx, viteManifest, absoluteFilePath) => {
1328
2022
  let rootRelativeFilePath = vite2.normalizePath(
1329
2023
  path6.relative(ctx.rootDirectory, absoluteFilePath)
1330
2024
  );
1331
- let entryChunk = viteManifest[rootRelativeFilePath + BUILD_CLIENT_ROUTE_QUERY_STRING] ?? viteManifest[rootRelativeFilePath];
2025
+ let entryChunk = viteManifest[rootRelativeFilePath];
1332
2026
  if (!entryChunk) {
1333
- let knownManifestKeys = Object.keys(viteManifest).map((key) => '"' + key + '"').join(", ");
1334
- throw new Error(
1335
- `No manifest entry found for "${rootRelativeFilePath}". Known manifest keys: ${knownManifestKeys}`
1336
- );
2027
+ return void 0;
1337
2028
  }
1338
2029
  return entryChunk;
1339
2030
  };
2031
+ var getPublicModulePathForEntry = (ctx, viteManifest, entryFilePath) => {
2032
+ let entryChunk = resolveChunk(ctx, viteManifest, entryFilePath);
2033
+ return entryChunk ? `${ctx.publicPath}${entryChunk.file}` : void 0;
2034
+ };
1340
2035
  var getReactRouterManifestBuildAssets = (ctx, viteManifest, entryFilePath, prependedAssetFilePaths = []) => {
1341
2036
  let entryChunk = resolveChunk(ctx, viteManifest, entryFilePath);
1342
- let prependedAssetChunks = prependedAssetFilePaths.map(
1343
- (filePath) => resolveChunk(ctx, viteManifest, filePath)
1344
- );
2037
+ invariant(entryChunk, "Chunk not found");
2038
+ let prependedAssetChunks = prependedAssetFilePaths.map((filePath) => {
2039
+ let chunk = resolveChunk(ctx, viteManifest, filePath);
2040
+ invariant(chunk, "Chunk not found");
2041
+ return chunk;
2042
+ });
2043
+ let routeModuleChunks = routeChunkNames.map(
2044
+ (routeChunkName) => resolveChunk(
2045
+ ctx,
2046
+ viteManifest,
2047
+ getRouteChunkModuleId(entryFilePath.split("?")[0], routeChunkName)
2048
+ )
2049
+ ).filter(isNonNullable);
1345
2050
  let chunks = resolveDependantChunks(viteManifest, [
1346
2051
  ...prependedAssetChunks,
1347
- entryChunk
2052
+ entryChunk,
2053
+ ...routeModuleChunks
1348
2054
  ]);
1349
2055
  return {
1350
2056
  module: `${ctx.publicPath}${entryChunk.file}`,
@@ -1381,6 +2087,10 @@ var writeFileSafe = async (file, contents) => {
1381
2087
  await fse.ensureDir(path6.dirname(file));
1382
2088
  await fse.writeFile(file, contents);
1383
2089
  };
2090
+ var getExportNames = (code) => {
2091
+ let [, exportSpecifiers] = (0, import_es_module_lexer.parse)(code);
2092
+ return exportSpecifiers.map(({ n: name }) => name);
2093
+ };
1384
2094
  var getRouteManifestModuleExports = async (viteChildCompiler, ctx) => {
1385
2095
  let entries = await Promise.all(
1386
2096
  Object.entries(ctx.reactRouterConfig.routes).map(async ([key, route]) => {
@@ -1394,7 +2104,7 @@ var getRouteManifestModuleExports = async (viteChildCompiler, ctx) => {
1394
2104
  );
1395
2105
  return Object.fromEntries(entries);
1396
2106
  };
1397
- var getRouteModuleExports = async (viteChildCompiler, ctx, routeFile, readRouteFile) => {
2107
+ var compileRouteFile = async (viteChildCompiler, ctx, routeFile, readRouteFile) => {
1398
2108
  if (!viteChildCompiler) {
1399
2109
  throw new Error("Vite child compiler not found");
1400
2110
  }
@@ -1414,20 +2124,38 @@ var getRouteModuleExports = async (viteChildCompiler, ctx, routeFile, readRouteF
1414
2124
  moduleGraph.ensureEntryFromUrl(url2, ssr)
1415
2125
  ]);
1416
2126
  let transformed = await pluginContainer.transform(code, id, { ssr });
1417
- let [, exports2] = (0, import_es_module_lexer.parse)(transformed.code);
1418
- let exportNames = exports2.map((e) => e.n);
1419
- return exportNames;
2127
+ return transformed.code;
1420
2128
  };
1421
- var getServerBundleBuildConfig = (viteUserConfig) => {
1422
- if (!("__reactRouterServerBundleBuildConfig" in viteUserConfig) || !viteUserConfig.__reactRouterServerBundleBuildConfig) {
2129
+ var getRouteModuleExports = async (viteChildCompiler, ctx, routeFile, readRouteFile) => {
2130
+ if (!viteChildCompiler) {
2131
+ throw new Error("Vite child compiler not found");
2132
+ }
2133
+ let code = await compileRouteFile(
2134
+ viteChildCompiler,
2135
+ ctx,
2136
+ routeFile,
2137
+ readRouteFile
2138
+ );
2139
+ return getExportNames(code);
2140
+ };
2141
+ var resolveEnvironmentBuildContext = ({
2142
+ viteCommand,
2143
+ viteUserConfig
2144
+ }) => {
2145
+ if (!("__reactRouterEnvironmentBuildContext" in viteUserConfig) || !viteUserConfig.__reactRouterEnvironmentBuildContext) {
1423
2146
  return null;
1424
2147
  }
1425
- return viteUserConfig.__reactRouterServerBundleBuildConfig;
2148
+ let buildContext = viteUserConfig.__reactRouterEnvironmentBuildContext;
2149
+ let resolvedBuildContext = {
2150
+ name: buildContext.name,
2151
+ options: buildContext.resolveOptions({ viteUserConfig })
2152
+ };
2153
+ return resolvedBuildContext;
1426
2154
  };
1427
- var getServerBuildDirectory = (ctx) => path6.join(
2155
+ var getServerBuildDirectory = (ctx, { serverBundleId } = {}) => path6.join(
1428
2156
  ctx.reactRouterConfig.buildDirectory,
1429
2157
  "server",
1430
- ...ctx.serverBundleBuildConfig ? [ctx.serverBundleBuildConfig.serverBundleId] : []
2158
+ ...serverBundleId ? [serverBundleId] : []
1431
2159
  );
1432
2160
  var getClientBuildDirectory = (reactRouterConfig) => path6.join(reactRouterConfig.buildDirectory, "client");
1433
2161
  var defaultEntriesDir = path6.resolve(
@@ -1445,8 +2173,10 @@ var reactRouterVitePlugin = () => {
1445
2173
  let viteUserConfig;
1446
2174
  let viteConfigEnv;
1447
2175
  let viteConfig;
2176
+ let buildManifest;
1448
2177
  let cssModulesManifest = {};
1449
2178
  let viteChildCompiler = null;
2179
+ let cache = /* @__PURE__ */ new Map();
1450
2180
  let reactRouterConfigLoader;
1451
2181
  let typegenWatcherPromise;
1452
2182
  let logger;
@@ -1478,36 +2208,37 @@ var reactRouterVitePlugin = () => {
1478
2208
  process.exit(1);
1479
2209
  }
1480
2210
  let viteManifestEnabled = viteUserConfig.build?.manifest === true;
1481
- let ssrBuildCtx = viteConfigEnv.isSsrBuild && viteCommand === "build" ? {
1482
- isSsrBuild: true,
1483
- getReactRouterServerManifest: async () => (await generateReactRouterManifestsForBuild()).reactRouterServerManifest,
1484
- serverBundleBuildConfig: getServerBundleBuildConfig(viteUserConfig)
1485
- } : { isSsrBuild: false };
2211
+ let environmentBuildContext = viteCommand === "build" ? resolveEnvironmentBuildContext({ viteCommand, viteUserConfig }) : null;
1486
2212
  firstLoad = false;
1487
2213
  ctx = {
2214
+ environmentBuildContext,
1488
2215
  reactRouterConfig,
1489
2216
  rootDirectory,
1490
2217
  entryClientFilePath,
1491
2218
  entryServerFilePath,
1492
2219
  publicPath,
1493
- viteManifestEnabled,
1494
- ...ssrBuildCtx
2220
+ viteManifestEnabled
1495
2221
  };
1496
2222
  };
1497
2223
  let pluginIndex = (pluginName) => {
1498
2224
  invariant(viteConfig);
1499
2225
  return viteConfig.plugins.findIndex((plugin2) => plugin2.name === pluginName);
1500
2226
  };
1501
- let getServerEntry = async () => {
2227
+ let getServerEntry = async ({ routeIds }) => {
1502
2228
  invariant(viteConfig, "viteconfig required to generate the server entry");
1503
- let routes = ctx.serverBundleBuildConfig ? (
2229
+ let routes = routeIds ? (
1504
2230
  // For server bundle builds, the server build should only import the
1505
2231
  // routes for this bundle rather than importing all routes
1506
- ctx.serverBundleBuildConfig.routes
2232
+ (0, import_pick3.default)(ctx.reactRouterConfig.routes, routeIds)
1507
2233
  ) : (
1508
2234
  // Otherwise, all routes are imported as usual
1509
2235
  ctx.reactRouterConfig.routes
1510
2236
  );
2237
+ let prerenderPaths = await getPrerenderPaths(
2238
+ ctx.reactRouterConfig.prerender,
2239
+ ctx.reactRouterConfig.ssr,
2240
+ routes
2241
+ );
1511
2242
  return `
1512
2243
  import * as entryServer from ${JSON.stringify(
1513
2244
  resolveFileUrl(ctx, ctx.entryServerFilePath)
@@ -1522,7 +2253,7 @@ var reactRouterVitePlugin = () => {
1522
2253
  )};`;
1523
2254
  }).join("\n")}
1524
2255
  export { default as assets } from ${JSON.stringify(
1525
- virtual.serverManifest.id
2256
+ `${virtual.serverManifest.id}${routeIds ? `?route-ids=${routeIds.join(",")}` : ""}`
1526
2257
  )};
1527
2258
  export const assetsBuildDirectory = ${JSON.stringify(
1528
2259
  path6.relative(
@@ -1532,7 +2263,9 @@ var reactRouterVitePlugin = () => {
1532
2263
  )};
1533
2264
  export const basename = ${JSON.stringify(ctx.reactRouterConfig.basename)};
1534
2265
  export const future = ${JSON.stringify(ctx.reactRouterConfig.future)};
1535
- export const isSpaMode = ${!ctx.reactRouterConfig.ssr && ctx.reactRouterConfig.prerender == null};
2266
+ export const ssr = ${ctx.reactRouterConfig.ssr};
2267
+ export const isSpaMode = ${isSpaModeEnabled(ctx.reactRouterConfig)};
2268
+ export const prerender = ${JSON.stringify(prerenderPaths)};
1536
2269
  export const publicPath = ${JSON.stringify(ctx.publicPath)};
1537
2270
  export const entry = { module: entryServer };
1538
2271
  export const routes = {
@@ -1570,7 +2303,9 @@ var reactRouterVitePlugin = () => {
1570
2303
  );
1571
2304
  return /* @__PURE__ */ new Set([...cssUrlPaths, ...chunkAssetPaths]);
1572
2305
  };
1573
- let generateReactRouterManifestsForBuild = async () => {
2306
+ let generateReactRouterManifestsForBuild = async ({
2307
+ routeIds
2308
+ }) => {
1574
2309
  invariant(viteConfig);
1575
2310
  let viteManifest = await loadViteManifest(
1576
2311
  getClientBuildDirectory(ctx.reactRouterConfig)
@@ -1586,13 +2321,31 @@ var reactRouterVitePlugin = () => {
1586
2321
  viteChildCompiler,
1587
2322
  ctx
1588
2323
  );
1589
- for (let [key, route] of Object.entries(ctx.reactRouterConfig.routes)) {
1590
- let routeFilePath = path6.join(
1591
- ctx.reactRouterConfig.appDirectory,
1592
- route.file
1593
- );
1594
- let sourceExports = routeManifestExports[key];
2324
+ let enforceSplitRouteModules = ctx.reactRouterConfig.future.unstable_splitRouteModules === "enforce";
2325
+ for (let route of Object.values(ctx.reactRouterConfig.routes)) {
2326
+ let routeFile = path6.join(ctx.reactRouterConfig.appDirectory, route.file);
2327
+ let sourceExports = routeManifestExports[route.id];
1595
2328
  let isRootRoute = route.parentId === void 0;
2329
+ let hasClientAction = sourceExports.includes("clientAction");
2330
+ let hasClientLoader = sourceExports.includes("clientLoader");
2331
+ let hasHydrateFallback = sourceExports.includes("HydrateFallback");
2332
+ let { hasRouteChunkByExportName } = await detectRouteChunksIfEnabled(
2333
+ cache,
2334
+ ctx,
2335
+ routeFile,
2336
+ { routeFile, viteChildCompiler }
2337
+ );
2338
+ if (enforceSplitRouteModules) {
2339
+ validateRouteChunks({
2340
+ ctx,
2341
+ id: route.file,
2342
+ valid: {
2343
+ clientAction: !hasClientAction || hasRouteChunkByExportName.clientAction,
2344
+ clientLoader: !hasClientLoader || hasRouteChunkByExportName.clientLoader,
2345
+ HydrateFallback: !hasHydrateFallback || hasRouteChunkByExportName.HydrateFallback
2346
+ }
2347
+ });
2348
+ }
1596
2349
  let routeManifestEntry = {
1597
2350
  id: route.id,
1598
2351
  parentId: route.parentId,
@@ -1601,23 +2354,37 @@ var reactRouterVitePlugin = () => {
1601
2354
  caseSensitive: route.caseSensitive,
1602
2355
  hasAction: sourceExports.includes("action"),
1603
2356
  hasLoader: sourceExports.includes("loader"),
1604
- hasClientAction: sourceExports.includes("clientAction"),
1605
- hasClientLoader: sourceExports.includes("clientLoader"),
2357
+ hasClientAction,
2358
+ hasClientLoader,
1606
2359
  hasErrorBoundary: sourceExports.includes("ErrorBoundary"),
1607
2360
  ...getReactRouterManifestBuildAssets(
1608
2361
  ctx,
1609
2362
  viteManifest,
1610
- routeFilePath,
2363
+ `${routeFile}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
1611
2364
  // If this is the root route, we also need to include assets from the
1612
2365
  // client entry file as this is a common way for consumers to import
1613
2366
  // global reset styles, etc.
1614
2367
  isRootRoute ? [ctx.entryClientFilePath] : []
1615
- )
2368
+ ),
2369
+ clientActionModule: hasRouteChunkByExportName.clientAction ? getPublicModulePathForEntry(
2370
+ ctx,
2371
+ viteManifest,
2372
+ getRouteChunkModuleId(routeFile, "clientAction")
2373
+ ) : void 0,
2374
+ clientLoaderModule: hasRouteChunkByExportName.clientLoader ? getPublicModulePathForEntry(
2375
+ ctx,
2376
+ viteManifest,
2377
+ getRouteChunkModuleId(routeFile, "clientLoader")
2378
+ ) : void 0,
2379
+ hydrateFallbackModule: hasRouteChunkByExportName.HydrateFallback ? getPublicModulePathForEntry(
2380
+ ctx,
2381
+ viteManifest,
2382
+ getRouteChunkModuleId(routeFile, "HydrateFallback")
2383
+ ) : void 0
1616
2384
  };
1617
- browserRoutes[key] = routeManifestEntry;
1618
- let serverBundleRoutes = ctx.serverBundleBuildConfig?.routes;
1619
- if (!serverBundleRoutes || serverBundleRoutes[key]) {
1620
- serverRoutes[key] = routeManifestEntry;
2385
+ browserRoutes[route.id] = routeManifestEntry;
2386
+ if (!routeIds || routeIds.includes(route.id)) {
2387
+ serverRoutes[route.id] = routeManifestEntry;
1621
2388
  }
1622
2389
  }
1623
2390
  let fingerprintedValues = { entry, routes: browserRoutes };
@@ -1653,25 +2420,52 @@ var reactRouterVitePlugin = () => {
1653
2420
  viteChildCompiler,
1654
2421
  ctx
1655
2422
  );
2423
+ let enforceSplitRouteModules = ctx.reactRouterConfig.future.unstable_splitRouteModules === "enforce";
1656
2424
  for (let [key, route] of Object.entries(ctx.reactRouterConfig.routes)) {
2425
+ let routeFile = route.file;
1657
2426
  let sourceExports = routeManifestExports[key];
2427
+ let hasClientAction = sourceExports.includes("clientAction");
2428
+ let hasClientLoader = sourceExports.includes("clientLoader");
2429
+ let hasHydrateFallback = sourceExports.includes("HydrateFallback");
2430
+ let routeModulePath = combineURLs(
2431
+ ctx.publicPath,
2432
+ `${resolveFileUrl(
2433
+ ctx,
2434
+ resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)
2435
+ )}`
2436
+ );
2437
+ if (enforceSplitRouteModules) {
2438
+ let { hasRouteChunkByExportName } = await detectRouteChunksIfEnabled(
2439
+ cache,
2440
+ ctx,
2441
+ routeFile,
2442
+ { routeFile, viteChildCompiler }
2443
+ );
2444
+ validateRouteChunks({
2445
+ ctx,
2446
+ id: route.file,
2447
+ valid: {
2448
+ clientAction: !hasClientAction || hasRouteChunkByExportName.clientAction,
2449
+ clientLoader: !hasClientLoader || hasRouteChunkByExportName.clientLoader,
2450
+ HydrateFallback: !hasHydrateFallback || hasRouteChunkByExportName.HydrateFallback
2451
+ }
2452
+ });
2453
+ }
1658
2454
  routes[key] = {
1659
2455
  id: route.id,
1660
2456
  parentId: route.parentId,
1661
2457
  path: route.path,
1662
2458
  index: route.index,
1663
2459
  caseSensitive: route.caseSensitive,
1664
- module: combineURLs(
1665
- ctx.publicPath,
1666
- resolveFileUrl(
1667
- ctx,
1668
- resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)
1669
- )
1670
- ),
2460
+ module: routeModulePath,
2461
+ // Split route modules are a build-time optimization
2462
+ clientActionModule: void 0,
2463
+ clientLoaderModule: void 0,
2464
+ hydrateFallbackModule: void 0,
1671
2465
  hasAction: sourceExports.includes("action"),
1672
2466
  hasLoader: sourceExports.includes("loader"),
1673
- hasClientAction: sourceExports.includes("clientAction"),
1674
- hasClientLoader: sourceExports.includes("clientLoader"),
2467
+ hasClientAction,
2468
+ hasClientLoader,
1675
2469
  hasErrorBoundary: sourceExports.includes("ErrorBoundary"),
1676
2470
  imports: []
1677
2471
  };
@@ -1704,14 +2498,6 @@ var reactRouterVitePlugin = () => {
1704
2498
  let viteClientConditions = [
1705
2499
  ...vite2.defaultClientConditions ?? []
1706
2500
  ];
1707
- let packageRoot = path6.dirname(
1708
- require.resolve("@react-router/dev/package.json")
1709
- );
1710
- let { moduleSyncEnabled } = await import(`file:///${path6.join(packageRoot, "module-sync-enabled/index.mjs")}`);
1711
- let viteServerConditions = [
1712
- ...vite2.defaultServerConditions ?? [],
1713
- ...moduleSyncEnabled ? ["module-sync"] : []
1714
- ];
1715
2501
  logger = vite2.createLogger(viteUserConfig.logLevel, {
1716
2502
  prefix: "[react-router]"
1717
2503
  });
@@ -1727,43 +2513,37 @@ var reactRouterVitePlugin = () => {
1727
2513
  watch: viteCommand === "serve"
1728
2514
  });
1729
2515
  await updatePluginContext();
2516
+ buildManifest = await getBuildManifest(ctx);
1730
2517
  Object.assign(
1731
2518
  process.env,
1732
2519
  vite2.loadEnv(
1733
2520
  viteConfigEnv.mode,
1734
- ctx.rootDirectory,
2521
+ viteUserConfig.envDir ?? ctx.rootDirectory,
1735
2522
  // We override default prefix of "VITE_" with a blank string since
1736
2523
  // we're targeting the server, so we want to load all environment
1737
2524
  // variables, not just those explicitly marked for the client
1738
2525
  ""
1739
2526
  )
1740
2527
  );
1741
- let baseRollupOptions = {
1742
- // Silence Rollup "use client" warnings
1743
- // Adapted from https://github.com/vitejs/vite-plugin-react/pull/144
1744
- onwarn(warning, defaultHandler) {
1745
- if (warning.code === "MODULE_LEVEL_DIRECTIVE" && warning.message.includes("use client")) {
1746
- return;
1747
- }
1748
- if (viteUserConfig.build?.rollupOptions?.onwarn) {
1749
- viteUserConfig.build.rollupOptions.onwarn(
1750
- warning,
1751
- defaultHandler
1752
- );
1753
- } else {
1754
- defaultHandler(warning);
1755
- }
1756
- }
1757
- };
2528
+ let environments = await getEnvironmentsOptions(
2529
+ ctx,
2530
+ buildManifest,
2531
+ viteCommand,
2532
+ { viteUserConfig }
2533
+ );
2534
+ let serverEnvironment = getServerEnvironmentValues(
2535
+ environments,
2536
+ buildManifest
2537
+ )[0];
2538
+ invariant(serverEnvironment);
2539
+ let clientEnvironment = environments.client;
2540
+ invariant(clientEnvironment);
1758
2541
  return {
1759
2542
  __reactRouterPluginContext: ctx,
1760
2543
  appType: viteCommand === "serve" && viteConfigEnv.mode === "production" && ctx.reactRouterConfig.ssr === false ? "spa" : "custom",
1761
2544
  ssr: {
1762
- external: ssrExternals,
1763
- resolve: {
1764
- conditions: viteCommand === "build" ? viteServerConditions : ["development", ...viteServerConditions],
1765
- externalConditions: viteCommand === "build" ? viteServerConditions : ["development", ...viteServerConditions]
1766
- }
2545
+ external: serverEnvironment.resolve?.external,
2546
+ resolve: serverEnvironment.resolve
1767
2547
  },
1768
2548
  optimizeDeps: {
1769
2549
  entries: ctx.reactRouterConfig.future.unstable_optimizeDeps ? [
@@ -1812,57 +2592,41 @@ var reactRouterVitePlugin = () => {
1812
2592
  // will throw an error that the file is not allowed to be read.
1813
2593
  // https://vitejs.dev/config/server-options#server-fs-allow
1814
2594
  server: viteUserConfig.server?.fs?.allow ? { fs: { allow: defaultEntries } } : void 0,
1815
- // Vite config options for building
1816
- ...viteCommand === "build" ? {
2595
+ ...ctx.reactRouterConfig.future.unstable_viteEnvironmentApi ? {
2596
+ environments,
1817
2597
  build: {
1818
- cssMinify: viteUserConfig.build?.cssMinify ?? true,
1819
- ...!viteConfigEnv.isSsrBuild ? {
1820
- manifest: true,
1821
- outDir: getClientBuildDirectory(ctx.reactRouterConfig),
1822
- rollupOptions: {
1823
- ...baseRollupOptions,
1824
- preserveEntrySignatures: "exports-only",
1825
- input: [
1826
- ctx.entryClientFilePath,
1827
- ...Object.values(ctx.reactRouterConfig.routes).map(
1828
- (route) => `${path6.resolve(
1829
- ctx.reactRouterConfig.appDirectory,
1830
- route.file
1831
- )}${BUILD_CLIENT_ROUTE_QUERY_STRING}`
1832
- )
1833
- ]
1834
- }
1835
- } : {
1836
- // We move SSR-only assets to client assets. Note that the
1837
- // SSR build can also emit code-split JS files (e.g. by
1838
- // dynamic import) under the same assets directory
1839
- // regardless of "ssrEmitAssets" option, so we also need to
1840
- // keep these JS files have to be kept as-is.
1841
- ssrEmitAssets: true,
1842
- copyPublicDir: false,
1843
- // Assets in the public directory are only used by the client
1844
- manifest: true,
1845
- // We need the manifest to detect SSR-only assets
1846
- outDir: getServerBuildDirectory(ctx),
1847
- rollupOptions: {
1848
- ...baseRollupOptions,
1849
- preserveEntrySignatures: "exports-only",
1850
- input: viteUserConfig.build?.rollupOptions?.input ?? virtual.serverBuild.id,
1851
- output: {
1852
- entryFileNames: ctx.reactRouterConfig.serverBuildFile,
1853
- format: ctx.reactRouterConfig.serverModuleFormat
1854
- }
1855
- }
2598
+ // This isn't honored by the SSR environment config (which seems
2599
+ // to be a Vite bug?) so we set it here too.
2600
+ ssrEmitAssets: true
2601
+ },
2602
+ builder: {
2603
+ sharedConfigBuild: true,
2604
+ sharedPlugins: true,
2605
+ async buildApp(builder) {
2606
+ invariant(viteConfig);
2607
+ invariant(buildManifest);
2608
+ viteConfig.logger.info(
2609
+ "Using Vite Environment API (experimental)"
2610
+ );
2611
+ let { reactRouterConfig } = ctx;
2612
+ await cleanBuildDirectory(viteConfig, ctx);
2613
+ await builder.build(builder.environments.client);
2614
+ let serverEnvironments = getServerEnvironmentValues(
2615
+ builder.environments,
2616
+ buildManifest
2617
+ );
2618
+ await Promise.all(serverEnvironments.map(builder.build));
2619
+ await cleanViteManifests(environments, ctx);
2620
+ await reactRouterConfig.buildEnd?.({
2621
+ buildManifest,
2622
+ reactRouterConfig,
2623
+ viteConfig
2624
+ });
1856
2625
  }
1857
2626
  }
1858
- } : void 0,
1859
- // Vite config options for SPA preview mode
1860
- ...viteCommand === "serve" && ctx.reactRouterConfig.ssr === false ? {
1861
- build: {
1862
- manifest: true,
1863
- outDir: getClientBuildDirectory(ctx.reactRouterConfig)
1864
- }
1865
- } : void 0
2627
+ } : {
2628
+ build: ctx.environmentBuildContext?.options.build ?? (viteConfigEnv.isSsrBuild ? serverEnvironment.build : clientEnvironment.build)
2629
+ }
1866
2630
  };
1867
2631
  },
1868
2632
  async configResolved(resolvedViteConfig) {
@@ -1878,8 +2642,7 @@ var reactRouterVitePlugin = () => {
1878
2642
  let childCompilerConfigFile = await vite2.loadConfigFromFile(
1879
2643
  {
1880
2644
  command: viteConfig.command,
1881
- mode: viteConfig.mode,
1882
- isSsrBuild: ctx.isSsrBuild
2645
+ mode: viteConfig.mode
1883
2646
  },
1884
2647
  viteConfig.configFile
1885
2648
  );
@@ -2021,14 +2784,15 @@ var reactRouterVitePlugin = () => {
2021
2784
  // After the SSR build is finished, we inspect the Vite manifest for
2022
2785
  // the SSR build and move server-only assets to client assets directory
2023
2786
  async handler() {
2024
- if (!ctx.isSsrBuild) {
2787
+ let { future } = ctx.reactRouterConfig;
2788
+ if (future.unstable_viteEnvironmentApi ? this.environment.name === "client" : !viteConfigEnv.isSsrBuild) {
2025
2789
  return;
2026
2790
  }
2027
2791
  invariant(viteConfig);
2028
2792
  let clientBuildDirectory = getClientBuildDirectory(
2029
2793
  ctx.reactRouterConfig
2030
2794
  );
2031
- let serverBuildDirectory = getServerBuildDirectory(ctx);
2795
+ let serverBuildDirectory = future.unstable_viteEnvironmentApi ? this.environment.config?.build?.outDir : ctx.environmentBuildContext?.options.build?.outDir ?? getServerBuildDirectory(ctx);
2032
2796
  let ssrViteManifest = await loadViteManifest(serverBuildDirectory);
2033
2797
  let ssrAssetPaths = getViteManifestAssetPaths(ssrViteManifest);
2034
2798
  let movedAssetPaths = [];
@@ -2062,7 +2826,7 @@ var reactRouterVitePlugin = () => {
2062
2826
  ].join("\n")
2063
2827
  );
2064
2828
  }
2065
- if (ctx.reactRouterConfig.prerender != null && ctx.reactRouterConfig.prerender !== false) {
2829
+ if (isPrerenderingEnabled(ctx.reactRouterConfig)) {
2066
2830
  await handlePrerender(
2067
2831
  viteConfig,
2068
2832
  ctx.reactRouterConfig,
@@ -2099,10 +2863,59 @@ var reactRouterVitePlugin = () => {
2099
2863
  await typegenWatcher?.close();
2100
2864
  }
2101
2865
  },
2866
+ {
2867
+ name: "react-router:route-chunks-index",
2868
+ // This plugin provides the route module "index" since route modules can
2869
+ // be chunked and may be made up of multiple smaller modules. This plugin
2870
+ // primarily ensures code is never duplicated across a route module and
2871
+ // its chunks. If we didn't have this plugin, any app that explicitly
2872
+ // imports a route module would result in duplicate code since the app
2873
+ // would contain code for both the unprocessed route module as well as its
2874
+ // individual chunks. This is because, since they have different module
2875
+ // IDs, they are treated as completely separate modules even though they
2876
+ // all reference the same underlying file. This plugin addresses this by
2877
+ // ensuring that any explicit imports of a route module resolve to a
2878
+ // module that simply re-exports from its underlying chunks, if present.
2879
+ async transform(code, id, options) {
2880
+ if (viteCommand !== "build") return;
2881
+ if (options?.ssr) {
2882
+ return;
2883
+ }
2884
+ if (!isRoute(ctx.reactRouterConfig, id)) {
2885
+ return;
2886
+ }
2887
+ if (isRouteVirtualModule(id)) {
2888
+ return;
2889
+ }
2890
+ let { hasRouteChunks, chunkedExports } = await detectRouteChunksIfEnabled(cache, ctx, id, code);
2891
+ if (!hasRouteChunks) {
2892
+ return;
2893
+ }
2894
+ let sourceExports = await getRouteModuleExports(
2895
+ viteChildCompiler,
2896
+ ctx,
2897
+ id
2898
+ );
2899
+ let isMainChunkExport = (name) => !chunkedExports.includes(name);
2900
+ let mainChunkReexports = sourceExports.filter(isMainChunkExport).join(", ");
2901
+ let chunkBasePath = `./${path6.basename(id)}`;
2902
+ return [
2903
+ `export { ${mainChunkReexports} } from "${getRouteChunkModuleId(
2904
+ chunkBasePath,
2905
+ "main"
2906
+ )}";`,
2907
+ ...chunkedExports.map(
2908
+ (exportName) => `export { ${exportName} } from "${getRouteChunkModuleId(
2909
+ chunkBasePath,
2910
+ exportName
2911
+ )}";`
2912
+ )
2913
+ ].filter(Boolean).join("\n");
2914
+ }
2915
+ },
2102
2916
  {
2103
2917
  name: "react-router:build-client-route",
2104
- enforce: "pre",
2105
- async transform(_code, id, options) {
2918
+ async transform(code, id, options) {
2106
2919
  if (!id.endsWith(BUILD_CLIENT_ROUTE_QUERY_STRING)) return;
2107
2920
  let routeModuleId = id.replace(BUILD_CLIENT_ROUTE_QUERY_STRING, "");
2108
2921
  let routeFileName = path6.basename(routeModuleId);
@@ -2111,26 +2924,91 @@ var reactRouterVitePlugin = () => {
2111
2924
  ctx,
2112
2925
  routeModuleId
2113
2926
  );
2114
- let reexports = sourceExports.filter(
2115
- (exportName) => options?.ssr && SERVER_ONLY_ROUTE_EXPORTS.includes(exportName) || CLIENT_ROUTE_EXPORTS.includes(exportName)
2116
- ).join(", ");
2927
+ let { chunkedExports = [] } = options?.ssr ? {} : await detectRouteChunksIfEnabled(cache, ctx, id, code);
2928
+ let reexports = sourceExports.filter((exportName) => {
2929
+ let isRouteEntryExport = options?.ssr && SERVER_ONLY_ROUTE_EXPORTS.includes(exportName) || CLIENT_ROUTE_EXPORTS.includes(exportName);
2930
+ let isChunkedExport = chunkedExports.includes(
2931
+ exportName
2932
+ );
2933
+ return isRouteEntryExport && !isChunkedExport;
2934
+ }).join(", ");
2117
2935
  return `export { ${reexports} } from "./${routeFileName}";`;
2118
2936
  }
2119
2937
  },
2938
+ {
2939
+ name: "react-router:split-route-modules",
2940
+ async transform(code, id, options) {
2941
+ if (options?.ssr) return;
2942
+ if (!isRouteChunkModuleId(id)) return;
2943
+ invariant(
2944
+ viteCommand === "build",
2945
+ "Route modules are only split in build mode"
2946
+ );
2947
+ let chunkName = getRouteChunkNameFromModuleId(id);
2948
+ if (!chunkName) {
2949
+ throw new Error(`Invalid route chunk name "${chunkName}" in "${id}"`);
2950
+ }
2951
+ let chunk = await getRouteChunkIfEnabled(
2952
+ cache,
2953
+ ctx,
2954
+ id,
2955
+ chunkName,
2956
+ code
2957
+ );
2958
+ let preventEmptyChunkSnippet = ({ reason }) => `Math.random()<0&&console.log(${JSON.stringify(reason)});`;
2959
+ if (chunk === null) {
2960
+ return preventEmptyChunkSnippet({
2961
+ reason: "Split round modules disabled"
2962
+ });
2963
+ }
2964
+ let enforceSplitRouteModules = ctx.reactRouterConfig.future.unstable_splitRouteModules === "enforce";
2965
+ if (enforceSplitRouteModules && chunkName === "main" && chunk) {
2966
+ let exportNames = getExportNames(chunk.code);
2967
+ validateRouteChunks({
2968
+ ctx,
2969
+ id,
2970
+ valid: {
2971
+ clientAction: !exportNames.includes("clientAction"),
2972
+ clientLoader: !exportNames.includes("clientLoader"),
2973
+ HydrateFallback: !exportNames.includes("HydrateFallback")
2974
+ }
2975
+ });
2976
+ }
2977
+ return chunk ?? preventEmptyChunkSnippet({ reason: `No ${chunkName} chunk` });
2978
+ }
2979
+ },
2120
2980
  {
2121
2981
  name: "react-router:virtual-modules",
2122
2982
  enforce: "pre",
2123
2983
  resolveId(id) {
2124
- const vmod2 = Object.values(virtual).find((vmod3) => vmod3.id === id);
2125
- if (vmod2) return vmod2.resolvedId;
2984
+ let [baseId, queryString] = id.split("?");
2985
+ const vmod2 = Object.values(virtual).find((vmod3) => vmod3.id === baseId);
2986
+ if (vmod2)
2987
+ return vmod2.resolvedId + (queryString ? `?${queryString}` : "");
2126
2988
  },
2127
2989
  async load(id) {
2128
- switch (id) {
2990
+ let [baseId, queryString] = id.split("?");
2991
+ switch (baseId) {
2129
2992
  case virtual.serverBuild.resolvedId: {
2130
- return await getServerEntry();
2993
+ let searchParams = new URLSearchParams(queryString);
2994
+ let routeIds = searchParams.get("route-ids")?.split(",") || void 0;
2995
+ return await getServerEntry({ routeIds });
2131
2996
  }
2132
2997
  case virtual.serverManifest.resolvedId: {
2133
- let reactRouterManifest = ctx.isSsrBuild ? await ctx.getReactRouterServerManifest() : await getReactRouterManifestForDev();
2998
+ let searchParams = new URLSearchParams(queryString);
2999
+ let routeIds = searchParams.get("route-ids")?.split(",") || void 0;
3000
+ let reactRouterManifest = viteCommand === "build" ? (await generateReactRouterManifestsForBuild({
3001
+ routeIds
3002
+ })).reactRouterServerManifest : await getReactRouterManifestForDev();
3003
+ if (!ctx.reactRouterConfig.ssr) {
3004
+ invariant(viteConfig);
3005
+ validateSsrFalsePrerenderExports(
3006
+ viteConfig,
3007
+ ctx,
3008
+ reactRouterManifest,
3009
+ viteChildCompiler
3010
+ );
3011
+ }
2134
3012
  return `export default ${(0, import_jsesc.default)(reactRouterManifest, {
2135
3013
  es6: true
2136
3014
  })};`;
@@ -2171,8 +3049,7 @@ var reactRouterVitePlugin = () => {
2171
3049
  let importerShort = vite2.normalizePath(
2172
3050
  path6.relative(ctx.rootDirectory, importer)
2173
3051
  );
2174
- let isRoute = getRoute(ctx.reactRouterConfig, importer);
2175
- if (isRoute) {
3052
+ if (isRoute(ctx.reactRouterConfig, importer)) {
2176
3053
  let serverOnlyExports = SERVER_ONLY_ROUTE_EXPORTS.map(
2177
3054
  (xport) => `\`${xport}\``
2178
3055
  ).join(", ");
@@ -2211,10 +3088,10 @@ var reactRouterVitePlugin = () => {
2211
3088
  let clientFileRE = /\.client(\.[cm]?[jt]sx?)?$/;
2212
3089
  let clientDirRE = /\/\.client\//;
2213
3090
  if (clientFileRE.test(id) || clientDirRE.test(id)) {
2214
- let exports2 = (0, import_es_module_lexer.parse)(code)[1];
3091
+ let exports2 = getExportNames(code);
2215
3092
  return {
2216
3093
  code: exports2.map(
2217
- ({ n: name }) => name === "default" ? "export default undefined;" : `export const ${name} = undefined;`
3094
+ (name) => name === "default" ? "export default undefined;" : `export const ${name} = undefined;`
2218
3095
  ).join("\n"),
2219
3096
  map: null
2220
3097
  };
@@ -2225,19 +3102,30 @@ var reactRouterVitePlugin = () => {
2225
3102
  {
2226
3103
  name: "react-router:route-exports",
2227
3104
  async transform(code, id, options) {
3105
+ if (isRouteChunkModuleId(id)) {
3106
+ id = id.split("?")[0];
3107
+ }
2228
3108
  let route = getRoute(ctx.reactRouterConfig, id);
2229
3109
  if (!route) return;
2230
- if (!options?.ssr && !ctx.reactRouterConfig.ssr) {
2231
- let serverOnlyExports = (0, import_es_module_lexer.parse)(code)[1].map((exp) => exp.n).filter((exp) => SERVER_ONLY_ROUTE_EXPORTS.includes(exp));
3110
+ if (!options?.ssr && isSpaModeEnabled(ctx.reactRouterConfig)) {
3111
+ let exportNames = getExportNames(code);
3112
+ let serverOnlyExports = exportNames.filter((exp) => {
3113
+ if (route.id === "root" && exp === "loader") {
3114
+ return false;
3115
+ }
3116
+ return SERVER_ONLY_ROUTE_EXPORTS.includes(exp);
3117
+ });
2232
3118
  if (serverOnlyExports.length > 0) {
2233
3119
  let str = serverOnlyExports.map((e) => `\`${e}\``).join(", ");
2234
- let message = `SPA Mode: ${serverOnlyExports.length} invalid route export(s) in \`${route.file}\`: ${str}. See https://remix.run/guides/spa-mode for more information.`;
3120
+ let message = `SPA Mode: ${serverOnlyExports.length} invalid route export(s) in \`${route.file}\`: ${str}. See https://reactrouter.com/how-to/spa for more information.`;
2235
3121
  throw Error(message);
2236
3122
  }
2237
3123
  if (route.id !== "root") {
2238
- let hasHydrateFallback = (0, import_es_module_lexer.parse)(code)[1].map((exp) => exp.n).some((exp) => exp === "HydrateFallback");
3124
+ let hasHydrateFallback = exportNames.some(
3125
+ (exp) => exp === "HydrateFallback"
3126
+ );
2239
3127
  if (hasHydrateFallback) {
2240
- let message = `SPA Mode: Invalid \`HydrateFallback\` export found in \`${route.file}\`. \`HydrateFallback\` is only permitted on the root route in SPA Mode. See https://remix.run/guides/spa-mode for more information.`;
3128
+ let message = `SPA Mode: Invalid \`HydrateFallback\` export found in \`${route.file}\`. \`HydrateFallback\` is only permitted on the root route in SPA Mode. See https://reactrouter.com/how-to/spa for more information.`;
2241
3129
  throw Error(message);
2242
3130
  }
2243
3131
  }
@@ -2248,7 +3136,7 @@ var reactRouterVitePlugin = () => {
2248
3136
  removeExports(ast, SERVER_ONLY_ROUTE_EXPORTS);
2249
3137
  }
2250
3138
  transform(ast);
2251
- return generate2(ast, {
3139
+ return generate(ast, {
2252
3140
  sourceMaps: true,
2253
3141
  filename: id,
2254
3142
  sourceFileName: filepath
@@ -2313,6 +3201,9 @@ var reactRouterVitePlugin = () => {
2313
3201
  let isJSX = filepath.endsWith("x");
2314
3202
  let useFastRefresh = !ssr && (isJSX || code.includes(devRuntime));
2315
3203
  if (!useFastRefresh) return;
3204
+ if (isRouteVirtualModule(id)) {
3205
+ return { code: addRefreshWrapper(ctx.reactRouterConfig, code, id) };
3206
+ }
2316
3207
  let result = await babel.transformAsync(code, {
2317
3208
  babelrc: false,
2318
3209
  configFile: false,
@@ -2343,6 +3234,7 @@ var reactRouterVitePlugin = () => {
2343
3234
  let serverManifest = (await server.ssrLoadModule(virtual.serverManifest.id)).default;
2344
3235
  let oldRouteMetadata = serverManifest.routes[route.id];
2345
3236
  let newRouteMetadata = await getRouteMetadata(
3237
+ cache,
2346
3238
  ctx,
2347
3239
  viteChildCompiler,
2348
3240
  route,
@@ -2352,9 +3244,12 @@ var reactRouterVitePlugin = () => {
2352
3244
  if (!oldRouteMetadata || [
2353
3245
  "hasLoader",
2354
3246
  "hasClientLoader",
3247
+ "clientLoaderModule",
2355
3248
  "hasAction",
2356
3249
  "hasClientAction",
2357
- "hasErrorBoundary"
3250
+ "clientActionModule",
3251
+ "hasErrorBoundary",
3252
+ "hydrateFallbackModule"
2358
3253
  ].some((key) => oldRouteMetadata[key] !== newRouteMetadata[key])) {
2359
3254
  invalidateVirtualModules(server);
2360
3255
  }
@@ -2472,13 +3367,30 @@ function getRoute(pluginConfig, file) {
2472
3367
  );
2473
3368
  return route;
2474
3369
  }
2475
- async function getRouteMetadata(ctx, viteChildCompiler, route, readRouteFile) {
3370
+ function isRoute(pluginConfig, file) {
3371
+ return Boolean(getRoute(pluginConfig, file));
3372
+ }
3373
+ async function getRouteMetadata(cache, ctx, viteChildCompiler, route, readRouteFile) {
3374
+ let routeFile = route.file;
2476
3375
  let sourceExports = await getRouteModuleExports(
2477
3376
  viteChildCompiler,
2478
3377
  ctx,
2479
3378
  route.file,
2480
3379
  readRouteFile
2481
3380
  );
3381
+ let { hasRouteChunkByExportName } = await detectRouteChunksIfEnabled(
3382
+ cache,
3383
+ ctx,
3384
+ routeFile,
3385
+ { routeFile, readRouteFile, viteChildCompiler }
3386
+ );
3387
+ let moduleUrl = combineURLs(
3388
+ ctx.publicPath,
3389
+ `${resolveFileUrl(
3390
+ ctx,
3391
+ resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)
3392
+ )}`
3393
+ );
2482
3394
  let info = {
2483
3395
  id: route.id,
2484
3396
  parentId: route.parentId,
@@ -2492,14 +3404,11 @@ async function getRouteMetadata(ctx, viteChildCompiler, route, readRouteFile) {
2492
3404
  resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)
2493
3405
  )
2494
3406
  ),
2495
- module: combineURLs(
2496
- ctx.publicPath,
2497
- `${resolveFileUrl(
2498
- ctx,
2499
- resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)
2500
- )}?import`
2501
- ),
3407
+ module: `${moduleUrl}?import`,
2502
3408
  // Ensure the Vite dev server responds with a JS module
3409
+ clientActionModule: hasRouteChunkByExportName.clientAction ? `${getRouteChunkModuleId(moduleUrl, "clientAction")}` : void 0,
3410
+ clientLoaderModule: hasRouteChunkByExportName.clientLoader ? `${getRouteChunkModuleId(moduleUrl, "clientLoader")}` : void 0,
3411
+ hydrateFallbackModule: hasRouteChunkByExportName.HydrateFallback ? `${getRouteChunkModuleId(moduleUrl, "HydrateFallback")}` : void 0,
2503
3412
  hasAction: sourceExports.includes("action"),
2504
3413
  hasClientAction: sourceExports.includes("clientAction"),
2505
3414
  hasLoader: sourceExports.includes("loader"),
@@ -2509,6 +3418,12 @@ async function getRouteMetadata(ctx, viteChildCompiler, route, readRouteFile) {
2509
3418
  };
2510
3419
  return info;
2511
3420
  }
3421
+ function isPrerenderingEnabled(reactRouterConfig) {
3422
+ return reactRouterConfig.prerender != null && reactRouterConfig.prerender !== false;
3423
+ }
3424
+ function isSpaModeEnabled(reactRouterConfig) {
3425
+ return reactRouterConfig.ssr === false && !isPrerenderingEnabled(reactRouterConfig);
3426
+ }
2512
3427
  async function getPrerenderBuildAndHandler(viteConfig, serverBuildDirectory, serverBuildFile) {
2513
3428
  let serverBuildPath = path6.join(serverBuildDirectory, serverBuildFile);
2514
3429
  let build = await import(url.pathToFileURL(serverBuildPath).toString());
@@ -2519,20 +3434,49 @@ async function getPrerenderBuildAndHandler(viteConfig, serverBuildDirectory, ser
2519
3434
  };
2520
3435
  }
2521
3436
  async function handleSpaMode(viteConfig, reactRouterConfig, serverBuildDirectory, serverBuildFile, clientBuildDirectory) {
2522
- let { handler } = await getPrerenderBuildAndHandler(
3437
+ let { build, handler } = await getPrerenderBuildAndHandler(
2523
3438
  viteConfig,
2524
3439
  serverBuildDirectory,
2525
3440
  serverBuildFile
2526
3441
  );
2527
- let request = new Request(`http://localhost${reactRouterConfig.basename}`);
3442
+ let request = new Request(`http://localhost${reactRouterConfig.basename}`, {
3443
+ headers: {
3444
+ // Enable SPA mode in the server runtime and only render down to the root
3445
+ "X-React-Router-SPA-Mode": "yes"
3446
+ }
3447
+ });
2528
3448
  let response = await handler(request);
2529
3449
  let html = await response.text();
2530
- validatePrerenderedResponse(response, html, "SPA Mode", "/");
2531
- validatePrerenderedHtml(html, "SPA Mode");
2532
- await fse.writeFile(path6.join(clientBuildDirectory, "index.html"), html);
2533
- viteConfig.logger.info(
2534
- "SPA Mode: index.html has been written to your " + import_picocolors3.default.bold(path6.relative(process.cwd(), clientBuildDirectory)) + " directory"
2535
- );
3450
+ let isPrerenderSpaFallback = build.prerender.includes("/");
3451
+ let filename3 = isPrerenderSpaFallback ? "__spa-fallback.html" : "index.html";
3452
+ if (response.status !== 200) {
3453
+ if (isPrerenderSpaFallback) {
3454
+ throw new Error(
3455
+ `Prerender: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering your \`${filename3}\` file.
3456
+ ` + html
3457
+ );
3458
+ } else {
3459
+ throw new Error(
3460
+ `SPA Mode: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering your \`${filename3}\` file.
3461
+ ` + html
3462
+ );
3463
+ }
3464
+ }
3465
+ if (!html.includes("window.__reactRouterContext =") || !html.includes("window.__reactRouterRouteModules =")) {
3466
+ throw new Error(
3467
+ "SPA Mode: Did you forget to include `<Scripts/>` in your root route? Your pre-rendered HTML cannot hydrate without `<Scripts />`."
3468
+ );
3469
+ }
3470
+ await fse.writeFile(path6.join(clientBuildDirectory, filename3), html);
3471
+ let prettyDir = path6.relative(process.cwd(), clientBuildDirectory);
3472
+ let prettyPath = path6.join(prettyDir, filename3);
3473
+ if (build.prerender.length > 0) {
3474
+ viteConfig.logger.info(
3475
+ `Prerender (html): SPA Fallback -> ${import_picocolors3.default.bold(prettyPath)}`
3476
+ );
3477
+ } else {
3478
+ viteConfig.logger.info(`SPA Mode: Generated ${import_picocolors3.default.bold(prettyPath)}`);
3479
+ }
2536
3480
  }
2537
3481
  async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirectory, serverBuildPath, clientBuildDirectory) {
2538
3482
  let { build, handler } = await getPrerenderBuildAndHandler(
@@ -2541,53 +3485,62 @@ async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirecto
2541
3485
  serverBuildPath
2542
3486
  );
2543
3487
  let routes = createPrerenderRoutes(build.routes);
2544
- let routesToPrerender;
2545
- if (typeof reactRouterConfig.prerender === "boolean") {
2546
- invariant(reactRouterConfig.prerender, "Expected prerender:true");
2547
- routesToPrerender = determineStaticPrerenderRoutes(
2548
- routes,
2549
- viteConfig,
2550
- true
2551
- );
2552
- } else if (typeof reactRouterConfig.prerender === "function") {
2553
- routesToPrerender = await reactRouterConfig.prerender({
2554
- getStaticPaths: () => determineStaticPrerenderRoutes(routes, viteConfig, false)
2555
- });
2556
- } else {
2557
- routesToPrerender = reactRouterConfig.prerender || ["/"];
2558
- }
2559
3488
  let headers = {
2560
3489
  // Header that can be used in the loader to know if you're running at
2561
3490
  // build time or runtime
2562
3491
  "X-React-Router-Prerender": "yes"
2563
3492
  };
2564
- for (let path7 of routesToPrerender) {
3493
+ for (let path7 of build.prerender) {
2565
3494
  let matches = (0, import_react_router2.matchRoutes)(routes, `/${path7}/`.replace(/^\/\/+/, "/"));
2566
- let hasLoaders = matches?.some((m) => m.route.loader);
2567
- let data;
2568
- if (hasLoaders) {
2569
- data = await prerenderData(
2570
- handler,
2571
- path7,
2572
- clientBuildDirectory,
2573
- reactRouterConfig,
2574
- viteConfig,
2575
- { headers }
2576
- );
2577
- }
3495
+ invariant(
3496
+ matches,
3497
+ `Unable to prerender path because it does not match any routes: ${path7}`
3498
+ );
2578
3499
  let leafRoute = matches ? matches[matches.length - 1].route : null;
2579
3500
  let manifestRoute = leafRoute ? build.routes[leafRoute.id]?.module : null;
2580
- let isResourceRoute = manifestRoute && !manifestRoute.default && !manifestRoute.ErrorBoundary && manifestRoute.loader;
3501
+ let isResourceRoute = manifestRoute && !manifestRoute.default && !manifestRoute.ErrorBoundary;
2581
3502
  if (isResourceRoute) {
2582
- await prerenderResourceRoute(
2583
- handler,
2584
- path7,
2585
- clientBuildDirectory,
2586
- reactRouterConfig,
2587
- viteConfig,
2588
- { headers }
2589
- );
3503
+ invariant(leafRoute);
3504
+ invariant(manifestRoute);
3505
+ if (manifestRoute.loader) {
3506
+ await prerenderData(
3507
+ handler,
3508
+ path7,
3509
+ [leafRoute.id],
3510
+ clientBuildDirectory,
3511
+ reactRouterConfig,
3512
+ viteConfig,
3513
+ { headers }
3514
+ );
3515
+ await prerenderResourceRoute(
3516
+ handler,
3517
+ path7,
3518
+ clientBuildDirectory,
3519
+ reactRouterConfig,
3520
+ viteConfig,
3521
+ { headers }
3522
+ );
3523
+ } else {
3524
+ viteConfig.logger.warn(
3525
+ `\u26A0\uFE0F Skipping prerendering for resource route without a loader: ${leafRoute?.id}`
3526
+ );
3527
+ }
2590
3528
  } else {
3529
+ let hasLoaders = matches.some(
3530
+ (m) => build.assets.routes[m.route.id]?.hasLoader
3531
+ );
3532
+ let data;
3533
+ if (!isResourceRoute && hasLoaders) {
3534
+ data = await prerenderData(
3535
+ handler,
3536
+ path7,
3537
+ null,
3538
+ clientBuildDirectory,
3539
+ reactRouterConfig,
3540
+ viteConfig,
3541
+ { headers }
3542
+ );
3543
+ }
2591
3544
  await prerenderRoute(
2592
3545
  handler,
2593
3546
  path7,
@@ -2603,14 +3556,8 @@ async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirecto
2603
3556
  );
2604
3557
  }
2605
3558
  }
2606
- await prerenderManifest(
2607
- build,
2608
- clientBuildDirectory,
2609
- reactRouterConfig,
2610
- viteConfig
2611
- );
2612
3559
  }
2613
- function determineStaticPrerenderRoutes(routes, viteConfig, isBooleanUsage = false) {
3560
+ function getStaticPrerenderPaths(routes) {
2614
3561
  let paths = ["/"];
2615
3562
  let paramRoutes = [];
2616
3563
  function recurse(subtree, prefix = "") {
@@ -2630,28 +3577,33 @@ function determineStaticPrerenderRoutes(routes, viteConfig, isBooleanUsage = fal
2630
3577
  }
2631
3578
  }
2632
3579
  recurse(routes);
2633
- if (isBooleanUsage && paramRoutes.length > 0) {
2634
- viteConfig.logger.warn(
2635
- [
2636
- "\u26A0\uFE0F Paths with dynamic/splat params cannot be prerendered when using `prerender: true`.",
2637
- "You may want to use the `prerender()` API to prerender the following paths:",
2638
- ...paramRoutes.map((p) => " - " + p)
2639
- ].join("\n")
2640
- );
2641
- }
2642
- return paths.map((p) => p.replace(/\/\/+/g, "/").replace(/(.+)\/$/, "$1"));
3580
+ return {
3581
+ paths: paths.map((p) => p.replace(/\/\/+/g, "/").replace(/(.+)\/$/, "$1")),
3582
+ paramRoutes
3583
+ };
2643
3584
  }
2644
- async function prerenderData(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
3585
+ async function prerenderData(handler, prerenderPath, onlyRoutes, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
2645
3586
  let normalizedPath = `${reactRouterConfig.basename}${prerenderPath === "/" ? "/_root.data" : `${prerenderPath.replace(/\/$/, "")}.data`}`.replace(/\/\/+/g, "/");
2646
- let request = new Request(`http://localhost${normalizedPath}`, requestInit);
3587
+ let url2 = new URL(`http://localhost${normalizedPath}`);
3588
+ if (onlyRoutes?.length) {
3589
+ url2.searchParams.set("_routes", onlyRoutes.join(","));
3590
+ }
3591
+ let request = new Request(url2, requestInit);
2647
3592
  let response = await handler(request);
2648
3593
  let data = await response.text();
2649
- validatePrerenderedResponse(response, data, "Prerender", normalizedPath);
3594
+ if (response.status !== 200) {
3595
+ throw new Error(
3596
+ `Prerender (data): Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${path6}\` path.
3597
+ ${normalizedPath}`
3598
+ );
3599
+ }
2650
3600
  let outdir = path6.relative(process.cwd(), clientBuildDirectory);
2651
3601
  let outfile = path6.join(outdir, ...normalizedPath.split("/"));
2652
3602
  await fse.ensureDir(path6.dirname(outfile));
2653
3603
  await fse.outputFile(outfile, data);
2654
- viteConfig.logger.info(`Prerender: Generated ${import_picocolors3.default.bold(outfile)}`);
3604
+ viteConfig.logger.info(
3605
+ `Prerender (data): ${prerenderPath} -> ${import_picocolors3.default.bold(outfile)}`
3606
+ );
2655
3607
  return data;
2656
3608
  }
2657
3609
  async function prerenderRoute(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
@@ -2662,54 +3614,65 @@ async function prerenderRoute(handler, prerenderPath, clientBuildDirectory, reac
2662
3614
  let request = new Request(`http://localhost${normalizedPath}`, requestInit);
2663
3615
  let response = await handler(request);
2664
3616
  let html = await response.text();
2665
- validatePrerenderedResponse(response, html, "Prerender", normalizedPath);
2666
- if (!reactRouterConfig.ssr) {
2667
- validatePrerenderedHtml(html, "Prerender");
3617
+ if (response.status !== 200) {
3618
+ throw new Error(
3619
+ `Prerender (html): Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${normalizedPath}\` path.
3620
+ ${html}`
3621
+ );
2668
3622
  }
2669
3623
  let outdir = path6.relative(process.cwd(), clientBuildDirectory);
2670
3624
  let outfile = path6.join(outdir, ...normalizedPath.split("/"), "index.html");
2671
3625
  await fse.ensureDir(path6.dirname(outfile));
2672
3626
  await fse.outputFile(outfile, html);
2673
- viteConfig.logger.info(`Prerender: Generated ${import_picocolors3.default.bold(outfile)}`);
3627
+ viteConfig.logger.info(
3628
+ `Prerender (html): ${prerenderPath} -> ${import_picocolors3.default.bold(outfile)}`
3629
+ );
2674
3630
  }
2675
3631
  async function prerenderResourceRoute(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
2676
3632
  let normalizedPath = `${reactRouterConfig.basename}${prerenderPath}/`.replace(/\/\/+/g, "/").replace(/\/$/g, "");
2677
3633
  let request = new Request(`http://localhost${normalizedPath}`, requestInit);
2678
3634
  let response = await handler(request);
2679
3635
  let text = await response.text();
2680
- validatePrerenderedResponse(response, text, "Prerender", normalizedPath);
3636
+ if (response.status !== 200) {
3637
+ throw new Error(
3638
+ `Prerender (resource): Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${normalizedPath}\` path.
3639
+ ${text}`
3640
+ );
3641
+ }
2681
3642
  let outdir = path6.relative(process.cwd(), clientBuildDirectory);
2682
3643
  let outfile = path6.join(outdir, ...normalizedPath.split("/"));
2683
3644
  await fse.ensureDir(path6.dirname(outfile));
2684
3645
  await fse.outputFile(outfile, text);
2685
- viteConfig.logger.info(`Prerender: Generated ${import_picocolors3.default.bold(outfile)}`);
2686
- }
2687
- async function prerenderManifest(build, clientBuildDirectory, reactRouterConfig, viteConfig) {
2688
- let normalizedPath = `${reactRouterConfig.basename}/__manifest`.replace(
2689
- /\/\/+/g,
2690
- "/"
3646
+ viteConfig.logger.info(
3647
+ `Prerender (resource): ${prerenderPath} -> ${import_picocolors3.default.bold(outfile)}`
2691
3648
  );
2692
- let outdir = path6.relative(process.cwd(), clientBuildDirectory);
2693
- let outfile = path6.join(outdir, ...normalizedPath.split("/"));
2694
- await fse.ensureDir(path6.dirname(outfile));
2695
- let manifestData = JSON.stringify(build.assets.routes);
2696
- await fse.outputFile(outfile, manifestData);
2697
- viteConfig.logger.info(`Prerender: Generated ${import_picocolors3.default.bold(outfile)}`);
2698
3649
  }
2699
- function validatePrerenderedResponse(response, html, prefix, path7) {
2700
- if (response.status !== 200) {
2701
- throw new Error(
2702
- `${prefix}: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${path7}\` path.
2703
- ${html}`
2704
- );
2705
- }
2706
- }
2707
- function validatePrerenderedHtml(html, prefix) {
2708
- if (!html.includes("window.__reactRouterContext =") || !html.includes("window.__reactRouterRouteModules =")) {
2709
- throw new Error(
2710
- `${prefix}: Did you forget to include <Scripts/> in your root route? Your pre-rendered HTML files cannot hydrate without \`<Scripts />\`.`
2711
- );
3650
+ async function getPrerenderPaths(prerender, ssr, routes, logWarning = false) {
3651
+ let prerenderPaths = [];
3652
+ if (prerender != null && prerender !== false) {
3653
+ let prerenderRoutes = createPrerenderRoutes(routes);
3654
+ if (prerender === true) {
3655
+ let { paths, paramRoutes } = getStaticPrerenderPaths(prerenderRoutes);
3656
+ if (logWarning && !ssr && paramRoutes.length > 0) {
3657
+ console.warn(
3658
+ import_picocolors3.default.yellow(
3659
+ [
3660
+ "\u26A0\uFE0F Paths with dynamic/splat params cannot be prerendered when using `prerender: true`. You may want to use the `prerender()` API to prerender the following paths:",
3661
+ ...paramRoutes.map((p) => " - " + p)
3662
+ ].join("\n")
3663
+ )
3664
+ );
3665
+ }
3666
+ prerenderPaths = paths;
3667
+ } else if (typeof prerender === "function") {
3668
+ prerenderPaths = await prerender({
3669
+ getStaticPaths: () => getStaticPrerenderPaths(prerenderRoutes).paths
3670
+ });
3671
+ } else {
3672
+ prerenderPaths = prerender || ["/"];
3673
+ }
2712
3674
  }
3675
+ return prerenderPaths;
2713
3676
  }
2714
3677
  function groupRoutesByParentId2(manifest) {
2715
3678
  let routes = {};
@@ -2727,24 +3690,445 @@ function groupRoutesByParentId2(manifest) {
2727
3690
  function createPrerenderRoutes(manifest, parentId = "", routesByParentId = groupRoutesByParentId2(manifest)) {
2728
3691
  return (routesByParentId[parentId] || []).map((route) => {
2729
3692
  let commonRoute = {
2730
- // Always include root due to default boundaries
2731
- hasErrorBoundary: route.id === "root" || route.module.ErrorBoundary != null,
2732
3693
  id: route.id,
2733
- path: route.path,
2734
- loader: route.module.loader ? () => null : void 0,
2735
- action: void 0,
2736
- handle: route.module.handle
3694
+ path: route.path
2737
3695
  };
2738
- return route.index ? {
2739
- index: true,
2740
- ...commonRoute
2741
- } : {
2742
- caseSensitive: route.caseSensitive,
3696
+ if (route.index) {
3697
+ return {
3698
+ index: true,
3699
+ ...commonRoute
3700
+ };
3701
+ }
3702
+ return {
2743
3703
  children: createPrerenderRoutes(manifest, route.id, routesByParentId),
2744
3704
  ...commonRoute
2745
3705
  };
2746
3706
  });
2747
3707
  }
3708
+ async function validateSsrFalsePrerenderExports(viteConfig, ctx, manifest, viteChildCompiler) {
3709
+ let prerenderPaths = await getPrerenderPaths(
3710
+ ctx.reactRouterConfig.prerender,
3711
+ ctx.reactRouterConfig.ssr,
3712
+ manifest.routes,
3713
+ true
3714
+ );
3715
+ if (prerenderPaths.length === 0) {
3716
+ return;
3717
+ }
3718
+ let prerenderRoutes = createPrerenderRoutes(manifest.routes);
3719
+ let prerenderedRoutes = /* @__PURE__ */ new Set();
3720
+ for (let path7 of prerenderPaths) {
3721
+ let matches = (0, import_react_router2.matchRoutes)(
3722
+ prerenderRoutes,
3723
+ `/${path7}/`.replace(/^\/\/+/, "/")
3724
+ );
3725
+ invariant(
3726
+ matches,
3727
+ `Unable to prerender path because it does not match any routes: ${path7}`
3728
+ );
3729
+ matches.forEach((m) => prerenderedRoutes.add(m.route.id));
3730
+ }
3731
+ let errors = [];
3732
+ let routeExports = await getRouteManifestModuleExports(
3733
+ viteChildCompiler,
3734
+ ctx
3735
+ );
3736
+ for (let [routeId, route] of Object.entries(manifest.routes)) {
3737
+ let invalidApis = [];
3738
+ invariant(route, "Expected a route object in validateSsrFalseExports");
3739
+ let exports2 = routeExports[route.id];
3740
+ if (exports2.includes("headers")) invalidApis.push("headers");
3741
+ if (exports2.includes("action")) invalidApis.push("action");
3742
+ if (invalidApis.length > 0) {
3743
+ errors.push(
3744
+ `Prerender: ${invalidApis.length} invalid route export(s) in \`${route.id}\` when prerendering with \`ssr:false\`: ${invalidApis.join(", ")}. See https://reactrouter.com/how-to/pre-rendering for more information.`
3745
+ );
3746
+ }
3747
+ if (exports2.includes("loader") && !prerenderedRoutes.has(routeId)) {
3748
+ errors.push(
3749
+ `Prerender: 1 invalid route export in \`${route.id}\` when using \`ssr:false\` with \`prerender\` because the route is never prerendered so the loader will never be called. See https://reactrouter.com/how-to/pre-rendering for more information.`
3750
+ );
3751
+ }
3752
+ }
3753
+ if (errors.length > 0) {
3754
+ viteConfig.logger.error(import_picocolors3.default.red(errors.join("\n")));
3755
+ throw new Error(
3756
+ "Invalid route exports found when prerendering with `ssr:false`"
3757
+ );
3758
+ }
3759
+ }
3760
+ function getAddressableRoutes(routes) {
3761
+ let nonAddressableIds = /* @__PURE__ */ new Set();
3762
+ for (let id in routes) {
3763
+ let route = routes[id];
3764
+ if (route.index) {
3765
+ invariant(
3766
+ route.parentId,
3767
+ `Expected index route "${route.id}" to have "parentId" set`
3768
+ );
3769
+ nonAddressableIds.add(route.parentId);
3770
+ }
3771
+ if (typeof route.path !== "string" && !route.index) {
3772
+ nonAddressableIds.add(id);
3773
+ }
3774
+ }
3775
+ return Object.values(routes).filter(
3776
+ (route) => !nonAddressableIds.has(route.id)
3777
+ );
3778
+ }
3779
+ function getRouteBranch(routes, routeId) {
3780
+ let branch = [];
3781
+ let currentRouteId = routeId;
3782
+ while (currentRouteId) {
3783
+ let route = routes[currentRouteId];
3784
+ invariant(route, `Missing route for ${currentRouteId}`);
3785
+ branch.push(route);
3786
+ currentRouteId = route.parentId;
3787
+ }
3788
+ return branch.reverse();
3789
+ }
3790
+ function hasServerBundles(buildManifest) {
3791
+ return Object.keys(buildManifest.serverBundles ?? {}).length > 0;
3792
+ }
3793
+ function getRoutesByServerBundleId(buildManifest) {
3794
+ if (!buildManifest.routeIdToServerBundleId) {
3795
+ return {};
3796
+ }
3797
+ let routesByServerBundleId = {};
3798
+ for (let [routeId, serverBundleId] of Object.entries(
3799
+ buildManifest.routeIdToServerBundleId
3800
+ )) {
3801
+ routesByServerBundleId[serverBundleId] ??= {};
3802
+ let branch = getRouteBranch(buildManifest.routes, routeId);
3803
+ for (let route of branch) {
3804
+ routesByServerBundleId[serverBundleId][route.id] = route;
3805
+ }
3806
+ }
3807
+ return routesByServerBundleId;
3808
+ }
3809
+ var resolveRouteFileCode = async (ctx, input) => {
3810
+ if (typeof input === "string") return input;
3811
+ invariant(input.viteChildCompiler);
3812
+ return await compileRouteFile(
3813
+ input.viteChildCompiler,
3814
+ ctx,
3815
+ input.routeFile,
3816
+ input.readRouteFile
3817
+ );
3818
+ };
3819
+ async function detectRouteChunksIfEnabled(cache, ctx, id, input) {
3820
+ function noRouteChunks() {
3821
+ return {
3822
+ chunkedExports: [],
3823
+ hasRouteChunks: false,
3824
+ hasRouteChunkByExportName: {
3825
+ clientAction: false,
3826
+ clientLoader: false,
3827
+ HydrateFallback: false
3828
+ }
3829
+ };
3830
+ }
3831
+ if (!ctx.reactRouterConfig.future.unstable_splitRouteModules) {
3832
+ return noRouteChunks();
3833
+ }
3834
+ if (normalizeRelativeFilePath(id, ctx.reactRouterConfig) === ctx.reactRouterConfig.routes.root.file) {
3835
+ return noRouteChunks();
3836
+ }
3837
+ let code = await resolveRouteFileCode(ctx, input);
3838
+ if (!routeChunkExportNames.some((exportName) => code.includes(exportName))) {
3839
+ return noRouteChunks();
3840
+ }
3841
+ let cacheKey = normalizeRelativeFilePath(id, ctx.reactRouterConfig) + (typeof input === "string" ? "" : "?read");
3842
+ return detectRouteChunks(code, cache, cacheKey);
3843
+ }
3844
+ async function getRouteChunkIfEnabled(cache, ctx, id, chunkName, input) {
3845
+ if (!ctx.reactRouterConfig.future.unstable_splitRouteModules) {
3846
+ return null;
3847
+ }
3848
+ let code = await resolveRouteFileCode(ctx, input);
3849
+ let cacheKey = normalizeRelativeFilePath(id, ctx.reactRouterConfig) + (typeof input === "string" ? "" : "?read");
3850
+ return getRouteChunkCode(code, chunkName, cache, cacheKey);
3851
+ }
3852
+ function validateRouteChunks({
3853
+ ctx,
3854
+ id,
3855
+ valid
3856
+ }) {
3857
+ let invalidChunks = Object.entries(valid).filter(([_, isValid]) => !isValid).map(([chunkName]) => chunkName);
3858
+ if (invalidChunks.length === 0) {
3859
+ return;
3860
+ }
3861
+ let plural = invalidChunks.length > 1;
3862
+ throw new Error(
3863
+ [
3864
+ `Error splitting route module: ${normalizeRelativeFilePath(
3865
+ id,
3866
+ ctx.reactRouterConfig
3867
+ )}`,
3868
+ invalidChunks.map((name) => `- ${name}`).join("\n"),
3869
+ `${plural ? "These exports" : "This export"} could not be split into ${plural ? "their own chunks" : "its own chunk"} because ${plural ? "they share" : "it shares"} code with other exports. You should extract any shared code into its own module and then import it within the route module.`
3870
+ ].join("\n\n")
3871
+ );
3872
+ }
3873
+ async function cleanBuildDirectory(viteConfig, ctx) {
3874
+ let buildDirectory = ctx.reactRouterConfig.buildDirectory;
3875
+ let isWithinRoot = () => {
3876
+ let relativePath = path6.relative(ctx.rootDirectory, buildDirectory);
3877
+ return !relativePath.startsWith("..") && !path6.isAbsolute(relativePath);
3878
+ };
3879
+ if (viteConfig.build.emptyOutDir ?? isWithinRoot()) {
3880
+ await fse.remove(buildDirectory);
3881
+ }
3882
+ }
3883
+ async function cleanViteManifests(environmentsOptions, ctx) {
3884
+ let viteManifestPaths = Object.entries(environmentsOptions).map(
3885
+ ([environmentName, options]) => {
3886
+ let outDir = options.build?.outDir;
3887
+ invariant(outDir, `Expected build.outDir for ${environmentName}`);
3888
+ return path6.join(outDir, ".vite/manifest.json");
3889
+ }
3890
+ );
3891
+ await Promise.all(
3892
+ viteManifestPaths.map(async (viteManifestPath) => {
3893
+ let manifestExists = await fse.pathExists(viteManifestPath);
3894
+ if (!manifestExists) return;
3895
+ if (!ctx.viteManifestEnabled) {
3896
+ await fse.remove(viteManifestPath);
3897
+ }
3898
+ let viteDir = path6.dirname(viteManifestPath);
3899
+ let viteDirFiles = await fse.readdir(viteDir);
3900
+ if (viteDirFiles.length === 0) {
3901
+ await fse.remove(viteDir);
3902
+ }
3903
+ })
3904
+ );
3905
+ }
3906
+ async function getBuildManifest(ctx) {
3907
+ let { routes, serverBundles, appDirectory } = ctx.reactRouterConfig;
3908
+ if (!serverBundles) {
3909
+ return { routes };
3910
+ }
3911
+ let { normalizePath } = await import("vite");
3912
+ let serverBuildDirectory = getServerBuildDirectory(ctx);
3913
+ let resolvedAppDirectory = path6.resolve(ctx.rootDirectory, appDirectory);
3914
+ let rootRelativeRoutes = Object.fromEntries(
3915
+ Object.entries(routes).map(([id, route]) => {
3916
+ let filePath = path6.join(resolvedAppDirectory, route.file);
3917
+ let rootRelativeFilePath = normalizePath(
3918
+ path6.relative(ctx.rootDirectory, filePath)
3919
+ );
3920
+ return [id, { ...route, file: rootRelativeFilePath }];
3921
+ })
3922
+ );
3923
+ let buildManifest = {
3924
+ serverBundles: {},
3925
+ routeIdToServerBundleId: {},
3926
+ routes: rootRelativeRoutes
3927
+ };
3928
+ await Promise.all(
3929
+ getAddressableRoutes(routes).map(async (route) => {
3930
+ let branch = getRouteBranch(routes, route.id);
3931
+ let serverBundleId = await serverBundles({
3932
+ branch: branch.map(
3933
+ (route2) => configRouteToBranchRoute({
3934
+ ...route2,
3935
+ // Ensure absolute paths are passed to the serverBundles function
3936
+ file: path6.join(resolvedAppDirectory, route2.file)
3937
+ })
3938
+ )
3939
+ });
3940
+ if (typeof serverBundleId !== "string") {
3941
+ throw new Error(`The "serverBundles" function must return a string`);
3942
+ }
3943
+ if (!/^[a-zA-Z0-9-_]+$/.test(serverBundleId)) {
3944
+ throw new Error(
3945
+ `The "serverBundles" function must only return strings containing alphanumeric characters, hyphens and underscores.`
3946
+ );
3947
+ }
3948
+ buildManifest.routeIdToServerBundleId[route.id] = serverBundleId;
3949
+ buildManifest.serverBundles[serverBundleId] ??= {
3950
+ id: serverBundleId,
3951
+ file: normalizePath(
3952
+ path6.join(
3953
+ path6.relative(
3954
+ ctx.rootDirectory,
3955
+ path6.join(serverBuildDirectory, serverBundleId)
3956
+ ),
3957
+ ctx.reactRouterConfig.serverBuildFile
3958
+ )
3959
+ )
3960
+ };
3961
+ })
3962
+ );
3963
+ return buildManifest;
3964
+ }
3965
+ function mergeEnvironmentOptions(base, ...overrides) {
3966
+ let vite2 = getVite();
3967
+ return overrides.reduce(
3968
+ (merged, override) => vite2.mergeConfig(merged, override, false),
3969
+ base
3970
+ );
3971
+ }
3972
+ async function getEnvironmentOptionsResolvers(ctx, buildManifest, viteCommand) {
3973
+ let { serverBuildFile, serverModuleFormat } = ctx.reactRouterConfig;
3974
+ let packageRoot = path6.dirname(
3975
+ require.resolve("@react-router/dev/package.json")
3976
+ );
3977
+ let { moduleSyncEnabled } = await import(`file:///${path6.join(packageRoot, "module-sync-enabled/index.mjs")}`);
3978
+ let vite2 = getVite();
3979
+ let viteServerConditions = [
3980
+ ...vite2.defaultServerConditions ?? [],
3981
+ ...moduleSyncEnabled ? ["module-sync"] : []
3982
+ ];
3983
+ function getBaseOptions({
3984
+ viteUserConfig
3985
+ }) {
3986
+ return {
3987
+ build: {
3988
+ cssMinify: viteUserConfig.build?.cssMinify ?? true,
3989
+ manifest: true,
3990
+ // The manifest is enabled for all builds to detect SSR-only assets
3991
+ rollupOptions: {
3992
+ preserveEntrySignatures: "exports-only",
3993
+ // Silence Rollup "use client" warnings
3994
+ // Adapted from https://github.com/vitejs/vite-plugin-react/pull/144
3995
+ onwarn(warning, defaultHandler) {
3996
+ if (warning.code === "MODULE_LEVEL_DIRECTIVE" && warning.message.includes("use client")) {
3997
+ return;
3998
+ }
3999
+ let userHandler = viteUserConfig.build?.rollupOptions?.onwarn;
4000
+ if (userHandler) {
4001
+ userHandler(warning, defaultHandler);
4002
+ } else {
4003
+ defaultHandler(warning);
4004
+ }
4005
+ }
4006
+ }
4007
+ }
4008
+ };
4009
+ }
4010
+ function getBaseServerOptions({
4011
+ viteUserConfig
4012
+ }) {
4013
+ let conditions = viteCommand === "build" ? viteServerConditions : ["development", ...viteServerConditions];
4014
+ return mergeEnvironmentOptions(getBaseOptions({ viteUserConfig }), {
4015
+ resolve: {
4016
+ external: ssrExternals,
4017
+ conditions,
4018
+ externalConditions: conditions
4019
+ },
4020
+ build: {
4021
+ // We move SSR-only assets to client assets. Note that the
4022
+ // SSR build can also emit code-split JS files (e.g. by
4023
+ // dynamic import) under the same assets directory
4024
+ // regardless of "ssrEmitAssets" option, so we also need to
4025
+ // keep these JS files have to be kept as-is.
4026
+ ssrEmitAssets: true,
4027
+ copyPublicDir: false,
4028
+ // Assets in the public directory are only used by the client
4029
+ rollupOptions: {
4030
+ output: {
4031
+ entryFileNames: serverBuildFile,
4032
+ format: serverModuleFormat
4033
+ }
4034
+ }
4035
+ }
4036
+ });
4037
+ }
4038
+ let environmentOptionsResolvers = {
4039
+ client: ({ viteUserConfig }) => mergeEnvironmentOptions(getBaseOptions({ viteUserConfig }), {
4040
+ build: {
4041
+ rollupOptions: {
4042
+ input: [
4043
+ ctx.entryClientFilePath,
4044
+ ...Object.values(ctx.reactRouterConfig.routes).flatMap(
4045
+ (route) => {
4046
+ let routeFilePath = path6.resolve(
4047
+ ctx.reactRouterConfig.appDirectory,
4048
+ route.file
4049
+ );
4050
+ let isRootRoute = route.file === ctx.reactRouterConfig.routes.root.file;
4051
+ let code = fse.readFileSync(routeFilePath, "utf-8");
4052
+ return [
4053
+ `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
4054
+ ...ctx.reactRouterConfig.future.unstable_splitRouteModules && !isRootRoute ? routeChunkExportNames.map(
4055
+ (exportName) => code.includes(exportName) ? getRouteChunkModuleId(routeFilePath, exportName) : null
4056
+ ) : []
4057
+ ].filter(isNonNullable);
4058
+ }
4059
+ )
4060
+ ],
4061
+ output: {
4062
+ entryFileNames({ moduleIds }) {
4063
+ let routeChunkModuleId = moduleIds.find(isRouteChunkModuleId);
4064
+ let routeChunkName = routeChunkModuleId ? getRouteChunkNameFromModuleId(routeChunkModuleId) : null;
4065
+ let routeChunkSuffix = routeChunkName ? `-${(0, import_kebabCase.default)(routeChunkName)}` : "";
4066
+ return `assets/[name]${routeChunkSuffix}-[hash].js`;
4067
+ }
4068
+ }
4069
+ },
4070
+ outDir: getClientBuildDirectory(ctx.reactRouterConfig)
4071
+ }
4072
+ })
4073
+ };
4074
+ if (hasServerBundles(buildManifest)) {
4075
+ for (let [serverBundleId, routes] of Object.entries(
4076
+ getRoutesByServerBundleId(buildManifest)
4077
+ )) {
4078
+ const serverBundleEnvironmentId = serverBundleId.replaceAll("-", "_");
4079
+ const environmentName = `${SSR_BUNDLE_PREFIX}${serverBundleEnvironmentId}`;
4080
+ environmentOptionsResolvers[environmentName] = ({ viteUserConfig }) => mergeEnvironmentOptions(
4081
+ getBaseServerOptions({ viteUserConfig }),
4082
+ {
4083
+ build: {
4084
+ outDir: getServerBuildDirectory(ctx, { serverBundleId }),
4085
+ rollupOptions: {
4086
+ input: `${virtual.serverBuild.id}?route-ids=${Object.keys(
4087
+ routes
4088
+ ).join(",")}`
4089
+ }
4090
+ }
4091
+ },
4092
+ // Ensure server bundle environments extend the user's SSR
4093
+ // environment config if it exists
4094
+ viteUserConfig.environments?.ssr ?? {}
4095
+ );
4096
+ }
4097
+ } else {
4098
+ environmentOptionsResolvers.ssr = ({ viteUserConfig }) => mergeEnvironmentOptions(getBaseServerOptions({ viteUserConfig }), {
4099
+ build: {
4100
+ outDir: getServerBuildDirectory(ctx),
4101
+ rollupOptions: {
4102
+ input: (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi ? viteUserConfig.environments?.ssr?.build?.rollupOptions?.input : viteUserConfig.build?.rollupOptions?.input) ?? virtual.serverBuild.id
4103
+ }
4104
+ }
4105
+ });
4106
+ }
4107
+ return environmentOptionsResolvers;
4108
+ }
4109
+ function resolveEnvironmentsOptions(environmentResolvers, resolverOptions) {
4110
+ let environmentOptions = {};
4111
+ for (let [environmentName, resolver] of Object.entries(
4112
+ environmentResolvers
4113
+ )) {
4114
+ environmentOptions[environmentName] = resolver(resolverOptions);
4115
+ }
4116
+ return environmentOptions;
4117
+ }
4118
+ async function getEnvironmentsOptions(ctx, buildManifest, viteCommand, resolverOptions) {
4119
+ let environmentOptionsResolvers = await getEnvironmentOptionsResolvers(
4120
+ ctx,
4121
+ buildManifest,
4122
+ viteCommand
4123
+ );
4124
+ return resolveEnvironmentsOptions(
4125
+ environmentOptionsResolvers,
4126
+ resolverOptions
4127
+ );
4128
+ }
4129
+ function isNonNullable(x) {
4130
+ return x != null;
4131
+ }
2748
4132
  // Annotate the CommonJS export names for ESM import in node:
2749
4133
  0 && (module.exports = {
2750
4134
  reactRouter