@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/vite.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-a2c4d7fad
2
+ * @react-router/dev v0.0.0-experimental-004e483a8
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -454,7 +454,8 @@ async function resolveConfig({
454
454
  }
455
455
  let future = {
456
456
  unstable_optimizeDeps: reactRouterUserConfig.future?.unstable_optimizeDeps ?? false,
457
- unstable_splitRouteModules: reactRouterUserConfig.future?.unstable_splitRouteModules ?? false
457
+ unstable_splitRouteModules: reactRouterUserConfig.future?.unstable_splitRouteModules ?? false,
458
+ unstable_viteEnvironmentApi: reactRouterUserConfig.future?.unstable_viteEnvironmentApi ?? false
458
459
  };
459
460
  let reactRouterConfig = deepFreeze({
460
461
  appDirectory,
@@ -669,6 +670,7 @@ function getTypesPath(ctx, route) {
669
670
  // typegen/generate.ts
670
671
  function generate2(ctx, route) {
671
672
  const lineage = getRouteLineage(ctx.config.routes, route);
673
+ const urlpath = lineage.map((route2) => route2.path).join("/");
672
674
  const typesPath = getTypesPath(ctx, route);
673
675
  const parents = lineage.slice(0, -1);
674
676
  const parentTypeImports = parents.map((parent, i) => {
@@ -686,15 +688,22 @@ function generate2(ctx, route) {
686
688
  // ${route.file}
687
689
 
688
690
  import type * as T from "react-router/route-module"
689
- import type { Routes } from "react-router/types"
690
691
 
691
692
  ${parentTypeImports}
692
693
 
693
- type RouteId = "${route.id}"
694
+ type Module = typeof import("../${Pathe2.filename(route.file)}.js")
694
695
 
695
- export type Info = Routes[RouteId] & {
696
+ export type Info = {
696
697
  parents: [${parents.map((_, i) => `Parent${i}`).join(", ")}],
697
- id: RouteId
698
+ id: "${route.id}"
699
+ file: "${route.file}"
700
+ path: "${route.path}"
701
+ params: {${formatParamProperties(
702
+ urlpath
703
+ )}} & { [key: string]: string | undefined }
704
+ module: Module
705
+ loaderData: T.CreateLoaderData<Module>
706
+ actionData: T.CreateActionData<Module>
698
707
  }
699
708
 
700
709
  export namespace Route {
@@ -730,9 +739,38 @@ function getRouteLineage(routes, route) {
730
739
  result.reverse();
731
740
  return result;
732
741
  }
742
+ function formatParamProperties(urlpath) {
743
+ const params = parseParams(urlpath);
744
+ const properties = Object.entries(params).map(([name, values]) => {
745
+ if (values.length === 1) {
746
+ const isOptional = values[0];
747
+ return isOptional ? `"${name}"?: string` : `"${name}": string`;
748
+ }
749
+ const items = values.map(
750
+ (isOptional) => isOptional ? "string | undefined" : "string"
751
+ );
752
+ return `"${name}": [${items.join(", ")}]`;
753
+ });
754
+ return properties.join("; ");
755
+ }
756
+ function parseParams(urlpath) {
757
+ const result = {};
758
+ let segments = urlpath.split("/");
759
+ segments.forEach((segment) => {
760
+ const match = segment.match(/^:([\w-]+)(\?)?/);
761
+ if (!match) return;
762
+ const param = match[1];
763
+ const isOptional = match[2] !== void 0;
764
+ result[param] ??= [];
765
+ result[param].push(isOptional);
766
+ return;
767
+ });
768
+ const hasSplat = segments.at(-1) === "*";
769
+ if (hasSplat) result["*"] = [false];
770
+ return result;
771
+ }
733
772
 
734
773
  // typegen/index.ts
735
- var { t: t2 } = babel_exports;
736
774
  async function watch(rootDirectory, { logger } = {}) {
737
775
  const ctx = await createContext2({ rootDirectory, watch: true });
738
776
  await writeAll(ctx);
@@ -780,92 +818,72 @@ async function writeAll(ctx) {
780
818
  import_node_fs2.default.mkdirSync(Path4.dirname(typesPath), { recursive: true });
781
819
  import_node_fs2.default.writeFileSync(typesPath, content);
782
820
  });
783
- Object.values(ctx.config.routes).map(
784
- (route) => t2.tsPropertySignature(
785
- t2.stringLiteral(route.id),
786
- t2.tsTypeAnnotation(
787
- t2.tsTypeLiteral([
788
- t2.tsPropertySignature(
789
- t2.identifier("parentId"),
790
- t2.tsTypeAnnotation(
791
- route.parentId ? t2.tsLiteralType(t2.stringLiteral(route.parentId)) : t2.tsUndefinedKeyword()
792
- )
793
- ),
794
- t2.tsPropertySignature(
795
- t2.identifier("path"),
796
- t2.tsTypeAnnotation(
797
- route.path ? t2.tsLiteralType(t2.stringLiteral(route.path)) : t2.tsUndefinedKeyword()
798
- )
799
- ),
800
- t2.tsPropertySignature(
801
- t2.identifier("module"),
802
- t2.tsTypeAnnotation(
803
- t2.tsTypeQuery(t2.tsImportType(t2.stringLiteral(route.file)))
804
- )
805
- )
806
- ])
807
- )
808
- )
809
- );
810
821
  const registerPath = Path4.join(typegenDir, "+register.ts");
811
822
  import_node_fs2.default.writeFileSync(registerPath, register(ctx));
812
823
  }
813
824
  function register(ctx) {
814
- const routes = generate(
815
- t2.tsTypeAliasDeclaration(
816
- t2.identifier("Routes"),
817
- null,
818
- t2.tsTypeLiteral(
819
- Object.values(ctx.config.routes).map(
820
- (route) => t2.tsPropertySignature(
821
- t2.stringLiteral(route.id),
822
- t2.tsTypeAnnotation(
823
- t2.tsTypeLiteral([
824
- t2.tsPropertySignature(
825
- t2.identifier("parentId"),
826
- t2.tsTypeAnnotation(
827
- route.parentId ? t2.tsLiteralType(t2.stringLiteral(route.parentId)) : t2.tsUndefinedKeyword()
828
- )
829
- ),
830
- t2.tsPropertySignature(
831
- t2.identifier("path"),
832
- t2.tsTypeAnnotation(
833
- route.path ? t2.tsLiteralType(t2.stringLiteral(route.path)) : t2.tsUndefinedKeyword()
834
- )
835
- ),
836
- t2.tsPropertySignature(
837
- t2.identifier("module"),
838
- t2.tsTypeAnnotation(
839
- t2.tsTypeQuery(
840
- t2.tsImportType(
841
- t2.stringLiteral(compiledModulePath(ctx, route))
842
- )
843
- )
844
- )
845
- )
846
- ])
847
- )
848
- )
849
- )
850
- )
851
- )
852
- ).code;
853
- const registerTypes = import_dedent2.default`
854
- import "react-router/types";
825
+ const register2 = import_dedent2.default`
826
+ import "react-router";
855
827
 
856
- declare module "react-router/types" {
828
+ declare module "react-router" {
857
829
  interface Register {
858
- routes: Routes;
830
+ params: Params;
859
831
  }
860
832
  }
861
833
  `;
862
- return [registerTypes, routes].join("\n\n");
834
+ const { t: t2 } = babel_exports;
835
+ const typeParams = t2.tsTypeAliasDeclaration(
836
+ t2.identifier("Params"),
837
+ null,
838
+ t2.tsTypeLiteral(
839
+ Object.values(ctx.config.routes).map((route) => {
840
+ const lineage = getRouteLineage2(ctx.config.routes, route);
841
+ const fullpath = lineage.map((route2) => route2.path).join("/");
842
+ const params = parseParams2(fullpath);
843
+ return t2.tsPropertySignature(
844
+ t2.stringLiteral(fullpath),
845
+ t2.tsTypeAnnotation(
846
+ t2.tsTypeLiteral(
847
+ Object.entries(params).map(([param, isRequired]) => {
848
+ const property = t2.tsPropertySignature(
849
+ t2.stringLiteral(param),
850
+ t2.tsTypeAnnotation(t2.tsStringKeyword())
851
+ );
852
+ property.optional = !isRequired;
853
+ return property;
854
+ })
855
+ )
856
+ )
857
+ );
858
+ })
859
+ )
860
+ );
861
+ return [register2, generate(typeParams).code].join("\n\n");
862
+ }
863
+ function parseParams2(fullpath) {
864
+ const result = {};
865
+ let segments = fullpath.split("/");
866
+ segments.forEach((segment) => {
867
+ const match = segment.match(/^:([\w-]+)(\?)?/);
868
+ if (!match) return;
869
+ const param = match[1];
870
+ const isRequired = match[2] === void 0;
871
+ result[param] ||= isRequired;
872
+ return;
873
+ });
874
+ const hasSplat = segments.at(-1) === "*";
875
+ if (hasSplat) result["*"] = true;
876
+ return result;
863
877
  }
864
- function compiledModulePath(ctx, route) {
865
- return "./" + Path4.relative(
866
- ctx.rootDirectory,
867
- Path4.join(ctx.config.appDirectory, route.file)
868
- ).replace(/\.(js|ts)x?$/, ".js");
878
+ function getRouteLineage2(routes, route) {
879
+ const result = [];
880
+ while (route) {
881
+ result.push(route);
882
+ if (!route.parentId) break;
883
+ route = routes[route.parentId];
884
+ }
885
+ result.reverse();
886
+ return result;
869
887
  }
870
888
 
871
889
  // vite/node-adapter.ts
@@ -1970,6 +1988,20 @@ var CLIENT_ROUTE_EXPORTS = [
1970
1988
  "shouldRevalidate"
1971
1989
  ];
1972
1990
  var BUILD_CLIENT_ROUTE_QUERY_STRING = "?__react-router-build-client-route";
1991
+ var SSR_BUNDLE_PREFIX = "ssrBundle_";
1992
+ function isSeverBundleEnvironmentName(name) {
1993
+ return name.startsWith(SSR_BUNDLE_PREFIX);
1994
+ }
1995
+ function getServerEnvironmentEntries(record, buildManifest) {
1996
+ return Object.entries(record).filter(
1997
+ ([name]) => buildManifest.serverBundles ? isSeverBundleEnvironmentName(name) : name === "ssr"
1998
+ );
1999
+ }
2000
+ function getServerEnvironmentValues(record, buildManifest) {
2001
+ return getServerEnvironmentEntries(record, buildManifest).map(
2002
+ ([, value]) => value
2003
+ );
2004
+ }
1973
2005
  var isRouteEntryModuleId = (id) => {
1974
2006
  return id.endsWith(BUILD_CLIENT_ROUTE_QUERY_STRING);
1975
2007
  };
@@ -2138,7 +2170,7 @@ var resolveEnvironmentBuildContext = ({
2138
2170
  let buildContext = viteUserConfig.__reactRouterEnvironmentBuildContext;
2139
2171
  let resolvedBuildContext = {
2140
2172
  name: buildContext.name,
2141
- options: buildContext.resolveOptions({ viteCommand, viteUserConfig })
2173
+ options: buildContext.resolveOptions({ viteUserConfig })
2142
2174
  };
2143
2175
  return resolvedBuildContext;
2144
2176
  };
@@ -2163,6 +2195,7 @@ var reactRouterVitePlugin = () => {
2163
2195
  let viteUserConfig;
2164
2196
  let viteConfigEnv;
2165
2197
  let viteConfig;
2198
+ let buildManifest;
2166
2199
  let cssModulesManifest = {};
2167
2200
  let viteChildCompiler = null;
2168
2201
  let cache = /* @__PURE__ */ new Map();
@@ -2481,14 +2514,6 @@ var reactRouterVitePlugin = () => {
2481
2514
  let viteClientConditions = [
2482
2515
  ...vite2.defaultClientConditions ?? []
2483
2516
  ];
2484
- let packageRoot = path6.dirname(
2485
- require.resolve("@react-router/dev/package.json")
2486
- );
2487
- let { moduleSyncEnabled } = await import(`file:///${path6.join(packageRoot, "module-sync-enabled/index.mjs")}`);
2488
- let viteServerConditions = [
2489
- ...vite2.defaultServerConditions ?? [],
2490
- ...moduleSyncEnabled ? ["module-sync"] : []
2491
- ];
2492
2517
  logger = vite2.createLogger(viteUserConfig.logLevel, {
2493
2518
  prefix: "[react-router]"
2494
2519
  });
@@ -2504,26 +2529,37 @@ var reactRouterVitePlugin = () => {
2504
2529
  watch: viteCommand === "serve"
2505
2530
  });
2506
2531
  await updatePluginContext();
2532
+ buildManifest = await getBuildManifest(ctx);
2507
2533
  Object.assign(
2508
2534
  process.env,
2509
2535
  vite2.loadEnv(
2510
2536
  viteConfigEnv.mode,
2511
- ctx.rootDirectory,
2537
+ viteUserConfig.envDir ?? ctx.rootDirectory,
2512
2538
  // We override default prefix of "VITE_" with a blank string since
2513
2539
  // we're targeting the server, so we want to load all environment
2514
2540
  // variables, not just those explicitly marked for the client
2515
2541
  ""
2516
2542
  )
2517
2543
  );
2544
+ let environments = await getEnvironmentsOptions(
2545
+ ctx,
2546
+ buildManifest,
2547
+ viteCommand,
2548
+ { viteUserConfig }
2549
+ );
2550
+ let serverEnvironment = getServerEnvironmentValues(
2551
+ environments,
2552
+ buildManifest
2553
+ )[0];
2554
+ invariant(serverEnvironment);
2555
+ let clientEnvironment = environments.client;
2556
+ invariant(clientEnvironment);
2518
2557
  return {
2519
2558
  __reactRouterPluginContext: ctx,
2520
2559
  appType: viteCommand === "serve" && viteConfigEnv.mode === "production" && ctx.reactRouterConfig.ssr === false ? "spa" : "custom",
2521
2560
  ssr: {
2522
- external: ssrExternals,
2523
- resolve: {
2524
- conditions: viteCommand === "build" ? viteServerConditions : ["development", ...viteServerConditions],
2525
- externalConditions: viteCommand === "build" ? viteServerConditions : ["development", ...viteServerConditions]
2526
- }
2561
+ external: serverEnvironment.resolve?.external,
2562
+ resolve: serverEnvironment.resolve
2527
2563
  },
2528
2564
  optimizeDeps: {
2529
2565
  entries: ctx.reactRouterConfig.future.unstable_optimizeDeps ? [
@@ -2572,11 +2608,41 @@ var reactRouterVitePlugin = () => {
2572
2608
  // will throw an error that the file is not allowed to be read.
2573
2609
  // https://vitejs.dev/config/server-options#server-fs-allow
2574
2610
  server: viteUserConfig.server?.fs?.allow ? { fs: { allow: defaultEntries } } : void 0,
2575
- build: ctx.environmentBuildContext?.options.build ?? (await getEnvironmentOptions(
2576
- ctx,
2577
- viteConfigEnv.isSsrBuild ? "ssr" : "client",
2578
- { viteCommand, viteUserConfig }
2579
- )).build
2611
+ ...ctx.reactRouterConfig.future.unstable_viteEnvironmentApi ? {
2612
+ environments,
2613
+ build: {
2614
+ // This isn't honored by the SSR environment config (which seems
2615
+ // to be a Vite bug?) so we set it here too.
2616
+ ssrEmitAssets: true
2617
+ },
2618
+ builder: {
2619
+ sharedConfigBuild: true,
2620
+ sharedPlugins: true,
2621
+ async buildApp(builder) {
2622
+ invariant(viteConfig);
2623
+ invariant(buildManifest);
2624
+ viteConfig.logger.info(
2625
+ "Using Vite Environment API (experimental)"
2626
+ );
2627
+ let { reactRouterConfig } = ctx;
2628
+ await cleanBuildDirectory(viteConfig, ctx);
2629
+ await builder.build(builder.environments.client);
2630
+ let serverEnvironments = getServerEnvironmentValues(
2631
+ builder.environments,
2632
+ buildManifest
2633
+ );
2634
+ await Promise.all(serverEnvironments.map(builder.build));
2635
+ await cleanViteManifests(environments, ctx);
2636
+ await reactRouterConfig.buildEnd?.({
2637
+ buildManifest,
2638
+ reactRouterConfig,
2639
+ viteConfig
2640
+ });
2641
+ }
2642
+ }
2643
+ } : {
2644
+ build: ctx.environmentBuildContext?.options.build ?? (viteConfigEnv.isSsrBuild ? serverEnvironment.build : clientEnvironment.build)
2645
+ }
2580
2646
  };
2581
2647
  },
2582
2648
  async configResolved(resolvedViteConfig) {
@@ -2734,14 +2800,15 @@ var reactRouterVitePlugin = () => {
2734
2800
  // After the SSR build is finished, we inspect the Vite manifest for
2735
2801
  // the SSR build and move server-only assets to client assets directory
2736
2802
  async handler() {
2737
- if (!viteConfigEnv.isSsrBuild) {
2803
+ let { future } = ctx.reactRouterConfig;
2804
+ if (future.unstable_viteEnvironmentApi ? this.environment.name === "client" : !viteConfigEnv.isSsrBuild) {
2738
2805
  return;
2739
2806
  }
2740
2807
  invariant(viteConfig);
2741
2808
  let clientBuildDirectory = getClientBuildDirectory(
2742
2809
  ctx.reactRouterConfig
2743
2810
  );
2744
- let serverBuildDirectory = ctx.environmentBuildContext?.options.build.outDir ?? getServerBuildDirectory(ctx);
2811
+ let serverBuildDirectory = future.unstable_viteEnvironmentApi ? this.environment.config?.build?.outDir : ctx.environmentBuildContext?.options.build?.outDir ?? getServerBuildDirectory(ctx);
2745
2812
  let ssrViteManifest = await loadViteManifest(serverBuildDirectory);
2746
2813
  let ssrAssetPaths = getViteManifestAssetPaths(ssrViteManifest);
2747
2814
  let movedAssetPaths = [];
@@ -3689,6 +3756,39 @@ function validateRouteChunks({
3689
3756
  ].join("\n\n")
3690
3757
  );
3691
3758
  }
3759
+ async function cleanBuildDirectory(viteConfig, ctx) {
3760
+ let buildDirectory = ctx.reactRouterConfig.buildDirectory;
3761
+ let isWithinRoot = () => {
3762
+ let relativePath = path6.relative(ctx.rootDirectory, buildDirectory);
3763
+ return !relativePath.startsWith("..") && !path6.isAbsolute(relativePath);
3764
+ };
3765
+ if (viteConfig.build.emptyOutDir ?? isWithinRoot()) {
3766
+ await fse.remove(buildDirectory);
3767
+ }
3768
+ }
3769
+ async function cleanViteManifests(environmentsOptions, ctx) {
3770
+ let viteManifestPaths = Object.entries(environmentsOptions).map(
3771
+ ([environmentName, options]) => {
3772
+ let outDir = options.build?.outDir;
3773
+ invariant(outDir, `Expected build.outDir for ${environmentName}`);
3774
+ return path6.join(outDir, ".vite/manifest.json");
3775
+ }
3776
+ );
3777
+ await Promise.all(
3778
+ viteManifestPaths.map(async (viteManifestPath) => {
3779
+ let manifestExists = await fse.pathExists(viteManifestPath);
3780
+ if (!manifestExists) return;
3781
+ if (!ctx.viteManifestEnabled) {
3782
+ await fse.remove(viteManifestPath);
3783
+ }
3784
+ let viteDir = path6.dirname(viteManifestPath);
3785
+ let viteDirFiles = await fse.readdir(viteDir);
3786
+ if (viteDirFiles.length === 0) {
3787
+ await fse.remove(viteDir);
3788
+ }
3789
+ })
3790
+ );
3791
+ }
3692
3792
  async function getBuildManifest(ctx) {
3693
3793
  let { routes, serverBundles, appDirectory } = ctx.reactRouterConfig;
3694
3794
  if (!serverBundles) {
@@ -3748,77 +3848,101 @@ async function getBuildManifest(ctx) {
3748
3848
  );
3749
3849
  return buildManifest;
3750
3850
  }
3751
- function mergeBuildOptions(base, overrides) {
3851
+ function mergeEnvironmentOptions(base, ...overrides) {
3752
3852
  let vite2 = getVite();
3753
- return vite2.mergeConfig({ build: base }, { build: overrides }).build;
3853
+ return overrides.reduce(
3854
+ (merged, override) => vite2.mergeConfig(merged, override, false),
3855
+ base
3856
+ );
3754
3857
  }
3755
- async function getEnvironmentOptionsResolvers(ctx, buildManifest) {
3858
+ async function getEnvironmentOptionsResolvers(ctx, buildManifest, viteCommand) {
3756
3859
  let { serverBuildFile, serverModuleFormat } = ctx.reactRouterConfig;
3757
- function getBaseBuildOptions({
3860
+ let packageRoot = path6.dirname(
3861
+ require.resolve("@react-router/dev/package.json")
3862
+ );
3863
+ let { moduleSyncEnabled } = await import(`file:///${path6.join(packageRoot, "module-sync-enabled/index.mjs")}`);
3864
+ let vite2 = getVite();
3865
+ let viteServerConditions = [
3866
+ ...vite2.defaultServerConditions ?? [],
3867
+ ...moduleSyncEnabled ? ["module-sync"] : []
3868
+ ];
3869
+ function getBaseOptions({
3758
3870
  viteUserConfig
3759
3871
  }) {
3760
3872
  return {
3761
- cssMinify: viteUserConfig.build?.cssMinify ?? true,
3762
- manifest: true,
3763
- // The manifest is enabled for all builds to detect SSR-only assets
3764
- rollupOptions: {
3765
- preserveEntrySignatures: "exports-only",
3766
- // Silence Rollup "use client" warnings
3767
- // Adapted from https://github.com/vitejs/vite-plugin-react/pull/144
3768
- onwarn(warning, defaultHandler) {
3769
- if (warning.code === "MODULE_LEVEL_DIRECTIVE" && warning.message.includes("use client")) {
3770
- return;
3771
- }
3772
- let userHandler = viteUserConfig.build?.rollupOptions?.onwarn;
3773
- if (userHandler) {
3774
- userHandler(warning, defaultHandler);
3775
- } else {
3776
- defaultHandler(warning);
3873
+ build: {
3874
+ cssMinify: viteUserConfig.build?.cssMinify ?? true,
3875
+ manifest: true,
3876
+ // The manifest is enabled for all builds to detect SSR-only assets
3877
+ rollupOptions: {
3878
+ preserveEntrySignatures: "exports-only",
3879
+ // Silence Rollup "use client" warnings
3880
+ // Adapted from https://github.com/vitejs/vite-plugin-react/pull/144
3881
+ onwarn(warning, defaultHandler) {
3882
+ if (warning.code === "MODULE_LEVEL_DIRECTIVE" && warning.message.includes("use client")) {
3883
+ return;
3884
+ }
3885
+ let userHandler = viteUserConfig.build?.rollupOptions?.onwarn;
3886
+ if (userHandler) {
3887
+ userHandler(warning, defaultHandler);
3888
+ } else {
3889
+ defaultHandler(warning);
3890
+ }
3777
3891
  }
3778
3892
  }
3779
3893
  }
3780
3894
  };
3781
3895
  }
3782
- function getBaseServerBuildOptions({
3896
+ function getBaseServerOptions({
3783
3897
  viteUserConfig
3784
3898
  }) {
3785
- return mergeBuildOptions(getBaseBuildOptions({ viteUserConfig }), {
3786
- // We move SSR-only assets to client assets. Note that the
3787
- // SSR build can also emit code-split JS files (e.g. by
3788
- // dynamic import) under the same assets directory
3789
- // regardless of "ssrEmitAssets" option, so we also need to
3790
- // keep these JS files have to be kept as-is.
3791
- ssrEmitAssets: true,
3792
- copyPublicDir: false,
3793
- // Assets in the public directory are only used by the client
3794
- rollupOptions: {
3795
- output: {
3796
- entryFileNames: serverBuildFile,
3797
- format: serverModuleFormat
3899
+ let conditions = viteCommand === "build" ? viteServerConditions : ["development", ...viteServerConditions];
3900
+ return mergeEnvironmentOptions(getBaseOptions({ viteUserConfig }), {
3901
+ resolve: {
3902
+ external: ssrExternals,
3903
+ conditions,
3904
+ externalConditions: conditions
3905
+ },
3906
+ build: {
3907
+ // We move SSR-only assets to client assets. Note that the
3908
+ // SSR build can also emit code-split JS files (e.g. by
3909
+ // dynamic import) under the same assets directory
3910
+ // regardless of "ssrEmitAssets" option, so we also need to
3911
+ // keep these JS files have to be kept as-is.
3912
+ ssrEmitAssets: true,
3913
+ copyPublicDir: false,
3914
+ // Assets in the public directory are only used by the client
3915
+ rollupOptions: {
3916
+ output: {
3917
+ entryFileNames: serverBuildFile,
3918
+ format: serverModuleFormat
3919
+ }
3798
3920
  }
3799
3921
  }
3800
3922
  });
3801
3923
  }
3802
3924
  let environmentOptionsResolvers = {
3803
- client: ({ viteUserConfig }) => ({
3804
- build: mergeBuildOptions(getBaseBuildOptions({ viteUserConfig }), {
3925
+ client: ({ viteUserConfig }) => mergeEnvironmentOptions(getBaseOptions({ viteUserConfig }), {
3926
+ build: {
3805
3927
  rollupOptions: {
3806
3928
  input: [
3807
3929
  ctx.entryClientFilePath,
3808
- ...Object.values(ctx.reactRouterConfig.routes).flatMap((route) => {
3809
- let routeFilePath = path6.resolve(
3810
- ctx.reactRouterConfig.appDirectory,
3811
- route.file
3812
- );
3813
- let isRootRoute = route.file === ctx.reactRouterConfig.routes.root.file;
3814
- let code = fse.readFileSync(routeFilePath, "utf-8");
3815
- return [
3816
- `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
3817
- ...ctx.reactRouterConfig.future.unstable_splitRouteModules && !isRootRoute ? routeChunkExportNames.map(
3818
- (exportName) => code.includes(exportName) ? getRouteChunkModuleId(routeFilePath, exportName) : null
3819
- ) : []
3820
- ].filter(isNonNullable);
3821
- })
3930
+ ...Object.values(ctx.reactRouterConfig.routes).flatMap(
3931
+ (route) => {
3932
+ let routeFilePath = path6.resolve(
3933
+ ctx.reactRouterConfig.appDirectory,
3934
+ route.file
3935
+ );
3936
+ let isRootRoute = route.file === ctx.reactRouterConfig.routes.root.file;
3937
+ let code = fse.readFileSync(routeFilePath, "utf-8");
3938
+ return [
3939
+ `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
3940
+ ...ctx.reactRouterConfig.future.unstable_splitRouteModules && !isRootRoute ? routeChunkExportNames.map(
3941
+ (exportName) => code.includes(exportName) ? getRouteChunkModuleId(routeFilePath, exportName) : null
3942
+ ) : []
3943
+ ].filter(isNonNullable);
3944
+ }
3945
+ )
3822
3946
  ],
3823
3947
  output: {
3824
3948
  entryFileNames({ moduleIds }) {
@@ -3830,19 +3954,19 @@ async function getEnvironmentOptionsResolvers(ctx, buildManifest) {
3830
3954
  }
3831
3955
  },
3832
3956
  outDir: getClientBuildDirectory(ctx.reactRouterConfig)
3833
- })
3957
+ }
3834
3958
  })
3835
3959
  };
3836
3960
  if (hasServerBundles(buildManifest)) {
3837
3961
  for (let [serverBundleId, routes] of Object.entries(
3838
3962
  getRoutesByServerBundleId(buildManifest)
3839
3963
  )) {
3840
- environmentOptionsResolvers[`ssr-bundle-${serverBundleId}`] = ({
3841
- viteUserConfig
3842
- }) => ({
3843
- build: mergeBuildOptions(
3844
- getBaseServerBuildOptions({ viteUserConfig }),
3845
- {
3964
+ const serverBundleEnvironmentId = serverBundleId.replaceAll("-", "_");
3965
+ const environmentName = `${SSR_BUNDLE_PREFIX}${serverBundleEnvironmentId}`;
3966
+ environmentOptionsResolvers[environmentName] = ({ viteUserConfig }) => mergeEnvironmentOptions(
3967
+ getBaseServerOptions({ viteUserConfig }),
3968
+ {
3969
+ build: {
3846
3970
  outDir: getServerBuildDirectory(ctx, { serverBundleId }),
3847
3971
  rollupOptions: {
3848
3972
  input: `${virtual.serverBuild.id}?route-ids=${Object.keys(
@@ -3850,30 +3974,43 @@ async function getEnvironmentOptionsResolvers(ctx, buildManifest) {
3850
3974
  ).join(",")}`
3851
3975
  }
3852
3976
  }
3853
- )
3854
- });
3977
+ },
3978
+ // Ensure server bundle environments extend the user's SSR
3979
+ // environment config if it exists
3980
+ viteUserConfig.environments?.ssr ?? {}
3981
+ );
3855
3982
  }
3856
3983
  } else {
3857
- environmentOptionsResolvers.ssr = ({ viteUserConfig }) => ({
3858
- build: mergeBuildOptions(getBaseServerBuildOptions({ viteUserConfig }), {
3984
+ environmentOptionsResolvers.ssr = ({ viteUserConfig }) => mergeEnvironmentOptions(getBaseServerOptions({ viteUserConfig }), {
3985
+ build: {
3859
3986
  outDir: getServerBuildDirectory(ctx),
3860
3987
  rollupOptions: {
3861
3988
  input: viteUserConfig.build?.rollupOptions?.input ?? virtual.serverBuild.id
3862
3989
  }
3863
- })
3990
+ }
3864
3991
  });
3865
3992
  }
3866
3993
  return environmentOptionsResolvers;
3867
3994
  }
3868
- async function getEnvironmentOptions(ctx, environmentName, resolverOptions) {
3869
- let buildManifest = await getBuildManifest(ctx);
3870
- let environmentResolvers = await getEnvironmentOptionsResolvers(
3995
+ function resolveEnvironmentsOptions(environmentResolvers, resolverOptions) {
3996
+ let environmentOptions = {};
3997
+ for (let [environmentName, resolver] of Object.entries(
3998
+ environmentResolvers
3999
+ )) {
4000
+ environmentOptions[environmentName] = resolver(resolverOptions);
4001
+ }
4002
+ return environmentOptions;
4003
+ }
4004
+ async function getEnvironmentsOptions(ctx, buildManifest, viteCommand, resolverOptions) {
4005
+ let environmentOptionsResolvers = await getEnvironmentOptionsResolvers(
3871
4006
  ctx,
3872
- buildManifest
4007
+ buildManifest,
4008
+ viteCommand
4009
+ );
4010
+ return resolveEnvironmentsOptions(
4011
+ environmentOptionsResolvers,
4012
+ resolverOptions
3873
4013
  );
3874
- let resolver = environmentResolvers[environmentName];
3875
- invariant(resolver, `Missing environment resolver for ${environmentName}`);
3876
- return resolver(resolverOptions);
3877
4014
  }
3878
4015
  function isNonNullable(x) {
3879
4016
  return x != null;