@react-router/dev 0.0.0-experimental-a2c4d7fad → 0.0.0-experimental-004e483a8

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 v0.0.0-experimental-a2c4d7fad
3
+ * @react-router/dev v0.0.0-experimental-004e483a8
4
4
  *
5
5
  * Copyright (c) Remix Software Inc.
6
6
  *
@@ -178,7 +178,7 @@ function validateRouteConfig({
178
178
  `Route config in "${routeConfigFile}" is invalid.`,
179
179
  root ? `${root}` : [],
180
180
  nested ? Object.entries(nested).map(
181
- ([path10, message]) => `Path: routes.${path10}
181
+ ([path9, message]) => `Path: routes.${path9}
182
182
  ${message}`
183
183
  ) : []
184
184
  ].flat().join("\n\n")
@@ -390,7 +390,8 @@ async function resolveConfig({
390
390
  }
391
391
  let future = {
392
392
  unstable_optimizeDeps: reactRouterUserConfig.future?.unstable_optimizeDeps ?? false,
393
- unstable_splitRouteModules: reactRouterUserConfig.future?.unstable_splitRouteModules ?? false
393
+ unstable_splitRouteModules: reactRouterUserConfig.future?.unstable_splitRouteModules ?? false,
394
+ unstable_viteEnvironmentApi: reactRouterUserConfig.future?.unstable_viteEnvironmentApi ?? false
394
395
  };
395
396
  let reactRouterConfig = deepFreeze({
396
397
  appDirectory,
@@ -678,6 +679,7 @@ var init_paths = __esm({
678
679
  // typegen/generate.ts
679
680
  function generate2(ctx, route) {
680
681
  const lineage = getRouteLineage(ctx.config.routes, route);
682
+ const urlpath = lineage.map((route2) => route2.path).join("/");
681
683
  const typesPath = getTypesPath(ctx, route);
682
684
  const parents = lineage.slice(0, -1);
683
685
  const parentTypeImports = parents.map((parent, i) => {
@@ -695,15 +697,22 @@ function generate2(ctx, route) {
695
697
  // ${route.file}
696
698
 
697
699
  import type * as T from "react-router/route-module"
698
- import type { Routes } from "react-router/types"
699
700
 
700
701
  ${parentTypeImports}
701
702
 
702
- type RouteId = "${route.id}"
703
+ type Module = typeof import("../${Pathe2.filename(route.file)}.js")
703
704
 
704
- export type Info = Routes[RouteId] & {
705
+ export type Info = {
705
706
  parents: [${parents.map((_, i) => `Parent${i}`).join(", ")}],
706
- id: RouteId
707
+ id: "${route.id}"
708
+ file: "${route.file}"
709
+ path: "${route.path}"
710
+ params: {${formatParamProperties(
711
+ urlpath
712
+ )}} & { [key: string]: string | undefined }
713
+ module: Module
714
+ loaderData: T.CreateLoaderData<Module>
715
+ actionData: T.CreateActionData<Module>
707
716
  }
708
717
 
709
718
  export namespace Route {
@@ -738,6 +747,36 @@ function getRouteLineage(routes2, route) {
738
747
  result.reverse();
739
748
  return result;
740
749
  }
750
+ function formatParamProperties(urlpath) {
751
+ const params = parseParams(urlpath);
752
+ const properties = Object.entries(params).map(([name, values]) => {
753
+ if (values.length === 1) {
754
+ const isOptional = values[0];
755
+ return isOptional ? `"${name}"?: string` : `"${name}": string`;
756
+ }
757
+ const items = values.map(
758
+ (isOptional) => isOptional ? "string | undefined" : "string"
759
+ );
760
+ return `"${name}": [${items.join(", ")}]`;
761
+ });
762
+ return properties.join("; ");
763
+ }
764
+ function parseParams(urlpath) {
765
+ const result = {};
766
+ let segments = urlpath.split("/");
767
+ segments.forEach((segment) => {
768
+ const match = segment.match(/^:([\w-]+)(\?)?/);
769
+ if (!match) return;
770
+ const param = match[1];
771
+ const isOptional = match[2] !== void 0;
772
+ result[param] ??= [];
773
+ result[param].push(isOptional);
774
+ return;
775
+ });
776
+ const hasSplat = segments.at(-1) === "*";
777
+ if (hasSplat) result["*"] = [false];
778
+ return result;
779
+ }
741
780
  var import_dedent, Path3, Pathe2, noExtension;
742
781
  var init_generate = __esm({
743
782
  "typegen/generate.ts"() {
@@ -746,7 +785,7 @@ var init_generate = __esm({
746
785
  Path3 = __toESM(require("pathe"));
747
786
  Pathe2 = __toESM(require("pathe/utils"));
748
787
  init_paths();
749
- noExtension = (path10) => Path3.join(Path3.dirname(path10), Pathe2.filename(path10));
788
+ noExtension = (path9) => Path3.join(Path3.dirname(path9), Pathe2.filename(path9));
750
789
  }
751
790
  });
752
791
 
@@ -802,94 +841,74 @@ async function writeAll(ctx) {
802
841
  import_node_fs3.default.mkdirSync(Path4.dirname(typesPath), { recursive: true });
803
842
  import_node_fs3.default.writeFileSync(typesPath, content);
804
843
  });
805
- Object.values(ctx.config.routes).map(
806
- (route) => t2.tsPropertySignature(
807
- t2.stringLiteral(route.id),
808
- t2.tsTypeAnnotation(
809
- t2.tsTypeLiteral([
810
- t2.tsPropertySignature(
811
- t2.identifier("parentId"),
812
- t2.tsTypeAnnotation(
813
- route.parentId ? t2.tsLiteralType(t2.stringLiteral(route.parentId)) : t2.tsUndefinedKeyword()
814
- )
815
- ),
816
- t2.tsPropertySignature(
817
- t2.identifier("path"),
818
- t2.tsTypeAnnotation(
819
- route.path ? t2.tsLiteralType(t2.stringLiteral(route.path)) : t2.tsUndefinedKeyword()
820
- )
821
- ),
822
- t2.tsPropertySignature(
823
- t2.identifier("module"),
824
- t2.tsTypeAnnotation(
825
- t2.tsTypeQuery(t2.tsImportType(t2.stringLiteral(route.file)))
826
- )
827
- )
828
- ])
829
- )
830
- )
831
- );
832
844
  const registerPath = Path4.join(typegenDir, "+register.ts");
833
845
  import_node_fs3.default.writeFileSync(registerPath, register(ctx));
834
846
  }
835
847
  function register(ctx) {
836
- const routes2 = generate(
837
- t2.tsTypeAliasDeclaration(
838
- t2.identifier("Routes"),
839
- null,
840
- t2.tsTypeLiteral(
841
- Object.values(ctx.config.routes).map(
842
- (route) => t2.tsPropertySignature(
843
- t2.stringLiteral(route.id),
844
- t2.tsTypeAnnotation(
845
- t2.tsTypeLiteral([
846
- t2.tsPropertySignature(
847
- t2.identifier("parentId"),
848
- t2.tsTypeAnnotation(
849
- route.parentId ? t2.tsLiteralType(t2.stringLiteral(route.parentId)) : t2.tsUndefinedKeyword()
850
- )
851
- ),
852
- t2.tsPropertySignature(
853
- t2.identifier("path"),
854
- t2.tsTypeAnnotation(
855
- route.path ? t2.tsLiteralType(t2.stringLiteral(route.path)) : t2.tsUndefinedKeyword()
856
- )
857
- ),
858
- t2.tsPropertySignature(
859
- t2.identifier("module"),
860
- t2.tsTypeAnnotation(
861
- t2.tsTypeQuery(
862
- t2.tsImportType(
863
- t2.stringLiteral(compiledModulePath(ctx, route))
864
- )
865
- )
866
- )
867
- )
868
- ])
869
- )
870
- )
871
- )
872
- )
873
- )
874
- ).code;
875
- const registerTypes = import_dedent2.default`
876
- import "react-router/types";
848
+ const register2 = import_dedent2.default`
849
+ import "react-router";
877
850
 
878
- declare module "react-router/types" {
851
+ declare module "react-router" {
879
852
  interface Register {
880
- routes: Routes;
853
+ params: Params;
881
854
  }
882
855
  }
883
856
  `;
884
- return [registerTypes, routes2].join("\n\n");
857
+ const { t: t2 } = babel_exports;
858
+ const typeParams = t2.tsTypeAliasDeclaration(
859
+ t2.identifier("Params"),
860
+ null,
861
+ t2.tsTypeLiteral(
862
+ Object.values(ctx.config.routes).map((route) => {
863
+ const lineage = getRouteLineage2(ctx.config.routes, route);
864
+ const fullpath = lineage.map((route2) => route2.path).join("/");
865
+ const params = parseParams2(fullpath);
866
+ return t2.tsPropertySignature(
867
+ t2.stringLiteral(fullpath),
868
+ t2.tsTypeAnnotation(
869
+ t2.tsTypeLiteral(
870
+ Object.entries(params).map(([param, isRequired]) => {
871
+ const property = t2.tsPropertySignature(
872
+ t2.stringLiteral(param),
873
+ t2.tsTypeAnnotation(t2.tsStringKeyword())
874
+ );
875
+ property.optional = !isRequired;
876
+ return property;
877
+ })
878
+ )
879
+ )
880
+ );
881
+ })
882
+ )
883
+ );
884
+ return [register2, generate(typeParams).code].join("\n\n");
885
+ }
886
+ function parseParams2(fullpath) {
887
+ const result = {};
888
+ let segments = fullpath.split("/");
889
+ segments.forEach((segment) => {
890
+ const match = segment.match(/^:([\w-]+)(\?)?/);
891
+ if (!match) return;
892
+ const param = match[1];
893
+ const isRequired = match[2] === void 0;
894
+ result[param] ||= isRequired;
895
+ return;
896
+ });
897
+ const hasSplat = segments.at(-1) === "*";
898
+ if (hasSplat) result["*"] = true;
899
+ return result;
885
900
  }
886
- function compiledModulePath(ctx, route) {
887
- return "./" + Path4.relative(
888
- ctx.rootDirectory,
889
- Path4.join(ctx.config.appDirectory, route.file)
890
- ).replace(/\.(js|ts)x?$/, ".js");
901
+ function getRouteLineage2(routes2, route) {
902
+ const result = [];
903
+ while (route) {
904
+ result.push(route);
905
+ if (!route.parentId) break;
906
+ route = routes2[route.parentId];
907
+ }
908
+ result.reverse();
909
+ return result;
891
910
  }
892
- var import_node_fs3, import_dedent2, Path4, import_picocolors3, t2;
911
+ var import_node_fs3, import_dedent2, Path4, import_picocolors3;
893
912
  var init_typegen = __esm({
894
913
  "typegen/index.ts"() {
895
914
  "use strict";
@@ -901,7 +920,6 @@ var init_typegen = __esm({
901
920
  init_babel();
902
921
  init_generate();
903
922
  init_paths();
904
- ({ t: t2 } = babel_exports);
905
923
  }
906
924
  });
907
925
 
@@ -1043,11 +1061,12 @@ var init_with_props = __esm({
1043
1061
  async function resolveViteConfig({
1044
1062
  configFile,
1045
1063
  mode,
1046
- root
1064
+ root,
1065
+ plugins
1047
1066
  }) {
1048
1067
  let vite2 = getVite();
1049
1068
  let viteConfig = await vite2.resolveConfig(
1050
- { mode, configFile, root },
1069
+ { mode, configFile, root, plugins },
1051
1070
  "build",
1052
1071
  // command
1053
1072
  "production",
@@ -1063,6 +1082,17 @@ async function resolveViteConfig({
1063
1082
  async function extractPluginContext(viteConfig) {
1064
1083
  return viteConfig["__reactRouterPluginContext"];
1065
1084
  }
1085
+ function isSeverBundleEnvironmentName(name) {
1086
+ return name.startsWith(SSR_BUNDLE_PREFIX);
1087
+ }
1088
+ function getServerEnvironmentEntries(record, buildManifest) {
1089
+ return Object.entries(record).filter(
1090
+ ([name]) => buildManifest.serverBundles ? isSeverBundleEnvironmentName(name) : name === "ssr"
1091
+ );
1092
+ }
1093
+ function getServerEnvironmentKeys(record, buildManifest) {
1094
+ return getServerEnvironmentEntries(record, buildManifest).map(([key]) => key);
1095
+ }
1066
1096
  function getAddressableRoutes(routes2) {
1067
1097
  let nonAddressableIds = /* @__PURE__ */ new Set();
1068
1098
  for (let id in routes2) {
@@ -1112,6 +1142,39 @@ function getRoutesByServerBundleId(buildManifest) {
1112
1142
  }
1113
1143
  return routesByServerBundleId;
1114
1144
  }
1145
+ async function cleanBuildDirectory(viteConfig, ctx) {
1146
+ let buildDirectory = ctx.reactRouterConfig.buildDirectory;
1147
+ let isWithinRoot = () => {
1148
+ let relativePath = path7.relative(ctx.rootDirectory, buildDirectory);
1149
+ return !relativePath.startsWith("..") && !path7.isAbsolute(relativePath);
1150
+ };
1151
+ if (viteConfig.build.emptyOutDir ?? isWithinRoot()) {
1152
+ await fse.remove(buildDirectory);
1153
+ }
1154
+ }
1155
+ async function cleanViteManifests(environmentsOptions, ctx) {
1156
+ let viteManifestPaths = Object.entries(environmentsOptions).map(
1157
+ ([environmentName, options]) => {
1158
+ let outDir = options.build?.outDir;
1159
+ invariant(outDir, `Expected build.outDir for ${environmentName}`);
1160
+ return path7.join(outDir, ".vite/manifest.json");
1161
+ }
1162
+ );
1163
+ await Promise.all(
1164
+ viteManifestPaths.map(async (viteManifestPath) => {
1165
+ let manifestExists = await fse.pathExists(viteManifestPath);
1166
+ if (!manifestExists) return;
1167
+ if (!ctx.viteManifestEnabled) {
1168
+ await fse.remove(viteManifestPath);
1169
+ }
1170
+ let viteDir = path7.dirname(viteManifestPath);
1171
+ let viteDirFiles = await fse.readdir(viteDir);
1172
+ if (viteDirFiles.length === 0) {
1173
+ await fse.remove(viteDir);
1174
+ }
1175
+ })
1176
+ );
1177
+ }
1115
1178
  async function getBuildManifest(ctx) {
1116
1179
  let { routes: routes2, serverBundles, appDirectory } = ctx.reactRouterConfig;
1117
1180
  if (!serverBundles) {
@@ -1171,77 +1234,101 @@ async function getBuildManifest(ctx) {
1171
1234
  );
1172
1235
  return buildManifest;
1173
1236
  }
1174
- function mergeBuildOptions(base, overrides) {
1237
+ function mergeEnvironmentOptions(base, ...overrides) {
1175
1238
  let vite2 = getVite();
1176
- return vite2.mergeConfig({ build: base }, { build: overrides }).build;
1239
+ return overrides.reduce(
1240
+ (merged, override) => vite2.mergeConfig(merged, override, false),
1241
+ base
1242
+ );
1177
1243
  }
1178
- async function getEnvironmentOptionsResolvers(ctx, buildManifest) {
1244
+ async function getEnvironmentOptionsResolvers(ctx, buildManifest, viteCommand) {
1179
1245
  let { serverBuildFile, serverModuleFormat } = ctx.reactRouterConfig;
1180
- function getBaseBuildOptions({
1246
+ let packageRoot = path7.dirname(
1247
+ require.resolve("@react-router/dev/package.json")
1248
+ );
1249
+ let { moduleSyncEnabled } = await import(`file:///${path7.join(packageRoot, "module-sync-enabled/index.mjs")}`);
1250
+ let vite2 = getVite();
1251
+ let viteServerConditions = [
1252
+ ...vite2.defaultServerConditions ?? [],
1253
+ ...moduleSyncEnabled ? ["module-sync"] : []
1254
+ ];
1255
+ function getBaseOptions({
1181
1256
  viteUserConfig
1182
1257
  }) {
1183
1258
  return {
1184
- cssMinify: viteUserConfig.build?.cssMinify ?? true,
1185
- manifest: true,
1186
- // The manifest is enabled for all builds to detect SSR-only assets
1187
- rollupOptions: {
1188
- preserveEntrySignatures: "exports-only",
1189
- // Silence Rollup "use client" warnings
1190
- // Adapted from https://github.com/vitejs/vite-plugin-react/pull/144
1191
- onwarn(warning, defaultHandler) {
1192
- if (warning.code === "MODULE_LEVEL_DIRECTIVE" && warning.message.includes("use client")) {
1193
- return;
1194
- }
1195
- let userHandler = viteUserConfig.build?.rollupOptions?.onwarn;
1196
- if (userHandler) {
1197
- userHandler(warning, defaultHandler);
1198
- } else {
1199
- defaultHandler(warning);
1259
+ build: {
1260
+ cssMinify: viteUserConfig.build?.cssMinify ?? true,
1261
+ manifest: true,
1262
+ // The manifest is enabled for all builds to detect SSR-only assets
1263
+ rollupOptions: {
1264
+ preserveEntrySignatures: "exports-only",
1265
+ // Silence Rollup "use client" warnings
1266
+ // Adapted from https://github.com/vitejs/vite-plugin-react/pull/144
1267
+ onwarn(warning, defaultHandler) {
1268
+ if (warning.code === "MODULE_LEVEL_DIRECTIVE" && warning.message.includes("use client")) {
1269
+ return;
1270
+ }
1271
+ let userHandler = viteUserConfig.build?.rollupOptions?.onwarn;
1272
+ if (userHandler) {
1273
+ userHandler(warning, defaultHandler);
1274
+ } else {
1275
+ defaultHandler(warning);
1276
+ }
1200
1277
  }
1201
1278
  }
1202
1279
  }
1203
1280
  };
1204
1281
  }
1205
- function getBaseServerBuildOptions({
1282
+ function getBaseServerOptions({
1206
1283
  viteUserConfig
1207
1284
  }) {
1208
- return mergeBuildOptions(getBaseBuildOptions({ viteUserConfig }), {
1209
- // We move SSR-only assets to client assets. Note that the
1210
- // SSR build can also emit code-split JS files (e.g. by
1211
- // dynamic import) under the same assets directory
1212
- // regardless of "ssrEmitAssets" option, so we also need to
1213
- // keep these JS files have to be kept as-is.
1214
- ssrEmitAssets: true,
1215
- copyPublicDir: false,
1216
- // Assets in the public directory are only used by the client
1217
- rollupOptions: {
1218
- output: {
1219
- entryFileNames: serverBuildFile,
1220
- format: serverModuleFormat
1285
+ let conditions = viteCommand === "build" ? viteServerConditions : ["development", ...viteServerConditions];
1286
+ return mergeEnvironmentOptions(getBaseOptions({ viteUserConfig }), {
1287
+ resolve: {
1288
+ external: ssrExternals,
1289
+ conditions,
1290
+ externalConditions: conditions
1291
+ },
1292
+ build: {
1293
+ // We move SSR-only assets to client assets. Note that the
1294
+ // SSR build can also emit code-split JS files (e.g. by
1295
+ // dynamic import) under the same assets directory
1296
+ // regardless of "ssrEmitAssets" option, so we also need to
1297
+ // keep these JS files have to be kept as-is.
1298
+ ssrEmitAssets: true,
1299
+ copyPublicDir: false,
1300
+ // Assets in the public directory are only used by the client
1301
+ rollupOptions: {
1302
+ output: {
1303
+ entryFileNames: serverBuildFile,
1304
+ format: serverModuleFormat
1305
+ }
1221
1306
  }
1222
1307
  }
1223
1308
  });
1224
1309
  }
1225
1310
  let environmentOptionsResolvers = {
1226
- client: ({ viteUserConfig }) => ({
1227
- build: mergeBuildOptions(getBaseBuildOptions({ viteUserConfig }), {
1311
+ client: ({ viteUserConfig }) => mergeEnvironmentOptions(getBaseOptions({ viteUserConfig }), {
1312
+ build: {
1228
1313
  rollupOptions: {
1229
1314
  input: [
1230
1315
  ctx.entryClientFilePath,
1231
- ...Object.values(ctx.reactRouterConfig.routes).flatMap((route) => {
1232
- let routeFilePath = path7.resolve(
1233
- ctx.reactRouterConfig.appDirectory,
1234
- route.file
1235
- );
1236
- let isRootRoute = route.file === ctx.reactRouterConfig.routes.root.file;
1237
- let code = fse.readFileSync(routeFilePath, "utf-8");
1238
- return [
1239
- `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
1240
- ...ctx.reactRouterConfig.future.unstable_splitRouteModules && !isRootRoute ? routeChunkExportNames.map(
1241
- (exportName) => code.includes(exportName) ? getRouteChunkModuleId(routeFilePath, exportName) : null
1242
- ) : []
1243
- ].filter(isNonNullable);
1244
- })
1316
+ ...Object.values(ctx.reactRouterConfig.routes).flatMap(
1317
+ (route) => {
1318
+ let routeFilePath = path7.resolve(
1319
+ ctx.reactRouterConfig.appDirectory,
1320
+ route.file
1321
+ );
1322
+ let isRootRoute = route.file === ctx.reactRouterConfig.routes.root.file;
1323
+ let code = fse.readFileSync(routeFilePath, "utf-8");
1324
+ return [
1325
+ `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
1326
+ ...ctx.reactRouterConfig.future.unstable_splitRouteModules && !isRootRoute ? routeChunkExportNames.map(
1327
+ (exportName) => code.includes(exportName) ? getRouteChunkModuleId(routeFilePath, exportName) : null
1328
+ ) : []
1329
+ ].filter(isNonNullable);
1330
+ }
1331
+ )
1245
1332
  ],
1246
1333
  output: {
1247
1334
  entryFileNames({ moduleIds }) {
@@ -1253,19 +1340,19 @@ async function getEnvironmentOptionsResolvers(ctx, buildManifest) {
1253
1340
  }
1254
1341
  },
1255
1342
  outDir: getClientBuildDirectory(ctx.reactRouterConfig)
1256
- })
1343
+ }
1257
1344
  })
1258
1345
  };
1259
1346
  if (hasServerBundles(buildManifest)) {
1260
1347
  for (let [serverBundleId, routes2] of Object.entries(
1261
1348
  getRoutesByServerBundleId(buildManifest)
1262
1349
  )) {
1263
- environmentOptionsResolvers[`ssr-bundle-${serverBundleId}`] = ({
1264
- viteUserConfig
1265
- }) => ({
1266
- build: mergeBuildOptions(
1267
- getBaseServerBuildOptions({ viteUserConfig }),
1268
- {
1350
+ const serverBundleEnvironmentId = serverBundleId.replaceAll("-", "_");
1351
+ const environmentName = `${SSR_BUNDLE_PREFIX}${serverBundleEnvironmentId}`;
1352
+ environmentOptionsResolvers[environmentName] = ({ viteUserConfig }) => mergeEnvironmentOptions(
1353
+ getBaseServerOptions({ viteUserConfig }),
1354
+ {
1355
+ build: {
1269
1356
  outDir: getServerBuildDirectory(ctx, { serverBundleId }),
1270
1357
  rollupOptions: {
1271
1358
  input: `${virtual.serverBuild.id}?route-ids=${Object.keys(
@@ -1273,25 +1360,37 @@ async function getEnvironmentOptionsResolvers(ctx, buildManifest) {
1273
1360
  ).join(",")}`
1274
1361
  }
1275
1362
  }
1276
- )
1277
- });
1363
+ },
1364
+ // Ensure server bundle environments extend the user's SSR
1365
+ // environment config if it exists
1366
+ viteUserConfig.environments?.ssr ?? {}
1367
+ );
1278
1368
  }
1279
1369
  } else {
1280
- environmentOptionsResolvers.ssr = ({ viteUserConfig }) => ({
1281
- build: mergeBuildOptions(getBaseServerBuildOptions({ viteUserConfig }), {
1370
+ environmentOptionsResolvers.ssr = ({ viteUserConfig }) => mergeEnvironmentOptions(getBaseServerOptions({ viteUserConfig }), {
1371
+ build: {
1282
1372
  outDir: getServerBuildDirectory(ctx),
1283
1373
  rollupOptions: {
1284
1374
  input: viteUserConfig.build?.rollupOptions?.input ?? virtual.serverBuild.id
1285
1375
  }
1286
- })
1376
+ }
1287
1377
  });
1288
1378
  }
1289
1379
  return environmentOptionsResolvers;
1290
1380
  }
1381
+ function resolveEnvironmentsOptions(environmentResolvers, resolverOptions) {
1382
+ let environmentOptions = {};
1383
+ for (let [environmentName, resolver] of Object.entries(
1384
+ environmentResolvers
1385
+ )) {
1386
+ environmentOptions[environmentName] = resolver(resolverOptions);
1387
+ }
1388
+ return environmentOptions;
1389
+ }
1291
1390
  function isNonNullable(x) {
1292
1391
  return x != null;
1293
1392
  }
1294
- var import_node_crypto, path7, url, fse, babel2, import_react_router2, import_es_module_lexer, import_pick3, import_jsesc, import_picocolors4, import_kebabCase, BUILD_CLIENT_ROUTE_QUERY_STRING, virtualHmrRuntime, virtualInjectHmrRuntime, virtual, getServerBuildDirectory, getClientBuildDirectory, defaultEntriesDir, defaultEntries, REACT_REFRESH_HEADER;
1393
+ var import_node_crypto, path7, url, fse, babel2, import_react_router2, import_es_module_lexer, import_pick3, import_jsesc, import_picocolors4, import_kebabCase, BUILD_CLIENT_ROUTE_QUERY_STRING, SSR_BUNDLE_PREFIX, virtualHmrRuntime, virtualInjectHmrRuntime, virtual, getServerBuildDirectory, getClientBuildDirectory, defaultEntriesDir, defaultEntries, REACT_REFRESH_HEADER;
1295
1394
  var init_plugin = __esm({
1296
1395
  "vite/plugin.ts"() {
1297
1396
  "use strict";
@@ -1320,6 +1419,7 @@ var init_plugin = __esm({
1320
1419
  init_config();
1321
1420
  init_with_props();
1322
1421
  BUILD_CLIENT_ROUTE_QUERY_STRING = "?__react-router-build-client-route";
1422
+ SSR_BUNDLE_PREFIX = "ssrBundle_";
1323
1423
  virtualHmrRuntime = create("hmr-runtime");
1324
1424
  virtualInjectHmrRuntime = create("inject-hmr-runtime");
1325
1425
  virtual = {
@@ -1370,34 +1470,83 @@ var build_exports = {};
1370
1470
  __export(build_exports, {
1371
1471
  build: () => build
1372
1472
  });
1373
- async function cleanBuildDirectory(viteConfig, ctx) {
1374
- let buildDirectory = ctx.reactRouterConfig.buildDirectory;
1375
- let isWithinRoot = () => {
1376
- let relativePath = import_node_path2.default.relative(ctx.rootDirectory, buildDirectory);
1377
- return !relativePath.startsWith("..") && !import_node_path2.default.isAbsolute(relativePath);
1378
- };
1379
- if (viteConfig.build.emptyOutDir ?? isWithinRoot()) {
1380
- await import_fs_extra.default.remove(buildDirectory);
1473
+ async function build(root, viteBuildOptions) {
1474
+ await preloadVite();
1475
+ let vite2 = getVite();
1476
+ let configResult = await loadConfig({ rootDirectory: root });
1477
+ if (!configResult.ok) {
1478
+ throw new Error(configResult.error);
1381
1479
  }
1480
+ let config = configResult.value;
1481
+ let unstable_viteEnvironmentApi = config.future.unstable_viteEnvironmentApi;
1482
+ let viteMajor = parseInt(vite2.version.split(".")[0], 10);
1483
+ if (unstable_viteEnvironmentApi && viteMajor === 5) {
1484
+ throw new Error(
1485
+ "The future.unstable_viteEnvironmentApi option is not supported in Vite 5"
1486
+ );
1487
+ }
1488
+ return await (unstable_viteEnvironmentApi ? viteAppBuild(root, viteBuildOptions) : viteBuild(root, viteBuildOptions));
1382
1489
  }
1383
- function getViteManifestPaths(environmentOptionsResolvers) {
1384
- return Object.entries(environmentOptionsResolvers).map(
1385
- ([environmentName, resolveOptions]) => {
1386
- invariant(
1387
- resolveOptions,
1388
- `Expected build environment options resolver for ${environmentName}`
1389
- );
1390
- let options = resolveOptions({
1391
- viteCommand: "build",
1392
- viteUserConfig: {}
1393
- });
1394
- let outDir = options.build.outDir;
1395
- invariant(outDir, `Expected build.outDir for ${environmentName}`);
1396
- return import_node_path2.default.join(outDir, ".vite/manifest.json");
1397
- }
1398
- );
1490
+ async function viteAppBuild(root, {
1491
+ assetsInlineLimit,
1492
+ clearScreen,
1493
+ config: configFile,
1494
+ emptyOutDir,
1495
+ force,
1496
+ logLevel,
1497
+ minify,
1498
+ mode,
1499
+ sourcemapClient,
1500
+ sourcemapServer
1501
+ }) {
1502
+ let vite2 = getVite();
1503
+ let builder = await vite2.createBuilder({
1504
+ root,
1505
+ mode,
1506
+ configFile,
1507
+ build: {
1508
+ assetsInlineLimit,
1509
+ emptyOutDir,
1510
+ minify
1511
+ },
1512
+ optimizeDeps: { force },
1513
+ clearScreen,
1514
+ logLevel,
1515
+ plugins: [
1516
+ {
1517
+ name: "react-router:cli-config",
1518
+ configEnvironment(name) {
1519
+ if (sourcemapClient && name === "client") {
1520
+ return {
1521
+ build: {
1522
+ sourcemap: sourcemapClient
1523
+ }
1524
+ };
1525
+ }
1526
+ if (sourcemapServer && name !== "client") {
1527
+ return {
1528
+ build: {
1529
+ sourcemap: sourcemapServer
1530
+ }
1531
+ };
1532
+ }
1533
+ },
1534
+ configResolved(config) {
1535
+ let hasReactRouterPlugin = config.plugins.find(
1536
+ (plugin2) => plugin2.name === "react-router"
1537
+ );
1538
+ if (!hasReactRouterPlugin) {
1539
+ throw new Error(
1540
+ "React Router Vite plugin not found in Vite config"
1541
+ );
1542
+ }
1543
+ }
1544
+ }
1545
+ ]
1546
+ });
1547
+ await builder.buildApp();
1399
1548
  }
1400
- async function build(root, {
1549
+ async function viteBuild(root, {
1401
1550
  assetsInlineLimit,
1402
1551
  clearScreen,
1403
1552
  config: configFile,
@@ -1409,24 +1558,32 @@ async function build(root, {
1409
1558
  sourcemapClient,
1410
1559
  sourcemapServer
1411
1560
  }) {
1412
- await preloadVite();
1413
- let viteConfig = await resolveViteConfig({ configFile, mode, root });
1414
- const ctx = await extractPluginContext(viteConfig);
1561
+ let viteUserConfig = {};
1562
+ let viteConfig = await resolveViteConfig({
1563
+ configFile,
1564
+ mode,
1565
+ root,
1566
+ plugins: [
1567
+ {
1568
+ name: "react-router:extract-vite-user-config",
1569
+ config(config) {
1570
+ viteUserConfig = config;
1571
+ }
1572
+ }
1573
+ ]
1574
+ });
1575
+ let ctx = await extractPluginContext(viteConfig);
1415
1576
  if (!ctx) {
1416
1577
  console.error(
1417
1578
  import_picocolors5.default.red("React Router Vite plugin not found in Vite config")
1418
1579
  );
1419
1580
  process.exit(1);
1420
1581
  }
1421
- let { reactRouterConfig } = ctx;
1422
- let vite2 = getVite();
1423
- async function viteBuild(environmentOptionsResolvers2, environmentName) {
1582
+ async function buildEnvironment(environmentName) {
1583
+ let vite2 = getVite();
1424
1584
  let ssr = environmentName !== "client";
1425
- let resolveOptions = environmentOptionsResolvers2[environmentName];
1426
- invariant(
1427
- resolveOptions,
1428
- `Missing environment options resolver for ${environmentName}`
1429
- );
1585
+ let resolveOptions = environmentOptionsResolvers[environmentName];
1586
+ invariant(resolveOptions);
1430
1587
  let environmentBuildContext = {
1431
1588
  name: environmentName,
1432
1589
  resolveOptions
@@ -1448,49 +1605,37 @@ async function build(root, {
1448
1605
  ...{ __reactRouterEnvironmentBuildContext: environmentBuildContext }
1449
1606
  });
1450
1607
  }
1451
- await cleanBuildDirectory(viteConfig, ctx);
1608
+ let { reactRouterConfig } = ctx;
1452
1609
  let buildManifest = await getBuildManifest(ctx);
1453
1610
  let environmentOptionsResolvers = await getEnvironmentOptionsResolvers(
1454
1611
  ctx,
1455
- buildManifest
1612
+ buildManifest,
1613
+ "build"
1456
1614
  );
1457
- await viteBuild(environmentOptionsResolvers, "client");
1458
- let serverEnvironmentNames = Object.keys(
1459
- environmentOptionsResolvers
1460
- ).filter((environmentName) => environmentName !== "client");
1461
- await Promise.all(
1462
- serverEnvironmentNames.map(
1463
- (environmentName) => viteBuild(environmentOptionsResolvers, environmentName)
1464
- )
1615
+ let environmentsOptions = resolveEnvironmentsOptions(
1616
+ environmentOptionsResolvers,
1617
+ { viteUserConfig }
1465
1618
  );
1466
- let viteManifestPaths = getViteManifestPaths(environmentOptionsResolvers);
1467
- await Promise.all(
1468
- viteManifestPaths.map(async (viteManifestPath) => {
1469
- let manifestExists = await import_fs_extra.default.pathExists(viteManifestPath);
1470
- if (!manifestExists) return;
1471
- if (!ctx.viteManifestEnabled) {
1472
- await import_fs_extra.default.remove(viteManifestPath);
1473
- }
1474
- let viteDir = import_node_path2.default.dirname(viteManifestPath);
1475
- let viteDirFiles = await import_fs_extra.default.readdir(viteDir);
1476
- if (viteDirFiles.length === 0) {
1477
- await import_fs_extra.default.remove(viteDir);
1478
- }
1479
- })
1619
+ await cleanBuildDirectory(viteConfig, ctx);
1620
+ await buildEnvironment("client");
1621
+ let serverEnvironmentNames = getServerEnvironmentKeys(
1622
+ environmentOptionsResolvers,
1623
+ buildManifest
1480
1624
  );
1625
+ await Promise.all(serverEnvironmentNames.map(buildEnvironment));
1626
+ await cleanViteManifests(environmentsOptions, ctx);
1481
1627
  await reactRouterConfig.buildEnd?.({
1482
1628
  buildManifest,
1483
1629
  reactRouterConfig,
1484
1630
  viteConfig
1485
1631
  });
1486
1632
  }
1487
- var import_node_path2, import_fs_extra, import_picocolors5;
1633
+ var import_picocolors5;
1488
1634
  var init_build = __esm({
1489
1635
  "vite/build.ts"() {
1490
1636
  "use strict";
1491
- import_node_path2 = __toESM(require("path"));
1492
- import_fs_extra = __toESM(require("fs-extra"));
1493
1637
  import_picocolors5 = __toESM(require("picocolors"));
1638
+ init_config();
1494
1639
  init_plugin();
1495
1640
  init_invariant();
1496
1641
  init_vite();
@@ -1566,8 +1711,8 @@ var import_semver = __toESM(require("semver"));
1566
1711
  var import_picocolors8 = __toESM(require("picocolors"));
1567
1712
 
1568
1713
  // cli/commands.ts
1569
- var path9 = __toESM(require("path"));
1570
- var import_fs_extra2 = __toESM(require("fs-extra"));
1714
+ var path8 = __toESM(require("path"));
1715
+ var import_fs_extra = __toESM(require("fs-extra"));
1571
1716
  var import_package_json2 = __toESM(require("@npmcli/package-json"));
1572
1717
  var import_exit_hook = __toESM(require("exit-hook"));
1573
1718
  var import_picocolors7 = __toESM(require("picocolors"));
@@ -1635,8 +1780,8 @@ var babel = __toESM(require("@babel/core"));
1635
1780
  var import_plugin_syntax_jsx = __toESM(require("@babel/plugin-syntax-jsx"));
1636
1781
  var import_preset_typescript = __toESM(require("@babel/preset-typescript"));
1637
1782
  var import_prettier = __toESM(require("prettier"));
1638
- function transpile(tsx2, options = {}) {
1639
- let mjs = babel.transformSync(tsx2, {
1783
+ function transpile(tsx, options = {}) {
1784
+ let mjs = babel.transformSync(tsx, {
1640
1785
  compact: false,
1641
1786
  cwd: options.cwd,
1642
1787
  filename: options.filename,
@@ -1720,14 +1865,14 @@ async function generateEntry(entry, reactRouterRoot, flags = {}) {
1720
1865
  console.error(import_picocolors7.default.red(`No default server entry detected.`));
1721
1866
  return;
1722
1867
  }
1723
- let defaultsDirectory = path9.resolve(
1724
- path9.dirname(require.resolve("@react-router/dev/package.json")),
1868
+ let defaultsDirectory = path8.resolve(
1869
+ path8.dirname(require.resolve("@react-router/dev/package.json")),
1725
1870
  "dist",
1726
1871
  "config",
1727
1872
  "defaults"
1728
1873
  );
1729
- let defaultEntryClient = path9.resolve(defaultsDirectory, "entry.client.tsx");
1730
- let defaultEntryServer = path9.resolve(
1874
+ let defaultEntryClient = path8.resolve(defaultsDirectory, "entry.client.tsx");
1875
+ let defaultEntryServer = path8.resolve(
1731
1876
  defaultsDirectory,
1732
1877
  `entry.server.node.tsx`
1733
1878
  );
@@ -1736,19 +1881,19 @@ async function generateEntry(entry, reactRouterRoot, flags = {}) {
1736
1881
  let useTypeScript = flags.typescript ?? true;
1737
1882
  let outputExtension = useTypeScript ? "tsx" : "jsx";
1738
1883
  let outputEntry = `${entry}.${outputExtension}`;
1739
- let outputFile2 = path9.resolve(appDirectory, outputEntry);
1884
+ let outputFile2 = path8.resolve(appDirectory, outputEntry);
1740
1885
  if (!useTypeScript) {
1741
1886
  let javascript = transpile(contents, {
1742
1887
  cwd: rootDirectory,
1743
1888
  filename: isServerEntry ? defaultEntryServer : defaultEntryClient
1744
1889
  });
1745
- await import_fs_extra2.default.writeFile(outputFile2, javascript, "utf-8");
1890
+ await import_fs_extra.default.writeFile(outputFile2, javascript, "utf-8");
1746
1891
  } else {
1747
- await import_fs_extra2.default.writeFile(outputFile2, contents, "utf-8");
1892
+ await import_fs_extra.default.writeFile(outputFile2, contents, "utf-8");
1748
1893
  }
1749
1894
  console.log(
1750
1895
  import_picocolors7.default.blue(
1751
- `Entry file ${entry} created at ${path9.relative(
1896
+ `Entry file ${entry} created at ${path8.relative(
1752
1897
  rootDirectory,
1753
1898
  outputFile2
1754
1899
  )}.`
@@ -1757,23 +1902,23 @@ async function generateEntry(entry, reactRouterRoot, flags = {}) {
1757
1902
  }
1758
1903
  async function checkForEntry(rootDirectory, appDirectory, entries2) {
1759
1904
  for (let entry of entries2) {
1760
- let entryPath = path9.resolve(appDirectory, entry);
1761
- let exists = await import_fs_extra2.default.pathExists(entryPath);
1905
+ let entryPath = path8.resolve(appDirectory, entry);
1906
+ let exists = await import_fs_extra.default.pathExists(entryPath);
1762
1907
  if (exists) {
1763
- let relative9 = path9.relative(rootDirectory, entryPath);
1764
- console.error(import_picocolors7.default.red(`Entry file ${relative9} already exists.`));
1908
+ let relative8 = path8.relative(rootDirectory, entryPath);
1909
+ console.error(import_picocolors7.default.red(`Entry file ${relative8} already exists.`));
1765
1910
  return process.exit(1);
1766
1911
  }
1767
1912
  }
1768
1913
  }
1769
1914
  async function createServerEntry(rootDirectory, appDirectory, inputFile) {
1770
1915
  await checkForEntry(rootDirectory, appDirectory, serverEntries);
1771
- let contents = await import_fs_extra2.default.readFile(inputFile, "utf-8");
1916
+ let contents = await import_fs_extra.default.readFile(inputFile, "utf-8");
1772
1917
  return contents;
1773
1918
  }
1774
1919
  async function createClientEntry(rootDirectory, appDirectory, inputFile) {
1775
1920
  await checkForEntry(rootDirectory, appDirectory, clientEntries);
1776
- let contents = await import_fs_extra2.default.readFile(inputFile, "utf-8");
1921
+ let contents = await import_fs_extra.default.readFile(inputFile, "utf-8");
1777
1922
  return contents;
1778
1923
  }
1779
1924
  async function typegen(root, flags) {