@react-router/dev 7.5.3 → 7.6.0-pre.0

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/cli/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @react-router/dev v7.5.3
3
+ * @react-router/dev v7.6.0-pre.0
4
4
  *
5
5
  * Copyright (c) Remix Software Inc.
6
6
  *
@@ -125,13 +125,15 @@ var init_ssr_externals = __esm({
125
125
  // vite/vite-node.ts
126
126
  async function createContext({
127
127
  root,
128
- mode
128
+ mode,
129
+ customLogger
129
130
  }) {
130
131
  await preloadVite();
131
132
  const vite2 = getVite();
132
133
  const devServer = await vite2.createServer({
133
134
  root,
134
135
  mode,
136
+ customLogger,
135
137
  server: {
136
138
  preTransformRequests: false,
137
139
  hmr: false,
@@ -143,6 +145,15 @@ async function createContext({
143
145
  optimizeDeps: {
144
146
  noDiscovery: true
145
147
  },
148
+ css: {
149
+ // This empty PostCSS config object prevents the PostCSS config file from
150
+ // being loaded. We don't need it in a React Router config context, and
151
+ // there's also an issue in Vite 5 when using a .ts PostCSS config file in
152
+ // an ESM project: https://github.com/vitejs/vite/issues/15869. Consumers
153
+ // can work around this in their own Vite config file, but they can't
154
+ // configure this internal usage of vite-node.
155
+ postcss: {}
156
+ },
146
157
  configFile: false,
147
158
  envFile: false,
148
159
  plugins: []
@@ -205,7 +216,7 @@ function validateRouteConfig({
205
216
  `Route config in "${routeConfigFile}" is invalid.`,
206
217
  root ? `${root}` : [],
207
218
  nested ? Object.entries(nested).map(
208
- ([path9, message]) => `Path: routes.${path9}
219
+ ([path8, message]) => `Path: routes.${path8}
209
220
  ${message}`
210
221
  ) : []
211
222
  ].flat().join("\n\n")
@@ -290,7 +301,8 @@ function err(error) {
290
301
  async function resolveConfig({
291
302
  root,
292
303
  viteNodeContext,
293
- reactRouterConfigFile
304
+ reactRouterConfigFile,
305
+ skipRoutes
294
306
  }) {
295
307
  let reactRouterUserConfig = {};
296
308
  if (reactRouterConfigFile) {
@@ -339,12 +351,17 @@ async function resolveConfig({
339
351
  serverModuleFormat: "esm",
340
352
  ssr: true
341
353
  };
354
+ let userAndPresetConfigs = mergeReactRouterConfig(
355
+ ...presets,
356
+ reactRouterUserConfig
357
+ );
342
358
  let {
343
359
  appDirectory: userAppDirectory,
344
360
  basename: basename2,
345
361
  buildDirectory: userBuildDirectory,
346
362
  buildEnd,
347
363
  prerender,
364
+ routeDiscovery: userRouteDiscovery,
348
365
  serverBuildFile,
349
366
  serverBundles,
350
367
  serverModuleFormat,
@@ -352,7 +369,7 @@ async function resolveConfig({
352
369
  } = {
353
370
  ...defaults,
354
371
  // Default values should be completely overridden by user/preset config, not merged
355
- ...mergeReactRouterConfig(...presets, reactRouterUserConfig)
372
+ ...userAndPresetConfigs
356
373
  };
357
374
  if (!ssr && serverBundles) {
358
375
  serverBundles = void 0;
@@ -363,6 +380,32 @@ async function resolveConfig({
363
380
  "The `prerender` config must be a boolean, an array of string paths, or a function returning a boolean or array of string paths"
364
381
  );
365
382
  }
383
+ let routeDiscovery;
384
+ if (userRouteDiscovery == null) {
385
+ if (ssr) {
386
+ routeDiscovery = {
387
+ mode: "lazy",
388
+ manifestPath: "/__manifest"
389
+ };
390
+ } else {
391
+ routeDiscovery = { mode: "initial" };
392
+ }
393
+ } else if (userRouteDiscovery.mode === "initial") {
394
+ routeDiscovery = userRouteDiscovery;
395
+ } else if (userRouteDiscovery.mode === "lazy") {
396
+ if (!ssr) {
397
+ return err(
398
+ 'The `routeDiscovery.mode` config cannot be set to "lazy" when setting `ssr:false`'
399
+ );
400
+ }
401
+ let { manifestPath } = userRouteDiscovery;
402
+ if (manifestPath != null && !manifestPath.startsWith("/")) {
403
+ return err(
404
+ 'The `routeDiscovery.manifestPath` config must be a root-relative pathname beginning with a slash (i.e., "/__manifest")'
405
+ );
406
+ }
407
+ routeDiscovery = userRouteDiscovery;
408
+ }
366
409
  let appDirectory = import_pathe3.default.resolve(root, userAppDirectory || "app");
367
410
  let buildDirectory = import_pathe3.default.resolve(root, userBuildDirectory);
368
411
  let rootRouteFile = findEntry(appDirectory, "root");
@@ -375,45 +418,50 @@ async function resolveConfig({
375
418
  `Could not find a root route module in the app directory as "${rootRouteDisplayPath}"`
376
419
  );
377
420
  }
378
- let routes2 = {
379
- root: { path: "", id: "root", file: rootRouteFile }
380
- };
381
- let routeConfigFile = findEntry(appDirectory, "routes");
382
- try {
383
- if (!routeConfigFile) {
384
- let routeConfigDisplayPath = import_pathe3.default.relative(
385
- root,
386
- import_pathe3.default.join(appDirectory, "routes.ts")
387
- );
388
- return err(`Route config file not found at "${routeConfigDisplayPath}".`);
389
- }
390
- setAppDirectory(appDirectory);
391
- let routeConfigExport = (await viteNodeContext.runner.executeFile(
392
- import_pathe3.default.join(appDirectory, routeConfigFile)
393
- )).default;
394
- let routeConfig = await routeConfigExport;
395
- let result = validateRouteConfig({
396
- routeConfigFile,
397
- routeConfig
398
- });
399
- if (!result.valid) {
400
- return err(result.message);
401
- }
421
+ let routes2 = {};
422
+ if (!skipRoutes) {
402
423
  routes2 = {
403
- ...routes2,
404
- ...configRoutesToRouteManifest(appDirectory, routeConfig)
424
+ root: { path: "", id: "root", file: rootRouteFile }
405
425
  };
406
- } catch (error) {
407
- return err(
408
- [
409
- import_picocolors.default.red(`Route config in "${routeConfigFile}" is invalid.`),
410
- "",
411
- error.loc?.file && error.loc?.column && error.frame ? [
412
- import_pathe3.default.relative(appDirectory, error.loc.file) + ":" + error.loc.line + ":" + error.loc.column,
413
- error.frame.trim?.()
414
- ] : error.stack
415
- ].flat().join("\n")
416
- );
426
+ let routeConfigFile = findEntry(appDirectory, "routes");
427
+ try {
428
+ if (!routeConfigFile) {
429
+ let routeConfigDisplayPath = import_pathe3.default.relative(
430
+ root,
431
+ import_pathe3.default.join(appDirectory, "routes.ts")
432
+ );
433
+ return err(
434
+ `Route config file not found at "${routeConfigDisplayPath}".`
435
+ );
436
+ }
437
+ setAppDirectory(appDirectory);
438
+ let routeConfigExport = (await viteNodeContext.runner.executeFile(
439
+ import_pathe3.default.join(appDirectory, routeConfigFile)
440
+ )).default;
441
+ let routeConfig = await routeConfigExport;
442
+ let result = validateRouteConfig({
443
+ routeConfigFile,
444
+ routeConfig
445
+ });
446
+ if (!result.valid) {
447
+ return err(result.message);
448
+ }
449
+ routes2 = {
450
+ ...routes2,
451
+ ...configRoutesToRouteManifest(appDirectory, routeConfig)
452
+ };
453
+ } catch (error) {
454
+ return err(
455
+ [
456
+ import_picocolors.default.red(`Route config in "${routeConfigFile}" is invalid.`),
457
+ "",
458
+ error.loc?.file && error.loc?.column && error.frame ? [
459
+ import_pathe3.default.relative(appDirectory, error.loc.file) + ":" + error.loc.line + ":" + error.loc.column,
460
+ error.frame.trim?.()
461
+ ] : error.stack
462
+ ].flat().join("\n")
463
+ );
464
+ }
417
465
  }
418
466
  let future = {
419
467
  unstable_middleware: reactRouterUserConfig.future?.unstable_middleware ?? false,
@@ -430,6 +478,7 @@ async function resolveConfig({
430
478
  future,
431
479
  prerender,
432
480
  routes: routes2,
481
+ routeDiscovery,
433
482
  serverBuildFile,
434
483
  serverBundles,
435
484
  serverModuleFormat,
@@ -442,24 +491,35 @@ async function resolveConfig({
442
491
  }
443
492
  async function createConfigLoader({
444
493
  rootDirectory: root,
445
- watch: watch2
494
+ watch: watch2,
495
+ mode,
496
+ skipRoutes
446
497
  }) {
447
- root = root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd();
498
+ root = import_pathe3.default.normalize(root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd());
499
+ let vite2 = await import("vite");
448
500
  let viteNodeContext = await createContext({
449
501
  root,
450
- mode: watch2 ? "development" : "production"
451
- });
452
- let reactRouterConfigFile = findEntry(root, "react-router.config", {
453
- absolute: true
502
+ mode,
503
+ // Filter out any info level logs from vite-node
504
+ customLogger: vite2.createLogger("warn", {
505
+ prefix: "[react-router]"
506
+ })
454
507
  });
455
- let getConfig = () => resolveConfig({ root, viteNodeContext, reactRouterConfigFile });
508
+ let reactRouterConfigFile;
509
+ let updateReactRouterConfigFile = () => {
510
+ reactRouterConfigFile = findEntry(root, "react-router.config", {
511
+ absolute: true
512
+ });
513
+ };
514
+ updateReactRouterConfigFile();
515
+ let getConfig = () => resolveConfig({ root, viteNodeContext, reactRouterConfigFile, skipRoutes });
456
516
  let appDirectory;
457
517
  let initialConfigResult = await getConfig();
458
518
  if (!initialConfigResult.ok) {
459
519
  throw new Error(initialConfigResult.error);
460
520
  }
461
- appDirectory = initialConfigResult.value.appDirectory;
462
- let lastConfig = initialConfigResult.value;
521
+ appDirectory = import_pathe3.default.normalize(initialConfigResult.value.appDirectory);
522
+ let currentConfig = initialConfigResult.value;
463
523
  let fsWatcher;
464
524
  let changeHandlers = [];
465
525
  return {
@@ -472,41 +532,71 @@ async function createConfigLoader({
472
532
  }
473
533
  changeHandlers.push(handler);
474
534
  if (!fsWatcher) {
475
- fsWatcher = import_chokidar.default.watch(
476
- [
477
- ...reactRouterConfigFile ? [reactRouterConfigFile] : [],
478
- appDirectory
479
- ],
480
- { ignoreInitial: true }
481
- );
535
+ fsWatcher = import_chokidar.default.watch([root, appDirectory], {
536
+ ignoreInitial: true,
537
+ ignored: (path8) => {
538
+ let dirname6 = import_pathe3.default.dirname(path8);
539
+ return !dirname6.startsWith(appDirectory) && // Ensure we're only watching files outside of the app directory
540
+ // that are at the root level, not nested in subdirectories
541
+ path8 !== root && // Watch the root directory itself
542
+ dirname6 !== root;
543
+ }
544
+ });
482
545
  fsWatcher.on("all", async (...args) => {
483
546
  let [event, rawFilepath] = args;
484
547
  let filepath = import_pathe3.default.normalize(rawFilepath);
485
- let appFileAddedOrRemoved = appDirectory && (event === "add" || event === "unlink") && filepath.startsWith(import_pathe3.default.normalize(appDirectory));
486
- let configCodeUpdated = Boolean(
548
+ let fileAddedOrRemoved = event === "add" || event === "unlink";
549
+ let appFileAddedOrRemoved = fileAddedOrRemoved && filepath.startsWith(import_pathe3.default.normalize(appDirectory));
550
+ let rootRelativeFilepath = import_pathe3.default.relative(root, filepath);
551
+ let configFileAddedOrRemoved = fileAddedOrRemoved && isEntryFile("react-router.config", rootRelativeFilepath);
552
+ if (configFileAddedOrRemoved) {
553
+ updateReactRouterConfigFile();
554
+ }
555
+ let moduleGraphChanged = configFileAddedOrRemoved || Boolean(
487
556
  viteNodeContext.devServer?.moduleGraph.getModuleById(filepath)
488
557
  );
489
- if (configCodeUpdated || appFileAddedOrRemoved) {
490
- viteNodeContext.devServer?.moduleGraph.invalidateAll();
491
- viteNodeContext.runner?.moduleCache.clear();
558
+ if (!moduleGraphChanged && !appFileAddedOrRemoved) {
559
+ return;
492
560
  }
493
- if (appFileAddedOrRemoved || configCodeUpdated) {
494
- let result = await getConfig();
495
- let configChanged = result.ok && !(0, import_isEqual.default)(lastConfig, result.value);
496
- let routeConfigChanged = result.ok && !(0, import_isEqual.default)(lastConfig?.routes, result.value.routes);
497
- for (let handler2 of changeHandlers) {
498
- handler2({
499
- result,
500
- configCodeUpdated,
501
- configChanged,
502
- routeConfigChanged,
503
- path: filepath,
504
- event
505
- });
506
- }
507
- if (result.ok) {
508
- lastConfig = result.value;
509
- }
561
+ viteNodeContext.devServer?.moduleGraph.invalidateAll();
562
+ viteNodeContext.runner?.moduleCache.clear();
563
+ let result = await getConfig();
564
+ let prevAppDirectory = appDirectory;
565
+ appDirectory = import_pathe3.default.normalize(
566
+ (result.value ?? currentConfig).appDirectory
567
+ );
568
+ if (appDirectory !== prevAppDirectory) {
569
+ fsWatcher.unwatch(prevAppDirectory);
570
+ fsWatcher.add(appDirectory);
571
+ }
572
+ let configCodeChanged = configFileAddedOrRemoved || reactRouterConfigFile !== void 0 && isEntryFileDependency(
573
+ viteNodeContext.devServer.moduleGraph,
574
+ reactRouterConfigFile,
575
+ filepath
576
+ );
577
+ let routeConfigFile = !skipRoutes ? findEntry(appDirectory, "routes", {
578
+ absolute: true
579
+ }) : void 0;
580
+ let routeConfigCodeChanged = routeConfigFile !== void 0 && isEntryFileDependency(
581
+ viteNodeContext.devServer.moduleGraph,
582
+ routeConfigFile,
583
+ filepath
584
+ );
585
+ let configChanged = result.ok && !(0, import_isEqual.default)(omitRoutes(currentConfig), omitRoutes(result.value));
586
+ let routeConfigChanged = result.ok && !(0, import_isEqual.default)(currentConfig?.routes, result.value.routes);
587
+ for (let handler2 of changeHandlers) {
588
+ handler2({
589
+ result,
590
+ configCodeChanged,
591
+ routeConfigCodeChanged,
592
+ configChanged,
593
+ routeConfigChanged,
594
+ path: filepath,
595
+ event
596
+ });
597
+ }
598
+ if (result.ok) {
599
+ currentConfig = result.value;
510
600
  }
511
601
  });
512
602
  }
@@ -523,23 +613,73 @@ async function createConfigLoader({
523
613
  }
524
614
  };
525
615
  }
526
- async function loadConfig({ rootDirectory }) {
616
+ async function loadConfig({
617
+ rootDirectory,
618
+ mode,
619
+ skipRoutes
620
+ }) {
527
621
  let configLoader = await createConfigLoader({
528
622
  rootDirectory,
623
+ mode,
624
+ skipRoutes,
529
625
  watch: false
530
626
  });
531
627
  let config = await configLoader.getConfig();
532
628
  await configLoader.close();
533
629
  return config;
534
630
  }
631
+ function omitRoutes(config) {
632
+ return {
633
+ ...config,
634
+ routes: {}
635
+ };
636
+ }
637
+ function isEntryFile(entryBasename, filename3) {
638
+ return entryExts.some((ext) => filename3 === `${entryBasename}${ext}`);
639
+ }
535
640
  function findEntry(dir, basename2, options) {
536
- for (let ext of entryExts) {
537
- let file = import_pathe3.default.resolve(dir, basename2 + ext);
538
- if (import_node_fs.default.existsSync(file)) {
539
- return options?.absolute ?? false ? file : import_pathe3.default.relative(dir, file);
641
+ let currentDir = import_pathe3.default.resolve(dir);
642
+ let { root } = import_pathe3.default.parse(currentDir);
643
+ while (true) {
644
+ for (let ext of options?.extensions ?? entryExts) {
645
+ let file = import_pathe3.default.resolve(currentDir, basename2 + ext);
646
+ if (import_node_fs.default.existsSync(file)) {
647
+ return options?.absolute ?? false ? file : import_pathe3.default.relative(dir, file);
648
+ }
649
+ }
650
+ if (!options?.walkParents) {
651
+ return void 0;
652
+ }
653
+ let parentDir = import_pathe3.default.dirname(currentDir);
654
+ if (currentDir === root || parentDir === currentDir) {
655
+ return void 0;
656
+ }
657
+ currentDir = parentDir;
658
+ }
659
+ }
660
+ function isEntryFileDependency(moduleGraph, entryFilepath, filepath, visited = /* @__PURE__ */ new Set()) {
661
+ entryFilepath = import_pathe3.default.normalize(entryFilepath);
662
+ filepath = import_pathe3.default.normalize(filepath);
663
+ if (visited.has(filepath)) {
664
+ return false;
665
+ }
666
+ visited.add(filepath);
667
+ if (filepath === entryFilepath) {
668
+ return true;
669
+ }
670
+ let mod = moduleGraph.getModuleById(filepath);
671
+ if (!mod) {
672
+ return false;
673
+ }
674
+ for (let importer of mod.importers) {
675
+ if (!importer.id) {
676
+ continue;
677
+ }
678
+ if (importer.id === entryFilepath || isEntryFileDependency(moduleGraph, entryFilepath, importer.id, visited)) {
679
+ return true;
540
680
  }
541
681
  }
542
- return void 0;
682
+ return false;
543
683
  }
544
684
  var import_node_fs, import_node_child_process, import_package_json, import_pathe3, import_chokidar, import_picocolors, import_pick2, import_omit, import_cloneDeep, import_isEqual, excludedConfigPresetKeys, mergeReactRouterConfig, deepFreeze, entryExts;
545
685
  var init_config = __esm({
@@ -661,21 +801,21 @@ var init_babel = __esm({
661
801
 
662
802
  // typegen/paths.ts
663
803
  function getTypesDir(ctx) {
664
- return Path2.join(ctx.rootDirectory, ".react-router/types");
804
+ return Path3.join(ctx.rootDirectory, ".react-router/types");
665
805
  }
666
806
  function getTypesPath(ctx, route) {
667
- return Path2.join(
807
+ return Path3.join(
668
808
  getTypesDir(ctx),
669
- Path2.relative(ctx.rootDirectory, ctx.config.appDirectory),
670
- Path2.dirname(route.file),
809
+ Path3.relative(ctx.rootDirectory, ctx.config.appDirectory),
810
+ Path3.dirname(route.file),
671
811
  "+types/" + Pathe.filename(route.file) + ".ts"
672
812
  );
673
813
  }
674
- var Path2, Pathe;
814
+ var Path3, Pathe;
675
815
  var init_paths = __esm({
676
816
  "typegen/paths.ts"() {
677
817
  "use strict";
678
- Path2 = __toESM(require("pathe"));
818
+ Path3 = __toESM(require("pathe"));
679
819
  Pathe = __toESM(require("pathe/utils"));
680
820
  }
681
821
  });
@@ -715,7 +855,7 @@ function lineage(routes2, route) {
715
855
  }
716
856
  function fullpath(lineage2) {
717
857
  if (lineage2.length === 1 && lineage2[0].id === "root") return "/";
718
- return "/" + lineage2.map((route) => route.path?.replace(/^\//, "")?.replace(/\/$/, "")).filter((path9) => path9 !== void 0 && path9 !== "").join("/");
858
+ return "/" + lineage2.map((route) => route.path?.replace(/^\//, "")?.replace(/\/$/, "")).filter((path8) => path8 !== void 0 && path8 !== "").join("/");
719
859
  }
720
860
  var init_route = __esm({
721
861
  "typegen/route.ts"() {
@@ -730,8 +870,8 @@ function generate2(ctx, route) {
730
870
  const typesPath = getTypesPath(ctx, route);
731
871
  const parents = lineage2.slice(0, -1);
732
872
  const parentTypeImports = parents.map((parent, i) => {
733
- const rel = Path3.relative(
734
- Path3.dirname(typesPath),
873
+ const rel = Path4.relative(
874
+ Path4.dirname(typesPath),
735
875
  getTypesPath(ctx, parent)
736
876
  );
737
877
  const indent = i === 0 ? "" : " ".repeat(2);
@@ -793,52 +933,55 @@ function formatParamProperties(fullpath2) {
793
933
  );
794
934
  return properties.join("; ");
795
935
  }
796
- var import_dedent, Path3, Pathe2, noExtension;
936
+ var import_dedent, Path4, Pathe2, noExtension;
797
937
  var init_generate = __esm({
798
938
  "typegen/generate.ts"() {
799
939
  "use strict";
800
940
  import_dedent = __toESM(require("dedent"));
801
- Path3 = __toESM(require("pathe"));
941
+ Path4 = __toESM(require("pathe"));
802
942
  Pathe2 = __toESM(require("pathe/utils"));
803
943
  init_paths();
804
944
  init_params();
805
945
  init_route();
806
- noExtension = (path9) => Path3.join(Path3.dirname(path9), Pathe2.filename(path9));
946
+ noExtension = (path8) => Path4.join(Path4.dirname(path8), Pathe2.filename(path8));
807
947
  }
808
948
  });
809
949
 
810
950
  // typegen/index.ts
811
- async function run(rootDirectory) {
812
- const ctx = await createContext2({ rootDirectory, watch: false });
951
+ async function run(rootDirectory, { mode }) {
952
+ const ctx = await createContext2({ rootDirectory, mode, watch: false });
813
953
  await writeAll(ctx);
814
954
  }
815
- async function watch(rootDirectory, { logger } = {}) {
816
- const ctx = await createContext2({ rootDirectory, watch: true });
955
+ async function watch(rootDirectory, { mode, logger }) {
956
+ const ctx = await createContext2({ rootDirectory, mode, watch: true });
817
957
  await writeAll(ctx);
818
958
  logger?.info(import_picocolors3.default.green("generated types"), { timestamp: true, clear: true });
819
- ctx.configLoader.onChange(async ({ result, routeConfigChanged }) => {
820
- if (!result.ok) {
821
- logger?.error(import_picocolors3.default.red(result.error), { timestamp: true, clear: true });
822
- return;
823
- }
824
- ctx.config = result.value;
825
- if (routeConfigChanged) {
826
- await writeAll(ctx);
827
- logger?.info(import_picocolors3.default.green("regenerated types"), {
828
- timestamp: true,
829
- clear: true
830
- });
959
+ ctx.configLoader.onChange(
960
+ async ({ result, configChanged, routeConfigChanged }) => {
961
+ if (!result.ok) {
962
+ logger?.error(import_picocolors3.default.red(result.error), { timestamp: true, clear: true });
963
+ return;
964
+ }
965
+ ctx.config = result.value;
966
+ if (configChanged || routeConfigChanged) {
967
+ await writeAll(ctx);
968
+ logger?.info(import_picocolors3.default.green("regenerated types"), {
969
+ timestamp: true,
970
+ clear: true
971
+ });
972
+ }
831
973
  }
832
- });
974
+ );
833
975
  return {
834
976
  close: async () => await ctx.configLoader.close()
835
977
  };
836
978
  }
837
979
  async function createContext2({
838
980
  rootDirectory,
839
- watch: watch2
981
+ watch: watch2,
982
+ mode
840
983
  }) {
841
- const configLoader = await createConfigLoader({ rootDirectory, watch: watch2 });
984
+ const configLoader = await createConfigLoader({ rootDirectory, mode, watch: watch2 });
842
985
  const configResult = await configLoader.getConfig();
843
986
  if (!configResult.ok) {
844
987
  throw new Error(configResult.error);
@@ -856,12 +999,12 @@ async function writeAll(ctx) {
856
999
  Object.values(ctx.config.routes).forEach((route) => {
857
1000
  const typesPath = getTypesPath(ctx, route);
858
1001
  const content = generate2(ctx, route);
859
- import_node_fs3.default.mkdirSync(Path4.dirname(typesPath), { recursive: true });
1002
+ import_node_fs3.default.mkdirSync(Path5.dirname(typesPath), { recursive: true });
860
1003
  import_node_fs3.default.writeFileSync(typesPath, content);
861
1004
  });
862
- const registerPath = Path4.join(typegenDir, "+register.ts");
1005
+ const registerPath = Path5.join(typegenDir, "+register.ts");
863
1006
  import_node_fs3.default.writeFileSync(registerPath, register(ctx));
864
- const virtualPath = Path4.join(typegenDir, "+virtual.d.ts");
1007
+ const virtualPath = Path5.join(typegenDir, "+virtual.d.ts");
865
1008
  import_node_fs3.default.writeFileSync(virtualPath, virtual);
866
1009
  }
867
1010
  function register(ctx) {
@@ -872,21 +1015,25 @@ function register(ctx) {
872
1015
  interface Register {
873
1016
  params: Params;
874
1017
  }
1018
+
1019
+ interface Future {
1020
+ unstable_middleware: ${ctx.config.future.unstable_middleware}
1021
+ }
875
1022
  }
876
1023
  `;
877
1024
  const { t: t2 } = babel_exports;
878
- const indexPaths = new Set(
879
- Object.values(ctx.config.routes).filter((route) => route.index).map((route) => route.path)
880
- );
1025
+ const fullpaths = /* @__PURE__ */ new Set();
1026
+ Object.values(ctx.config.routes).forEach((route) => {
1027
+ if (route.id !== "root" && !route.path) return;
1028
+ const lineage2 = lineage(ctx.config.routes, route);
1029
+ const fullpath2 = fullpath(lineage2);
1030
+ fullpaths.add(fullpath2);
1031
+ });
881
1032
  const typeParams = t2.tsTypeAliasDeclaration(
882
1033
  t2.identifier("Params"),
883
1034
  null,
884
1035
  t2.tsTypeLiteral(
885
- Object.values(ctx.config.routes).map((route) => {
886
- if (route.id !== "root" && !route.path) return void 0;
887
- if (!route.index && indexPaths.has(route.path)) return void 0;
888
- const lineage2 = lineage(ctx.config.routes, route);
889
- const fullpath2 = fullpath(lineage2);
1036
+ Array.from(fullpaths).map((fullpath2) => {
890
1037
  const params = parse2(fullpath2);
891
1038
  return t2.tsPropertySignature(
892
1039
  t2.stringLiteral(fullpath2),
@@ -903,18 +1050,18 @@ function register(ctx) {
903
1050
  )
904
1051
  )
905
1052
  );
906
- }).filter((x) => x !== void 0)
1053
+ })
907
1054
  )
908
1055
  );
909
1056
  return [register2, generate(typeParams).code].join("\n\n");
910
1057
  }
911
- var import_node_fs3, import_dedent2, Path4, import_picocolors3, virtual;
1058
+ var import_node_fs3, import_dedent2, Path5, import_picocolors3, virtual;
912
1059
  var init_typegen = __esm({
913
1060
  "typegen/index.ts"() {
914
1061
  "use strict";
915
1062
  import_node_fs3 = __toESM(require("fs"));
916
1063
  import_dedent2 = __toESM(require("dedent"));
917
- Path4 = __toESM(require("pathe"));
1064
+ Path5 = __toESM(require("pathe"));
918
1065
  import_picocolors3 = __toESM(require("picocolors"));
919
1066
  init_config();
920
1067
  init_babel();
@@ -933,6 +1080,7 @@ var init_typegen = __esm({
933
1080
  export const isSpaMode: ServerBuild["isSpaMode"];
934
1081
  export const prerender: ServerBuild["prerender"];
935
1082
  export const publicPath: ServerBuild["publicPath"];
1083
+ export const routeDiscovery: ServerBuild["routeDiscovery"];
936
1084
  export const routes: ServerBuild["routes"];
937
1085
  export const ssr: ServerBuild["ssr"];
938
1086
  export const unstable_getCriticalCss: ServerBuild["unstable_getCriticalCss"];
@@ -955,23 +1103,24 @@ var init_node_adapter = __esm({
955
1103
  });
956
1104
 
957
1105
  // vite/resolve-file-url.ts
958
- var path5;
1106
+ var path4;
959
1107
  var init_resolve_file_url = __esm({
960
1108
  "vite/resolve-file-url.ts"() {
961
1109
  "use strict";
962
- path5 = __toESM(require("path"));
1110
+ path4 = __toESM(require("path"));
963
1111
  init_vite();
964
1112
  }
965
1113
  });
966
1114
 
967
1115
  // vite/styles.ts
968
- var path6, import_react_router, cssFileRegExp, cssModulesRegExp;
1116
+ var path5, import_react_router, cssFileRegExp, cssModulesRegExp;
969
1117
  var init_styles = __esm({
970
1118
  "vite/styles.ts"() {
971
1119
  "use strict";
972
- path6 = __toESM(require("path"));
1120
+ path5 = __toESM(require("path"));
973
1121
  import_react_router = require("react-router");
974
1122
  init_resolve_file_url();
1123
+ init_babel();
975
1124
  cssFileRegExp = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
976
1125
  cssModulesRegExp = new RegExp(`\\.module${cssFileRegExp.source}`);
977
1126
  }
@@ -1118,8 +1267,8 @@ function getServerBundleIds(ctx) {
1118
1267
  async function cleanBuildDirectory(viteConfig, ctx) {
1119
1268
  let buildDirectory = ctx.reactRouterConfig.buildDirectory;
1120
1269
  let isWithinRoot = () => {
1121
- let relativePath = path7.relative(ctx.rootDirectory, buildDirectory);
1122
- return !relativePath.startsWith("..") && !path7.isAbsolute(relativePath);
1270
+ let relativePath = path6.relative(ctx.rootDirectory, buildDirectory);
1271
+ return !relativePath.startsWith("..") && !path6.isAbsolute(relativePath);
1123
1272
  };
1124
1273
  if (viteConfig.build.emptyOutDir ?? isWithinRoot()) {
1125
1274
  await fse.remove(buildDirectory);
@@ -1130,7 +1279,7 @@ async function cleanViteManifests(environmentsOptions, ctx) {
1130
1279
  ([environmentName, options]) => {
1131
1280
  let outDir = options.build?.outDir;
1132
1281
  invariant(outDir, `Expected build.outDir for ${environmentName}`);
1133
- return path7.join(outDir, ".vite/manifest.json");
1282
+ return path6.join(outDir, ".vite/manifest.json");
1134
1283
  }
1135
1284
  );
1136
1285
  await Promise.all(
@@ -1140,7 +1289,7 @@ async function cleanViteManifests(environmentsOptions, ctx) {
1140
1289
  if (!ctx.viteManifestEnabled) {
1141
1290
  await fse.remove(viteManifestPath);
1142
1291
  }
1143
- let viteDir = path7.dirname(viteManifestPath);
1292
+ let viteDir = path6.dirname(viteManifestPath);
1144
1293
  let viteDirFiles = await fse.readdir(viteDir);
1145
1294
  if (viteDirFiles.length === 0) {
1146
1295
  await fse.remove(viteDir);
@@ -1157,10 +1306,10 @@ function mergeEnvironmentOptions(base, ...overrides) {
1157
1306
  }
1158
1307
  async function getEnvironmentOptionsResolvers(ctx, viteCommand) {
1159
1308
  let { serverBuildFile, serverModuleFormat } = ctx.reactRouterConfig;
1160
- let packageRoot = path7.dirname(
1309
+ let packageRoot = path6.dirname(
1161
1310
  require.resolve("@react-router/dev/package.json")
1162
1311
  );
1163
- let { moduleSyncEnabled } = await import(`file:///${path7.join(packageRoot, "module-sync-enabled/index.mjs")}`);
1312
+ let { moduleSyncEnabled } = await import(`file:///${path6.join(packageRoot, "module-sync-enabled/index.mjs")}`);
1164
1313
  let vite2 = getVite();
1165
1314
  let viteServerConditions = [
1166
1315
  ...vite2.defaultServerConditions ?? [],
@@ -1234,7 +1383,7 @@ async function getEnvironmentOptionsResolvers(ctx, viteCommand) {
1234
1383
  ctx.entryClientFilePath,
1235
1384
  ...Object.values(ctx.reactRouterConfig.routes).flatMap(
1236
1385
  (route) => {
1237
- let routeFilePath = path7.resolve(
1386
+ let routeFilePath = path6.resolve(
1238
1387
  ctx.reactRouterConfig.appDirectory,
1239
1388
  route.file
1240
1389
  );
@@ -1257,8 +1406,9 @@ async function getEnvironmentOptionsResolvers(ctx, viteCommand) {
1257
1406
  ""
1258
1407
  ) : null;
1259
1408
  let routeChunkSuffix = routeChunkName ? `-${(0, import_kebabCase.default)(routeChunkName)}` : "";
1260
- return path7.posix.join(
1261
- (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi ? viteUserConfig?.environments?.client?.build?.assetsDir : viteUserConfig?.build?.assetsDir) ?? "assets",
1409
+ let assetsDir = (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi ? viteUserConfig?.environments?.client?.build?.assetsDir : null) ?? viteUserConfig?.build?.assetsDir ?? "assets";
1410
+ return path6.posix.join(
1411
+ assetsDir,
1262
1412
  `[name]${routeChunkSuffix}-[hash].js`
1263
1413
  );
1264
1414
  }
@@ -1293,9 +1443,6 @@ async function getEnvironmentOptionsResolvers(ctx, viteCommand) {
1293
1443
  }
1294
1444
  });
1295
1445
  }
1296
- if (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi && viteCommand === "serve") {
1297
- environmentOptionsResolvers[CSS_DEV_HELPER_ENVIRONMENT_NAME] = () => ({});
1298
- }
1299
1446
  return environmentOptionsResolvers;
1300
1447
  }
1301
1448
  function resolveEnvironmentsOptions(environmentResolvers, resolverOptions) {
@@ -1310,13 +1457,13 @@ function resolveEnvironmentsOptions(environmentResolvers, resolverOptions) {
1310
1457
  function isNonNullable(x) {
1311
1458
  return x != null;
1312
1459
  }
1313
- var import_node_crypto, fs4, path7, url, fse, babel2, import_react_router2, import_es_module_lexer, import_pick3, import_jsesc, import_picocolors4, import_kebabCase, CLIENT_NON_COMPONENT_EXPORTS, CLIENT_ROUTE_EXPORTS, BUILD_CLIENT_ROUTE_QUERY_STRING, SSR_BUNDLE_PREFIX, CSS_DEV_HELPER_ENVIRONMENT_NAME, virtualHmrRuntime, virtualInjectHmrRuntime, virtual2, getServerBuildDirectory, getClientBuildDirectory, defaultEntriesDir, defaultEntries, REACT_REFRESH_HEADER;
1460
+ var import_node_crypto, fs4, path6, url, fse, babel2, import_react_router2, import_es_module_lexer, import_pick3, import_jsesc, import_picocolors4, import_kebabCase, CLIENT_NON_COMPONENT_EXPORTS, CLIENT_ROUTE_EXPORTS, BUILD_CLIENT_ROUTE_QUERY_STRING, SSR_BUNDLE_PREFIX, virtualHmrRuntime, virtualInjectHmrRuntime, virtual2, getServerBuildDirectory, getClientBuildDirectory, defaultEntriesDir, defaultEntries, REACT_REFRESH_HEADER;
1314
1461
  var init_plugin = __esm({
1315
1462
  "vite/plugin.ts"() {
1316
1463
  "use strict";
1317
1464
  import_node_crypto = require("crypto");
1318
1465
  fs4 = __toESM(require("fs"));
1319
- path7 = __toESM(require("path"));
1466
+ path6 = __toESM(require("path"));
1320
1467
  url = __toESM(require("url"));
1321
1468
  fse = __toESM(require("fs-extra"));
1322
1469
  babel2 = __toESM(require("@babel/core"));
@@ -1358,7 +1505,6 @@ var init_plugin = __esm({
1358
1505
  ];
1359
1506
  BUILD_CLIENT_ROUTE_QUERY_STRING = "?__react-router-build-client-route";
1360
1507
  SSR_BUNDLE_PREFIX = "ssrBundle_";
1361
- CSS_DEV_HELPER_ENVIRONMENT_NAME = "__react_router_css_dev_helper__";
1362
1508
  virtualHmrRuntime = create("hmr-runtime");
1363
1509
  virtualInjectHmrRuntime = create("inject-hmr-runtime");
1364
1510
  virtual2 = {
@@ -1366,19 +1512,19 @@ var init_plugin = __esm({
1366
1512
  serverManifest: create("server-manifest"),
1367
1513
  browserManifest: create("browser-manifest")
1368
1514
  };
1369
- getServerBuildDirectory = (reactRouterConfig, { serverBundleId } = {}) => path7.join(
1515
+ getServerBuildDirectory = (reactRouterConfig, { serverBundleId } = {}) => path6.join(
1370
1516
  reactRouterConfig.buildDirectory,
1371
1517
  "server",
1372
1518
  ...serverBundleId ? [serverBundleId] : []
1373
1519
  );
1374
- getClientBuildDirectory = (reactRouterConfig) => path7.join(reactRouterConfig.buildDirectory, "client");
1375
- defaultEntriesDir = path7.resolve(
1376
- path7.dirname(require.resolve("@react-router/dev/package.json")),
1520
+ getClientBuildDirectory = (reactRouterConfig) => path6.join(reactRouterConfig.buildDirectory, "client");
1521
+ defaultEntriesDir = path6.resolve(
1522
+ path6.dirname(require.resolve("@react-router/dev/package.json")),
1377
1523
  "dist",
1378
1524
  "config",
1379
1525
  "defaults"
1380
1526
  );
1381
- defaultEntries = fse.readdirSync(defaultEntriesDir).map((filename3) => path7.join(defaultEntriesDir, filename3));
1527
+ defaultEntries = fse.readdirSync(defaultEntriesDir).map((filename3) => path6.join(defaultEntriesDir, filename3));
1382
1528
  invariant(defaultEntries.length > 0, "No default entries found");
1383
1529
  REACT_REFRESH_HEADER = `
1384
1530
  import RefreshRuntime from "${virtualHmrRuntime.id}";
@@ -1412,7 +1558,13 @@ __export(build_exports, {
1412
1558
  async function build(root, viteBuildOptions) {
1413
1559
  await preloadVite();
1414
1560
  let vite2 = getVite();
1415
- let configResult = await loadConfig({ rootDirectory: root });
1561
+ let configResult = await loadConfig({
1562
+ rootDirectory: root,
1563
+ mode: viteBuildOptions.mode ?? "production",
1564
+ // In this scope we only need future flags, so we can skip evaluating
1565
+ // routes.ts until we're within the Vite build context
1566
+ skipRoutes: true
1567
+ });
1416
1568
  if (!configResult.ok) {
1417
1569
  throw new Error(configResult.error);
1418
1570
  }
@@ -1652,7 +1804,7 @@ var import_semver = __toESM(require("semver"));
1652
1804
  var import_picocolors8 = __toESM(require("picocolors"));
1653
1805
 
1654
1806
  // cli/commands.ts
1655
- var path8 = __toESM(require("path"));
1807
+ var path7 = __toESM(require("path"));
1656
1808
  var import_fs_extra = __toESM(require("fs-extra"));
1657
1809
  var import_package_json2 = __toESM(require("@npmcli/package-json"));
1658
1810
  var import_exit_hook = __toESM(require("exit-hook"));
@@ -1738,9 +1890,12 @@ function transpile(tsx, options = {}) {
1738
1890
  init_profiler();
1739
1891
  init_typegen();
1740
1892
  init_vite();
1741
- async function routes(reactRouterRoot, flags = {}) {
1742
- let rootDirectory = reactRouterRoot ?? process.cwd();
1743
- let configResult = await loadConfig({ rootDirectory });
1893
+ async function routes(rootDirectory, flags = {}) {
1894
+ rootDirectory = resolveRootDirectory(rootDirectory, flags);
1895
+ let configResult = await loadConfig({
1896
+ rootDirectory,
1897
+ mode: flags.mode ?? "production"
1898
+ });
1744
1899
  if (!configResult.ok) {
1745
1900
  console.error(import_picocolors7.default.red(configResult.error));
1746
1901
  process.exit(1);
@@ -1749,9 +1904,7 @@ async function routes(reactRouterRoot, flags = {}) {
1749
1904
  console.log(formatRoutes(configResult.value.routes, format));
1750
1905
  }
1751
1906
  async function build2(root, options = {}) {
1752
- if (!root) {
1753
- root = process.env.REACT_ROUTER_ROOT || process.cwd();
1754
- }
1907
+ root = resolveRootDirectory(root, options);
1755
1908
  let { build: build3 } = await Promise.resolve().then(() => (init_build(), build_exports));
1756
1909
  if (options.profile) {
1757
1910
  await start();
@@ -1768,6 +1921,7 @@ async function dev2(root, options = {}) {
1768
1921
  await start();
1769
1922
  }
1770
1923
  (0, import_exit_hook.default)(() => stop(console.info));
1924
+ root = resolveRootDirectory(root, options);
1771
1925
  await dev3(root, options);
1772
1926
  await new Promise(() => {
1773
1927
  });
@@ -1779,14 +1933,17 @@ var conjunctionListFormat = new Intl.ListFormat("en", {
1779
1933
  style: "long",
1780
1934
  type: "conjunction"
1781
1935
  });
1782
- async function generateEntry(entry, reactRouterRoot, flags = {}) {
1936
+ async function generateEntry(entry, rootDirectory, flags = {}) {
1783
1937
  if (!entry) {
1784
- await generateEntry("entry.client", reactRouterRoot, flags);
1785
- await generateEntry("entry.server", reactRouterRoot, flags);
1938
+ await generateEntry("entry.client", rootDirectory, flags);
1939
+ await generateEntry("entry.server", rootDirectory, flags);
1786
1940
  return;
1787
1941
  }
1788
- let rootDirectory = reactRouterRoot ?? process.cwd();
1789
- let configResult = await loadConfig({ rootDirectory });
1942
+ rootDirectory = resolveRootDirectory(rootDirectory, flags);
1943
+ let configResult = await loadConfig({
1944
+ rootDirectory,
1945
+ mode: flags.mode ?? "production"
1946
+ });
1790
1947
  if (!configResult.ok) {
1791
1948
  console.error(import_picocolors7.default.red(configResult.error));
1792
1949
  return;
@@ -1806,14 +1963,14 @@ async function generateEntry(entry, reactRouterRoot, flags = {}) {
1806
1963
  console.error(import_picocolors7.default.red(`No default server entry detected.`));
1807
1964
  return;
1808
1965
  }
1809
- let defaultsDirectory = path8.resolve(
1810
- path8.dirname(require.resolve("@react-router/dev/package.json")),
1966
+ let defaultsDirectory = path7.resolve(
1967
+ path7.dirname(require.resolve("@react-router/dev/package.json")),
1811
1968
  "dist",
1812
1969
  "config",
1813
1970
  "defaults"
1814
1971
  );
1815
- let defaultEntryClient = path8.resolve(defaultsDirectory, "entry.client.tsx");
1816
- let defaultEntryServer = path8.resolve(
1972
+ let defaultEntryClient = path7.resolve(defaultsDirectory, "entry.client.tsx");
1973
+ let defaultEntryServer = path7.resolve(
1817
1974
  defaultsDirectory,
1818
1975
  `entry.server.node.tsx`
1819
1976
  );
@@ -1822,7 +1979,7 @@ async function generateEntry(entry, reactRouterRoot, flags = {}) {
1822
1979
  let useTypeScript = flags.typescript ?? true;
1823
1980
  let outputExtension = useTypeScript ? "tsx" : "jsx";
1824
1981
  let outputEntry = `${entry}.${outputExtension}`;
1825
- let outputFile2 = path8.resolve(appDirectory, outputEntry);
1982
+ let outputFile2 = path7.resolve(appDirectory, outputEntry);
1826
1983
  if (!useTypeScript) {
1827
1984
  let javascript = transpile(contents, {
1828
1985
  cwd: rootDirectory,
@@ -1834,19 +1991,25 @@ async function generateEntry(entry, reactRouterRoot, flags = {}) {
1834
1991
  }
1835
1992
  console.log(
1836
1993
  import_picocolors7.default.blue(
1837
- `Entry file ${entry} created at ${path8.relative(
1994
+ `Entry file ${entry} created at ${path7.relative(
1838
1995
  rootDirectory,
1839
1996
  outputFile2
1840
1997
  )}.`
1841
1998
  )
1842
1999
  );
1843
2000
  }
2001
+ function resolveRootDirectory(root, flags) {
2002
+ if (root) {
2003
+ return path7.resolve(root);
2004
+ }
2005
+ return process.env.REACT_ROUTER_ROOT || (flags?.config ? path7.dirname(path7.resolve(flags.config)) : process.cwd());
2006
+ }
1844
2007
  async function checkForEntry(rootDirectory, appDirectory, entries2) {
1845
2008
  for (let entry of entries2) {
1846
- let entryPath = path8.resolve(appDirectory, entry);
2009
+ let entryPath = path7.resolve(appDirectory, entry);
1847
2010
  let exists = await import_fs_extra.default.pathExists(entryPath);
1848
2011
  if (exists) {
1849
- let relative8 = path8.relative(rootDirectory, entryPath);
2012
+ let relative8 = path7.relative(rootDirectory, entryPath);
1850
2013
  console.error(import_picocolors7.default.red(`Entry file ${relative8} already exists.`));
1851
2014
  return process.exit(1);
1852
2015
  }
@@ -1863,17 +2026,22 @@ async function createClientEntry(rootDirectory, appDirectory, inputFile) {
1863
2026
  return contents;
1864
2027
  }
1865
2028
  async function typegen(root, flags) {
1866
- root ??= process.cwd();
2029
+ root = resolveRootDirectory(root, flags);
1867
2030
  if (flags.watch) {
1868
2031
  await preloadVite();
1869
2032
  const vite2 = getVite();
1870
2033
  const logger = vite2.createLogger("info", { prefix: "[react-router]" });
1871
- await watch(root, { logger });
2034
+ await watch(root, {
2035
+ mode: flags.mode ?? "development",
2036
+ logger
2037
+ });
1872
2038
  await new Promise(() => {
1873
2039
  });
1874
2040
  return;
1875
2041
  }
1876
- await run(root);
2042
+ await run(root, {
2043
+ mode: flags.mode ?? "production"
2044
+ });
1877
2045
  }
1878
2046
 
1879
2047
  // cli/run.ts