@react-router/dev 0.0.0-experimental-902325fda → 0.0.0-experimental-208f173a8

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-902325fda
3
+ * @react-router/dev v0.0.0-experimental-208f173a8
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,
@@ -635,6 +636,25 @@ var init_profiler = __esm({
635
636
  }
636
637
  });
637
638
 
639
+ // vite/babel.ts
640
+ var babel_exports = {};
641
+ __export(babel_exports, {
642
+ generate: () => generate,
643
+ parse: () => import_parser.parse,
644
+ t: () => t,
645
+ traverse: () => traverse
646
+ });
647
+ var import_parser, t, traverse, generate;
648
+ var init_babel = __esm({
649
+ "vite/babel.ts"() {
650
+ "use strict";
651
+ import_parser = require("@babel/parser");
652
+ t = __toESM(require("@babel/types"));
653
+ traverse = require("@babel/traverse").default;
654
+ generate = require("@babel/generator").default;
655
+ }
656
+ });
657
+
638
658
  // typegen/paths.ts
639
659
  function getTypesDir(ctx) {
640
660
  return Path2.join(ctx.rootDirectory, ".react-router/types");
@@ -657,7 +677,7 @@ var init_paths = __esm({
657
677
  });
658
678
 
659
679
  // typegen/generate.ts
660
- function generate(ctx, route) {
680
+ function generate2(ctx, route) {
661
681
  const lineage = getRouteLineage(ctx.config.routes, route);
662
682
  const urlpath = lineage.map((route2) => route2.path).join("/");
663
683
  const typesPath = getTypesPath(ctx, route);
@@ -765,7 +785,7 @@ var init_generate = __esm({
765
785
  Path3 = __toESM(require("pathe"));
766
786
  Pathe2 = __toESM(require("pathe/utils"));
767
787
  init_paths();
768
- noExtension = (path10) => Path3.join(Path3.dirname(path10), Pathe2.filename(path10));
788
+ noExtension = (path9) => Path3.join(Path3.dirname(path9), Pathe2.filename(path9));
769
789
  }
770
790
  });
771
791
 
@@ -817,36 +837,93 @@ async function writeAll(ctx) {
817
837
  import_node_fs3.default.rmSync(typegenDir, { recursive: true, force: true });
818
838
  Object.values(ctx.config.routes).forEach((route) => {
819
839
  const typesPath = getTypesPath(ctx, route);
820
- const content = generate(ctx, route);
840
+ const content = generate2(ctx, route);
821
841
  import_node_fs3.default.mkdirSync(Path4.dirname(typesPath), { recursive: true });
822
842
  import_node_fs3.default.writeFileSync(typesPath, content);
823
843
  });
844
+ const registerPath = Path4.join(typegenDir, "+register.ts");
845
+ import_node_fs3.default.writeFileSync(registerPath, register(ctx));
824
846
  }
825
- var import_node_fs3, Path4, import_picocolors3;
847
+ function register(ctx) {
848
+ const register2 = import_dedent2.default`
849
+ import "react-router";
850
+
851
+ declare module "react-router" {
852
+ interface Register {
853
+ params: Params;
854
+ }
855
+ }
856
+ `;
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
+ if (route.id !== "root" && !route.path) return void 0;
864
+ const lineage = getRouteLineage2(ctx.config.routes, route);
865
+ const fullpath = route.id === "root" ? "/" : lineage.map((route2) => route2.path).filter((path9) => path9 !== void 0).join("/");
866
+ const params = parseParams2(fullpath);
867
+ return t2.tsPropertySignature(
868
+ t2.stringLiteral(fullpath),
869
+ t2.tsTypeAnnotation(
870
+ t2.tsTypeLiteral(
871
+ Object.entries(params).map(([param, isRequired]) => {
872
+ const property = t2.tsPropertySignature(
873
+ t2.stringLiteral(param),
874
+ t2.tsTypeAnnotation(t2.tsStringKeyword())
875
+ );
876
+ property.optional = !isRequired;
877
+ return property;
878
+ })
879
+ )
880
+ )
881
+ );
882
+ }).filter((x) => x !== void 0)
883
+ )
884
+ );
885
+ return [register2, generate(typeParams).code].join("\n\n");
886
+ }
887
+ function parseParams2(fullpath) {
888
+ const result = {};
889
+ let segments = fullpath.split("/");
890
+ segments.forEach((segment) => {
891
+ const match = segment.match(/^:([\w-]+)(\?)?/);
892
+ if (!match) return;
893
+ const param = match[1];
894
+ const isRequired = match[2] === void 0;
895
+ result[param] ||= isRequired;
896
+ return;
897
+ });
898
+ const hasSplat = segments.at(-1) === "*";
899
+ if (hasSplat) result["*"] = true;
900
+ return result;
901
+ }
902
+ function getRouteLineage2(routes2, route) {
903
+ const result = [];
904
+ while (route) {
905
+ result.push(route);
906
+ if (!route.parentId) break;
907
+ route = routes2[route.parentId];
908
+ }
909
+ result.reverse();
910
+ return result;
911
+ }
912
+ var import_node_fs3, import_dedent2, Path4, import_picocolors3;
826
913
  var init_typegen = __esm({
827
914
  "typegen/index.ts"() {
828
915
  "use strict";
829
916
  import_node_fs3 = __toESM(require("fs"));
917
+ import_dedent2 = __toESM(require("dedent"));
830
918
  Path4 = __toESM(require("pathe"));
831
919
  import_picocolors3 = __toESM(require("picocolors"));
832
920
  init_config();
921
+ init_babel();
833
922
  init_generate();
834
923
  init_paths();
835
924
  }
836
925
  });
