Skip to content

Commit a25eed8

Browse files
committed
Get critical CSS in dev from client env
1 parent b571356 commit a25eed8

File tree

3 files changed

+45
-52
lines changed

3 files changed

+45
-52
lines changed

.changeset/proud-needles-destroy.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/dev": patch
3+
---
4+
5+
When extracting critical CSS during development, ensure it's loaded from the client environment to avoid issues with plugins that handle the SSR environment differently

packages/react-router-dev/vite/plugin.ts

Lines changed: 16 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ import type { Cache } from "./cache";
3939
import { generate, parse } from "./babel";
4040
import type { NodeRequestHandler } from "./node-adapter";
4141
import { fromNodeRequest, toNodeRequest } from "./node-adapter";
42-
import { getStylesForPathname, isCssModulesFile } from "./styles";
42+
import {
43+
getCssStringFromViteDevModuleCode,
44+
getStylesForPathname,
45+
isCssModulesFile,
46+
} from "./styles";
4347
import * as VirtualModule from "./virtual-module";
4448
import { resolveFileUrl } from "./resolve-file-url";
4549
import { combineURLs } from "./combine-urls";
@@ -136,10 +140,7 @@ exports are only ever used on the server. Without this optimization we can't
136140
tree-shake any unused custom exports because routes are entry points. */
137141
const BUILD_CLIENT_ROUTE_QUERY_STRING = "?__react-router-build-client-route";
138142

139-
export type EnvironmentName =
140-
| "client"
141-
| SsrEnvironmentName
142-
| CssDevHelperEnvironmentName;
143+
export type EnvironmentName = "client" | SsrEnvironmentName;
143144

144145
const SSR_BUNDLE_PREFIX = "ssrBundle_";
145146
type SsrBundleEnvironmentName = `${typeof SSR_BUNDLE_PREFIX}${string}`;
@@ -151,16 +152,6 @@ function isSsrBundleEnvironmentName(
151152
return name.startsWith(SSR_BUNDLE_PREFIX);
152153
}
153154

154-
// We use a separate environment for loading the critical CSS during
155-
// development. This is because "ssrLoadModule" isn't available if the "ssr"
156-
// environment has been defined by another plugin (e.g.
157-
// vite-plugin-cloudflare) as a custom Vite.DevEnvironment rather than a
158-
// Vite.RunnableDevEnvironment:
159-
// https://vite.dev/guide/api-environment-frameworks.html#runtime-agnostic-ssr
160-
const CSS_DEV_HELPER_ENVIRONMENT_NAME =
161-
"__react_router_css_dev_helper__" as const;
162-
type CssDevHelperEnvironmentName = typeof CSS_DEV_HELPER_ENVIRONMENT_NAME;
163-
164155
type EnvironmentOptions = Pick<Vite.EnvironmentOptions, "build" | "resolve">;
165156

166157
type EnvironmentOptionsResolver = (options: {
@@ -1121,40 +1112,20 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
11211112
return cssModulesManifest[dep.file];
11221113
}
11231114

1124-
const vite = getVite();
1125-
const viteMajor = parseInt(vite.version.split(".")[0], 10);
1126-
1127-
const url =
1128-
viteMajor >= 6
1129-
? // We need the ?inline query in Vite v6 when loading CSS in SSR
1130-
// since it does not expose the default export for CSS in a
1131-
// server environment. This is to align with non-SSR
1132-
// environments. For backwards compatibility with v5 we keep
1133-
// using the URL without ?inline query because the HMR code was
1134-
// relying on the implicit SSR-client module graph relationship.
1135-
injectQuery(dep.url, "inline")
1136-
: dep.url;
1137-
1138-
let cssMod: unknown;
1139-
if (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi) {
1140-
const cssDevHelperEnvironment =
1141-
viteDevServer.environments[CSS_DEV_HELPER_ENVIRONMENT_NAME];
1142-
invariant(cssDevHelperEnvironment, "Missing CSS dev helper environment");
1143-
invariant(vite.isRunnableDevEnvironment(cssDevHelperEnvironment));
1144-
cssMod = await cssDevHelperEnvironment.runner.import(url);
1145-
} else {
1146-
cssMod = await viteDevServer.ssrLoadModule(url);
1147-
}
1148-
1115+
let transformedCssCode = (await viteDevServer.transformRequest(dep.url))
1116+
?.code;
11491117
invariant(
1150-
typeof cssMod === "object" &&
1151-
cssMod !== null &&
1152-
"default" in cssMod &&
1153-
typeof cssMod.default === "string",
1118+
transformedCssCode,
11541119
`Failed to load CSS for ${dep.file ?? dep.url}`
11551120
);
11561121

1157-
return cssMod.default;
1122+
let cssString = getCssStringFromViteDevModuleCode(transformedCssCode);
1123+
invariant(
1124+
typeof cssString === "string",
1125+
`Failed to extract CSS for ${dep.file ?? dep.url}`
1126+
);
1127+
1128+
return cssString;
11581129
};
11591130

11601131
return [
@@ -3591,13 +3562,6 @@ export async function getEnvironmentOptionsResolvers(
35913562
});
35923563
}
35933564

3594-
if (
3595-
ctx.reactRouterConfig.future.unstable_viteEnvironmentApi &&
3596-
viteCommand === "serve"
3597-
) {
3598-
environmentOptionsResolvers[CSS_DEV_HELPER_ENVIRONMENT_NAME] = () => ({});
3599-
}
3600-
36013565
return environmentOptionsResolvers;
36023566
}
36033567

packages/react-router-dev/vite/styles.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { ResolvedReactRouterConfig } from "../config/config";
66
import type { RouteManifest, RouteManifestEntry } from "../config/routes";
77
import type { LoadCssContents } from "./plugin";
88
import { resolveFileUrl } from "./resolve-file-url";
9+
import * as babel from "./babel";
910

1011
// Style collection logic adapted from solid-start: https://github.com/solidjs/solid-start
1112

@@ -248,3 +249,26 @@ export const getStylesForPathname = async ({
248249

249250
return styles;
250251
};
252+
253+
export const getCssStringFromViteDevModuleCode = (
254+
code: string
255+
): string | undefined => {
256+
let cssContent = undefined;
257+
258+
const ast = babel.parse(code, { sourceType: "module" });
259+
babel.traverse(ast, {
260+
VariableDeclaration(path) {
261+
const declaration = path.node.declarations[0];
262+
if (
263+
declaration?.id?.type === "Identifier" &&
264+
declaration.id.name === "__vite__css" &&
265+
declaration.init?.type === "StringLiteral"
266+
) {
267+
cssContent = declaration.init.value;
268+
path.stop();
269+
}
270+
},
271+
});
272+
273+
return cssContent;
274+
};

0 commit comments

Comments
 (0)