837
926
 
838
- // vite/babel.ts
839
- var import_parser, t, traverse, generate2;
840
- var init_babel = __esm({
841
- "vite/babel.ts"() {
842
- "use strict";
843
- import_parser = require("@babel/parser");
844
- t = __toESM(require("@babel/types"));
845
- traverse = require("@babel/traverse").default;
846
- generate2 = require("@babel/generator").default;
847
- }
848
- });
849
-
850
927
  // vite/node-adapter.ts
851
928
  var import_node_events, import_node_stream, import_set_cookie_parser, import_node;
852
929
  var init_node_adapter = __esm({
@@ -970,11 +1047,11 @@ var init_route_chunks = __esm({
970
1047
  });
971
1048
 
972
1049
  // vite/with-props.ts
973
- var import_dedent2, vmod;
1050
+ var import_dedent3, vmod;
974
1051
  var init_with_props = __esm({
975
1052
  "vite/with-props.ts"() {
976
1053
  "use strict";
977
- import_dedent2 = __toESM(require("dedent"));
1054
+ import_dedent3 = __toESM(require("dedent"));
978
1055
  init_babel();
979
1056
  init_virtual_module();
980
1057
  vmod = create("with-props");
@@ -985,11 +1062,12 @@ var init_with_props = __esm({
985
1062
  async function resolveViteConfig({
986
1063
  configFile,
987
1064
  mode,
988
- root
1065
+ root,
1066
+ plugins
989
1067
  }) {
990
1068
  let vite2 = getVite();
991
1069
  let viteConfig = await vite2.resolveConfig(
992
- { mode, configFile, root },
1070
+ { mode, configFile, root, plugins },
993
1071
  "build",
994
1072
  // command
995
1073
  "production",
@@ -1005,6 +1083,17 @@ async function resolveViteConfig({
1005
1083
  async function extractPluginContext(viteConfig) {
1006
1084
  return viteConfig["__reactRouterPluginContext"];
1007
1085
  }
1086
+ function isSeverBundleEnvironmentName(name) {
1087
+ return name.startsWith(SSR_BUNDLE_PREFIX);
1088
+ }
1089
+ function getServerEnvironmentEntries(record, buildManifest) {
1090
+ return Object.entries(record).filter(
1091
+ ([name]) => buildManifest.serverBundles ? isSeverBundleEnvironmentName(name) : name === "ssr"
1092
+ );
1093
+ }
1094
+ function getServerEnvironmentKeys(record, buildManifest) {
1095
+ return getServerEnvironmentEntries(record, buildManifest).map(([key]) => key);
1096
+ }
1008
1097
  function getAddressableRoutes(routes2) {
1009
1098
  let nonAddressableIds = /* @__PURE__ */ new Set();
1010
1099
  for (let id in routes2) {
@@ -1054,6 +1143,39 @@ function getRoutesByServerBundleId(buildManifest) {
1054
1143
  }
1055
1144
  return routesByServerBundleId;
1056
1145
  }
1146
+ async function cleanBuildDirectory(viteConfig, ctx) {
1147
+ let buildDirectory = ctx.reactRouterConfig.buildDirectory;
1148
+ let isWithinRoot = () => {
1149
+ let relativePath = path7.relative(ctx.rootDirectory, buildDirectory);
1150
+ return !relativePath.startsWith("..") && !path7.isAbsolute(relativePath);
1151
+ };
1152
+ if (viteConfig.build.emptyOutDir ?? isWithinRoot()) {
1153
+ await fse.remove(buildDirectory);
1154
+ }
1155
+ }
1156
+ async function cleanViteManifests(environmentsOptions, ctx) {
1157
+ let viteManifestPaths = Object.entries(environmentsOptions).map(
1158
+ ([environmentName, options]) => {
1159
+ let outDir = options.build?.outDir;
1160
+ invariant(outDir, `Expected build.outDir for ${environmentName}`);
1161
+ return path7.join(outDir, ".vite/manifest.json");
1162
+ }
1163
+ );
1164
+ await Promise.all(
1165
+ viteManifestPaths.map(async (viteManifestPath) => {
1166
+ let manifestExists = await fse.pathExists(viteManifestPath);
1167
+ if (!manifestExists) return;
1168
+ if (!ctx.viteManifestEnabled) {
1169
+ await fse.remove(viteManifestPath);
1170
+ }
1171
+ let viteDir = path7.dirname(viteManifestPath);
1172
+ let viteDirFiles = await fse.readdir(viteDir);
1173
+ if (viteDirFiles.length === 0) {
1174
+ await fse.remove(viteDir);
1175
+ }
1176
+ })
1177
+ );
1178
+ }
1057
1179
  async function getBuildManifest(ctx) {
1058
1180
  let { routes: routes2, serverBundles, appDirectory } = ctx.reactRouterConfig;
1059
1181
  if (!serverBundles) {
@@ -1113,77 +1235,101 @@ async function getBuildManifest(ctx) {
1113
1235
  );
1114
1236
  return buildManifest;
1115
1237
  }
1116
- function mergeBuildOptions(base, overrides) {
1238
+ function mergeEnvironmentOptions(base, ...overrides) {
1117
1239
  let vite2 = getVite();
1118
- return vite2.mergeConfig({ build: base }, { build: overrides }).build;
1240
+ return overrides.reduce(
1241
+ (merged, override) => vite2.mergeConfig(merged, override, false),
1242
+ base
1243
+ );
1119
1244
  }
1120
- async function getEnvironmentOptionsResolvers(ctx, buildManifest) {
1245
+ async function getEnvironmentOptionsResolvers(ctx, buildManifest, viteCommand) {
1121
1246
  let { serverBuildFile, serverModuleFormat } = ctx.reactRouterConfig;
1122
- function getBaseBuildOptions({
1247
+ let packageRoot = path7.dirname(
1248
+ require.resolve("@react-router/dev/package.json")
1249
+ );
1250
+ let { moduleSyncEnabled } = await import(`file:///${path7.join(packageRoot, "module-sync-enabled/index.mjs")}`);
1251
+ let vite2 = getVite();
1252
+ let viteServerConditions = [
1253
+ ...vite2.defaultServerConditions ?? [],
1254
+ ...moduleSyncEnabled ? ["module-sync"] : []
1255
+ ];
1256
+ function getBaseOptions({
1123
1257
  viteUserConfig
1124
1258
  }) {
1125
1259
  return {
1126
- cssMinify: viteUserConfig.build?.cssMinify ?? true,
1127
- manifest: true,
1128
- // The manifest is enabled for all builds to detect SSR-only assets
1129
- rollupOptions: {
1130
- preserveEntrySignatures: "exports-only",
1131
- // Silence Rollup "use client" warnings
1132
- // Adapted from https://github.com/vitejs/vite-plugin-react/pull/144
1133
- onwarn(warning, defaultHandler) {
1134
- if (warning.code === "MODULE_LEVEL_DIRECTIVE" && warning.message.includes("use client")) {
1135
- return;
1136
- }
1137
- let userHandler = viteUserConfig.build?.rollupOptions?.onwarn;
1138
- if (userHandler) {
1139
- userHandler(warning, defaultHandler);
1140
- } else {
1141
- defaultHandler(warning);
1260
+ build: {
1261
+ cssMinify: viteUserConfig.build?.cssMinify ?? true,
1262
+ manifest: true,
1263
+ // The manifest is enabled for all builds to detect SSR-only assets
1264
+ rollupOptions: {
1265
+ preserveEntrySignatures: "exports-only",
1266
+ // Silence Rollup "use client" warnings
1267
+ // Adapted from https://github.com/vitejs/vite-plugin-react/pull/144
1268
+ onwarn(warning, defaultHandler) {
1269
+ if (warning.code === "MODULE_LEVEL_DIRECTIVE" && warning.message.includes("use client")) {
1270
+ return;
1271
+ }
1272
+ let userHandler = viteUserConfig.build?.rollupOptions?.onwarn;
1273
+ if (userHandler) {
1274
+ userHandler(warning, defaultHandler);
1275
+ } else {
1276
+ defaultHandler(warning);
1277
+ }
1142
1278
  }
1143
1279
  }
1144
1280
  }
1145
1281
  };
1146
1282
  }
1147
- function getBaseServerBuildOptions({
1283
+ function getBaseServerOptions({
1148
1284
  viteUserConfig
1149
1285
  }) {
1150
- return mergeBuildOptions(getBaseBuildOptions({ viteUserConfig }), {
1151
- // We move SSR-only assets to client assets. Note that the
1152
- // SSR build can also emit code-split JS files (e.g. by
1153
- // dynamic import) under the same assets directory
1154
- // regardless of "ssrEmitAssets" option, so we also need to
1155
- // keep these JS files have to be kept as-is.
1156
- ssrEmitAssets: true,
1157
- copyPublicDir: false,
1158
- // Assets in the public directory are only used by the client
1159
- rollupOptions: {
1160
- output: {
1161
- entryFileNames: serverBuildFile,
1162
- format: serverModuleFormat
1286
+ let conditions = viteCommand === "build" ? viteServerConditions : ["development", ...viteServerConditions];
1287
+ return mergeEnvironmentOptions(getBaseOptions({ viteUserConfig }), {
1288
+ resolve: {
1289
+ external: ssrExternals,
1290
+ conditions,
1291
+ externalConditions: conditions
1292
+ },
1293
+ build: {
1294
+ // We move SSR-only assets to client assets. Note that the
1295
+ // SSR build can also emit code-split JS files (e.g. by
1296
+ // dynamic import) under the same assets directory
1297
+ // regardless of "ssrEmitAssets" option, so we also need to
1298
+ // keep these JS files have to be kept as-is.
1299
+ ssrEmitAssets: true,
1300
+ copyPublicDir: false,
1301
+ // Assets in the public directory are only used by the client
1302
+ rollupOptions: {
1303
+ output: {
1304
+ entryFileNames: serverBuildFile,
1305
+ format: serverModuleFormat
1306
+ }
1163
1307
  }
1164
1308
  }
1165
1309
  });
1166
1310
  }
1167
1311
  let environmentOptionsResolvers = {
1168
- client: ({ viteUserConfig }) => ({
1169
- build: mergeBuildOptions(getBaseBuildOptions({ viteUserConfig }), {
1312
+ client: ({ viteUserConfig }) => mergeEnvironmentOptions(getBaseOptions({ viteUserConfig }), {
1313
+ build: {
1170
1314
  rollupOptions: {
1171
1315
  input: [
1172
1316
  ctx.entryClientFilePath,
1173
- ...Object.values(ctx.reactRouterConfig.routes).flatMap((route) => {
1174
- let routeFilePath = path7.resolve(
1175
- ctx.reactRouterConfig.appDirectory,
1176
- route.file
1177
- );
1178
- let isRootRoute = route.file === ctx.reactRouterConfig.routes.root.file;
1179
- let code = fse.readFileSync(routeFilePath, "utf-8");
1180
- return [
1181
- `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
1182
- ...ctx.reactRouterConfig.future.unstable_splitRouteModules && !isRootRoute ? routeChunkExportNames.map(
1183
- (exportName) => code.includes(exportName) ? getRouteChunkModuleId(routeFilePath, exportName) : null
1184
- ) : []
1185
- ].filter(isNonNullable);
1186
- })
1317
+ ...Object.values(ctx.reactRouterConfig.routes).flatMap(
1318
+ (route) => {
1319
+ let routeFilePath = path7.resolve(
1320
+ ctx.reactRouterConfig.appDirectory,
1321
+ route.file
1322
+ );
1323
+ let isRootRoute = route.file === ctx.reactRouterConfig.routes.root.file;
1324
+ let code = fse.readFileSync(routeFilePath, "utf-8");
1325
+ return [
1326
+ `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
1327
+ ...ctx.reactRouterConfig.future.unstable_splitRouteModules && !isRootRoute ? routeChunkExportNames.map(
1328
+ (exportName) => code.includes(exportName) ? getRouteChunkModuleId(routeFilePath, exportName) : null
1329
+ ) : []
1330
+ ].filter(isNonNullable);
1331
+ }
1332
+ )
1187
1333
  ],
1188
1334
  output: {
1189
1335
  entryFileNames({ moduleIds }) {
@@ -1195,19 +1341,19 @@ async function getEnvironmentOptionsResolvers(ctx, buildManifest) {
1195
1341
  }
1196
1342
  },
1197
1343
  outDir: getClientBuildDirectory(ctx.reactRouterConfig)
1198
- })
1344
+ }
1199
1345
  })
1200
1346
  };
1201
1347
  if (hasServerBundles(buildManifest)) {
1202
1348
  for (let [serverBundleId, routes2] of Object.entries(
1203
1349
  getRoutesByServerBundleId(buildManifest)
1204
1350
  )) {
1205
- environmentOptionsResolvers[`ssr-bundle-${serverBundleId}`] = ({
1206
- viteUserConfig
1207
- }) => ({
1208
- build: mergeBuildOptions(
1209
- getBaseServerBuildOptions({ viteUserConfig }),
1210
- {
1351
+ const serverBundleEnvironmentId = serverBundleId.replaceAll("-", "_");
1352
+ const environmentName = `${SSR_BUNDLE_PREFIX}${serverBundleEnvironmentId}`;
1353
+ environmentOptionsResolvers[environmentName] = ({ viteUserConfig }) => mergeEnvironmentOptions(
1354
+ getBaseServerOptions({ viteUserConfig }),
1355
+ {
1356
+ build: {
1211
1357
  outDir: getServerBuildDirectory(ctx, { serverBundleId }),
1212
1358
  rollupOptions: {
1213
1359
  input: `${virtual.serverBuild.id}?route-ids=${Object.keys(
@@ -1215,25 +1361,37 @@ async function getEnvironmentOptionsResolvers(ctx, buildManifest) {
1215
1361
  ).join(",")}`
1216
1362
  }
1217
1363
  }
1218
- )
1219
- });
1364
+ },
1365
+ // Ensure server bundle environments extend the user's SSR
1366
+ // environment config if it exists
1367
+ viteUserConfig.environments?.ssr ?? {}
1368
+ );
1220
1369
  }
1221
1370
  } else {
1222
- environmentOptionsResolvers.ssr = ({ viteUserConfig }) => ({
1223
- build: mergeBuildOptions(getBaseServerBuildOptions({ viteUserConfig }), {
1371
+ environmentOptionsResolvers.ssr = ({ viteUserConfig }) => mergeEnvironmentOptions(getBaseServerOptions({ viteUserConfig }), {
1372
+ build: {
1224
1373
  outDir: getServerBuildDirectory(ctx),
1225
1374
  rollupOptions: {
1226
1375
  input: viteUserConfig.build?.rollupOptions?.input ?? virtual.serverBuild.id
1227
1376
  }
1228
- })
1377
+ }
1229
1378
  });
1230
1379
  }
1231
1380
  return environmentOptionsResolvers;
1232
1381
  }
1382
+ function resolveEnvironmentsOptions(environmentResolvers, resolverOptions) {
1383
+ let environmentOptions = {};
1384
+ for (let [environmentName, resolver] of Object.entries(
1385
+ environmentResolvers
1386
+ )) {
1387
+ environmentOptions[environmentName] = resolver(resolverOptions);
1388
+ }
1389
+ return environmentOptions;
1390
+ }
1233
1391
  function isNonNullable(x) {
1234
1392
  return x != null;
1235
1393
  }
1236
- 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;
1394
+ 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;
1237
1395
  var init_plugin = __esm({
1238
1396
  "vite/plugin.ts"() {
1239
1397
  "use strict";
@@ -1262,6 +1420,7 @@ var init_plugin = __esm({
1262
1420
  init_config();
1263
1421
  init_with_props();
1264
1422
  BUILD_CLIENT_ROUTE_QUERY_STRING = "?__react-router-build-client-route";
1423
+ SSR_BUNDLE_PREFIX = "ssrBundle_";
1265
1424
  virtualHmrRuntime = create("hmr-runtime");
1266
1425
  virtualInjectHmrRuntime = create("inject-hmr-runtime");
1267
1426
  virtual = {
@@ -1312,34 +1471,83 @@ var build_exports = {};
1312
1471
  __export(build_exports, {
1313
1472
  build: () => build
1314
1473
  });
1315
- async function cleanBuildDirectory(viteConfig, ctx) {
1316
- let buildDirectory = ctx.reactRouterConfig.buildDirectory;
1317
- let isWithinRoot = () => {
1318
- let relativePath = import_node_path2.default.relative(ctx.rootDirectory, buildDirectory);
1319
- return !relativePath.startsWith("..") && !import_node_path2.default.isAbsolute(relativePath);
1320
- };
1321
- if (viteConfig.build.emptyOutDir ?? isWithinRoot()) {
1322
- await import_fs_extra.default.remove(buildDirectory);
1474
+ async function build(root, viteBuildOptions) {
1475
+ await preloadVite();
1476
+ let vite2 = getVite();
1477
+ let configResult = await loadConfig({ rootDirectory: root });
1478
+ if (!configResult.ok) {
1479
+ throw new Error(configResult.error);
1480
+ }
1481
+ let config = configResult.value;
1482
+ let unstable_viteEnvironmentApi = config.future.unstable_viteEnvironmentApi;
1483
+ let viteMajor = parseInt(vite2.version.split(".")[0], 10);
1484
+ if (unstable_viteEnvironmentApi && viteMajor === 5) {
1485
+ throw new Error(
1486
+ "The future.unstable_viteEnvironmentApi option is not supported in Vite 5"
1487
+ );
1323
1488
  }
1489
+ return await (unstable_viteEnvironmentApi ? viteAppBuild(root, viteBuildOptions) : viteBuild(root, viteBuildOptions));
1324
1490
  }
1325
- function getViteManifestPaths(environmentOptionsResolvers) {
1326
- return Object.entries(environmentOptionsResolvers).map(
1327
- ([environmentName, resolveOptions]) => {
1328
- invariant(
1329
- resolveOptions,
1330
- `Expected build environment options resolver for ${environmentName}`
1331
- );
1332
- let options = resolveOptions({
1333
- viteCommand: "build",
1334
- viteUserConfig: {}
1335
- });
1336
- let outDir = options.build.outDir;
1337
- invariant(outDir, `Expected build.outDir for ${environmentName}`);
1338
- return import_node_path2.default.join(outDir, ".vite/manifest.json");
1339
- }
1340
- );
1491
+ async function viteAppBuild(root, {
1492
+ assetsInlineLimit,
1493
+ clearScreen,
1494
+ config: configFile,
1495
+ emptyOutDir,
1496
+ force,
1497
+ logLevel,
1498
+ minify,
1499
+ mode,
1500
+ sourcemapClient,
1501
+ sourcemapServer
1502
+ }) {
1503
+ let vite2 = getVite();
1504
+ let builder = await vite2.createBuilder({
1505
+ root,
1506
+ mode,
1507
+ configFile,
1508
+ build: {
1509
+ assetsInlineLimit,
1510
+ emptyOutDir,
1511
+ minify
1512
+ },
1513
+ optimizeDeps: { force },
1514
+ clearScreen,
1515
+ logLevel,
1516
+ plugins: [
1517
+ {
1518
+ name: "react-router:cli-config",
1519
+ configEnvironment(name) {
1520
+ if (sourcemapClient && name === "client") {
1521
+ return {
1522
+ build: {
1523
+ sourcemap: sourcemapClient
1524
+ }
1525
+ };
1526
+ }
1527
+ if (sourcemapServer && name !== "client") {
1528
+ return {
1529
+ build: {
1530
+ sourcemap: sourcemapServer
1531
+ }
1532
+ };
1533
+ }
1534
+ },
1535
+ configResolved(config) {
1536
+ let hasReactRouterPlugin = config.plugins.find(
1537
+ (plugin2) => plugin2.name === "react-router"
1538
+ );
1539
+ if (!hasReactRouterPlugin) {
1540
+ throw new Error(
1541
+ "React Router Vite plugin not found in Vite config"
1542
+ );
1543
+ }
1544
+ }
1545
+ }
1546
+ ]
1547
+ });
1548
+ await builder.buildApp();
1341
1549
  }
1342
- async function build(root, {
1550
+ async function viteBuild(root, {
1343
1551
  assetsInlineLimit,
1344
1552
  clearScreen,
1345
1553
  config: configFile,
@@ -1351,24 +1559,32 @@ async function build(root, {
1351
1559
  sourcemapClient,
1352
1560
  sourcemapServer
1353
1561
  }) {
1354
- await preloadVite();
1355
- let viteConfig = await resolveViteConfig({ configFile, mode, root });
1356
- const ctx = await extractPluginContext(viteConfig);
1562
+ let viteUserConfig = {};
1563
+ let viteConfig = await resolveViteConfig({
1564
+ configFile,
1565
+ mode,
1566
+ root,
1567
+ plugins: [
1568
+ {
1569
+ name: "react-router:extract-vite-user-config",
1570
+ config(config) {
1571
+ viteUserConfig = config;
1572
+ }
1573
+ }
1574
+ ]
1575
+ });
1576
+ let ctx = await extractPluginContext(viteConfig);
1357
1577
  if (!ctx) {
1358
1578
  console.error(
1359
1579
  import_picocolors5.default.red("React Router Vite plugin not found in Vite config")
1360
1580
  );
1361
1581
  process.exit(1);
1362
1582
  }
1363
- let { reactRouterConfig } = ctx;
1364
- let vite2 = getVite();
1365
- async function viteBuild(environmentOptionsResolvers2, environmentName) {
1583
+ async function buildEnvironment(environmentName) {
1584
+ let vite2 = getVite();
1366
1585
  let ssr = environmentName !== "client";
1367
- let resolveOptions = environmentOptionsResolvers2[environmentName];
1368
- invariant(
1369
- resolveOptions,
1370
- `Missing environment options resolver for ${environmentName}`
1371
- );
1586
+ let resolveOptions = environmentOptionsResolvers[environmentName];
1587
+ invariant(resolveOptions);
1372
1588
  let environmentBuildContext = {
1373
1589
  name: environmentName,
1374
1590
  resolveOptions
@@ -1390,49 +1606,37 @@ async function build(root, {
1390
1606
  ...{ __reactRouterEnvironmentBuildContext: environmentBuildContext }
1391
1607
  });
1392
1608
  }
1393
- await cleanBuildDirectory(viteConfig, ctx);
1609
+ let { reactRouterConfig } = ctx;
1394
1610
  let buildManifest = await getBuildManifest(ctx);
1395
1611
  let environmentOptionsResolvers = await getEnvironmentOptionsResolvers(
1396
1612
  ctx,
1397
- buildManifest
1613
+ buildManifest,
1614
+ "build"
1398
1615
  );
1399
- await viteBuild(environmentOptionsResolvers, "client");
1400
- let serverEnvironmentNames = Object.keys(
1401
- environmentOptionsResolvers
1402
- ).filter((environmentName) => environmentName !== "client");
1403
- await Promise.all(
1404
- serverEnvironmentNames.map(
1405
- (environmentName) => viteBuild(environmentOptionsResolvers, environmentName)
1406
- )
1616
+ let environmentsOptions = resolveEnvironmentsOptions(
1617
+ environmentOptionsResolvers,
1618
+ { viteUserConfig }
1407
1619
  );
1408
- let viteManifestPaths = getViteManifestPaths(environmentOptionsResolvers);
1409
- await Promise.all(
1410
- viteManifestPaths.map(async (viteManifestPath) => {
1411
- let manifestExists = await import_fs_extra.default.pathExists(viteManifestPath);
1412
- if (!manifestExists) return;
1413
- if (!ctx.viteManifestEnabled) {
1414
- await import_fs_extra.default.remove(viteManifestPath);
1415
- }
1416
- let viteDir = import_node_path2.default.dirname(viteManifestPath);
1417
- let viteDirFiles = await import_fs_extra.default.readdir(viteDir);
1418
- if (viteDirFiles.length === 0) {
1419
- await import_fs_extra.default.remove(viteDir);
1420
- }
1421
- })
1620
+ await cleanBuildDirectory(viteConfig, ctx);
1621
+ await buildEnvironment("client");
1622
+ let serverEnvironmentNames = getServerEnvironmentKeys(
1623
+ environmentOptionsResolvers,
1624
+ buildManifest
1422
1625
  );
1626
+ await Promise.all(serverEnvironmentNames.map(buildEnvironment));
1627
+ await cleanViteManifests(environmentsOptions, ctx);
1423
1628
  await reactRouterConfig.buildEnd?.({
1424
1629
  buildManifest,
1425
1630
  reactRouterConfig,
1426
1631
  viteConfig
1427
1632
  });
1428
1633
  }
1429
- var import_node_path2, import_fs_extra, import_picocolors5;
1634
+ var import_picocolors5;
1430
1635
  var init_build = __esm({
1431
1636
  "vite/build.ts"() {
1432
1637
  "use strict";
1433
- import_node_path2 = __toESM(require("path"));
1434
- import_fs_extra = __toESM(require("fs-extra"));
1435
1638
  import_picocolors5 = __toESM(require("picocolors"));
1639
+ init_config();
1436
1640
  init_plugin();
1437
1641
  init_invariant();
1438
1642
  init_vite();
@@ -1508,8 +1712,8 @@ var import_semver = __toESM(require("semver"));
1508
1712
  var import_picocolors8 = __toESM(require("picocolors"));
1509
1713
 
1510
1714
  // cli/commands.ts
1511
- var path9 = __toESM(require("path"));
1512
- var import_fs_extra2 = __toESM(require("fs-extra"));
1715
+ var path8 = __toESM(require("path"));
1716
+ var import_fs_extra = __toESM(require("fs-extra"));
1513
1717
  var import_package_json2 = __toESM(require("@npmcli/package-json"));
1514
1718
  var import_exit_hook = __toESM(require("exit-hook"));
1515
1719
  var import_picocolors7 = __toESM(require("picocolors"));
@@ -1662,14 +1866,14 @@ async function generateEntry(entry, reactRouterRoot, flags = {}) {
1662
1866
  console.error(import_picocolors7.default.red(`No default server entry detected.`));
1663
1867
  return;
1664
1868
  }
1665
- let defaultsDirectory = path9.resolve(
1666
- path9.dirname(require.resolve("@react-router/dev/package.json")),
1869
+ let defaultsDirectory = path8.resolve(
1870
+ path8.dirname(require.resolve("@react-router/dev/package.json")),
1667
1871
  "dist",
1668
1872
  "config",
1669
1873
  "defaults"
1670
1874
  );
1671
- let defaultEntryClient = path9.resolve(defaultsDirectory, "entry.client.tsx");
1672
- let defaultEntryServer = path9.resolve(
1875
+ let defaultEntryClient = path8.resolve(defaultsDirectory, "entry.client.tsx");
1876
+ let defaultEntryServer = path8.resolve(
1673
1877
  defaultsDirectory,
1674
1878
  `entry.server.node.tsx`
1675
1879
  );
@@ -1678,19 +1882,19 @@ async function generateEntry(entry, reactRouterRoot, flags = {}) {
1678
1882
  let useTypeScript = flags.typescript ?? true;
1679
1883
  let outputExtension = useTypeScript ? "tsx" : "jsx";
1680
1884
  let outputEntry = `${entry}.${outputExtension}`;
1681
- let outputFile2 = path9.resolve(appDirectory, outputEntry);
1885
+ let outputFile2 = path8.resolve(appDirectory, outputEntry);
1682
1886
  if (!useTypeScript) {
1683
1887
  let javascript = transpile(contents, {
1684
1888
  cwd: rootDirectory,
1685
1889
  filename: isServerEntry ? defaultEntryServer : defaultEntryClient
1686
1890
  });
1687
- await import_fs_extra2.default.writeFile(outputFile2, javascript, "utf-8");
1891
+ await import_fs_extra.default.writeFile(outputFile2, javascript, "utf-8");
1688
1892
  } else {
1689
- await import_fs_extra2.default.writeFile(outputFile2, contents, "utf-8");
1893
+ await import_fs_extra.default.writeFile(outputFile2, contents, "utf-8");
1690
1894
  }
1691
1895
  console.log(
1692
1896
  import_picocolors7.default.blue(
1693
- `Entry file ${entry} created at ${path9.relative(
1897
+ `Entry file ${entry} created at ${path8.relative(
1694
1898
  rootDirectory,
1695
1899
  outputFile2
1696
1900
  )}.`
@@ -1699,10 +1903,10 @@ async function generateEntry(entry, reactRouterRoot, flags = {}) {
1699
1903
  }
1700
1904
  async function checkForEntry(rootDirectory, appDirectory, entries2) {
1701
1905
  for (let entry of entries2) {
1702
- let entryPath = path9.resolve(appDirectory, entry);
1703
- let exists = await import_fs_extra2.default.pathExists(entryPath);
1906
+ let entryPath = path8.resolve(appDirectory, entry);
1907
+ let exists = await import_fs_extra.default.pathExists(entryPath);
1704
1908
  if (exists) {
1705
- let relative8 = path9.relative(rootDirectory, entryPath);
1909
+ let relative8 = path8.relative(rootDirectory, entryPath);
1706
1910
  console.error(import_picocolors7.default.red(`Entry file ${relative8} already exists.`));
1707
1911
  return process.exit(1);
1708
1912
  }
@@ -1710,12 +1914,12 @@ async function checkForEntry(rootDirectory, appDirectory, entries2) {
1710
1914
  }
1711
1915
  async function createServerEntry(rootDirectory, appDirectory, inputFile) {
1712
1916
  await checkForEntry(rootDirectory, appDirectory, serverEntries);
1713
- let contents = await import_fs_extra2.default.readFile(inputFile, "utf-8");
1917
+ let contents = await import_fs_extra.default.readFile(inputFile, "utf-8");
1714
1918
  return contents;
1715
1919
  }
1716
1920
  async function createClientEntry(rootDirectory, appDirectory, inputFile) {
1717
1921
  await checkForEntry(rootDirectory, appDirectory, clientEntries);
1718
- let contents = await import_fs_extra2.default.readFile(inputFile, "utf-8");
1922
+ let contents = await import_fs_extra.default.readFile(inputFile, "utf-8");
1719
1923
  return contents;
1720
1924
  }
1721
1925
  async function typegen(root, flags) {