Skip to content

Commit cc18af5

Browse files
fix(remix-dev/vite): bundle CSS from client entry (#8143)
Co-authored-by: Pedro Cattori <[email protected]>
1 parent 8292199 commit cc18af5

File tree

6 files changed

+232
-71
lines changed

6 files changed

+232
-71
lines changed

integration/vite-css-build-test.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,22 @@ test.describe("Vite CSS build", () => {
5050
],
5151
});
5252
`,
53+
"app/entry.client.tsx": js`
54+
import "./entry.client.css";
55+
56+
import { RemixBrowser } from "@remix-run/react";
57+
import { startTransition, StrictMode } from "react";
58+
import { hydrateRoot } from "react-dom/client";
59+
60+
startTransition(() => {
61+
hydrateRoot(
62+
document,
63+
<StrictMode>
64+
<RemixBrowser />
65+
</StrictMode>
66+
);
67+
});
68+
`,
5369
"app/root.tsx": js`
5470
import { Links, Meta, Outlet, Scripts } from "@remix-run/react";
5571
@@ -70,6 +86,12 @@ test.describe("Vite CSS build", () => {
7086
);
7187
}
7288
`,
89+
"app/entry.client.css": css`
90+
.client-entry {
91+
background: pink;
92+
padding: ${TEST_PADDING_VALUE};
93+
}
94+
`,
7395
"app/routes/_index/styles-bundled.css": css`
7496
.index_bundled {
7597
background: papayawhip;
@@ -118,12 +140,14 @@ test.describe("Vite CSS build", () => {
118140
export default function IndexRoute() {
119141
return (
120142
<div id="index">
121-
<div data-css-modules className={cssModulesStyles.index}>
122-
<div data-css-linked className="index_linked">
123-
<div data-css-bundled className="index_bundled">
124-
<div data-css-vanilla-global className="index_vanilla_global">
125-
<div data-css-vanilla-local className={stylesVanillaLocal.index}>
126-
<h2>CSS test</h2>
143+
<div data-client-entry className="client-entry">
144+
<div data-css-modules className={cssModulesStyles.index}>
145+
<div data-css-linked className="index_linked">
146+
<div data-css-bundled className="index_bundled">
147+
<div data-css-vanilla-global className="index_vanilla_global">
148+
<div data-css-vanilla-local className={stylesVanillaLocal.index}>
149+
<h2>CSS test</h2>
150+
</div>
127151
</div>
128152
</div>
129153
</div>
@@ -146,6 +170,10 @@ test.describe("Vite CSS build", () => {
146170
test("renders styles", async ({ page }) => {
147171
let app = new PlaywrightFixture(appFixture, page);
148172
await app.goto("/");
173+
await expect(page.locator("#index [data-client-entry]")).toHaveCSS(
174+
"padding",
175+
TEST_PADDING_VALUE
176+
);
149177
await expect(page.locator("#index [data-css-modules]")).toHaveCSS(
150178
"padding",
151179
TEST_PADDING_VALUE

integration/vite-css-dev-test.ts

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,22 @@ test.describe("Vite CSS dev", () => {
4343
],
4444
});
4545
`,
46+
"app/entry.client.tsx": js`
47+
import "./entry.client.css";
48+
49+
import { RemixBrowser } from "@remix-run/react";
50+
import { startTransition, StrictMode } from "react";
51+
import { hydrateRoot } from "react-dom/client";
52+
53+
startTransition(() => {
54+
hydrateRoot(
55+
document,
56+
<StrictMode>
57+
<RemixBrowser />
58+
</StrictMode>
59+
);
60+
});
61+
`,
4662
"app/root.tsx": js`
4763
import { Links, Meta, Outlet, Scripts, LiveReload } from "@remix-run/react";
4864
@@ -64,6 +80,12 @@ test.describe("Vite CSS dev", () => {
6480
);
6581
}
6682
`,
83+
"app/entry.client.css": css`
84+
.client-entry {
85+
background: pink;
86+
padding: ${TEST_PADDING_VALUE};
87+
}
88+
`,
6789
"app/styles-bundled.css": css`
6890
.index_bundled {
6991
background: papayawhip;
@@ -113,12 +135,14 @@ test.describe("Vite CSS dev", () => {
113135
return (
114136
<div id="index">
115137
<input />
116-
<div data-css-modules className={cssModulesStyles.index}>
117-
<div data-css-linked className="index_linked">
118-
<div data-css-bundled className="index_bundled">
119-
<div data-css-vanilla-global className="index_vanilla_global">
120-
<div data-css-vanilla-local className={stylesVanillaLocal.index}>
121-
<h2>CSS test</h2>
138+
<div data-client-entry className="client-entry">
139+
<div data-css-modules className={cssModulesStyles.index}>
140+
<div data-css-linked className="index_linked">
141+
<div data-css-bundled className="index_bundled">
142+
<div data-css-vanilla-global className="index_vanilla_global">
143+
<div data-css-vanilla-local className={stylesVanillaLocal.index}>
144+
<h2>CSS test</h2>
145+
</div>
122146
</div>
123147
</div>
124148
</div>
@@ -169,6 +193,10 @@ test.describe("Vite CSS dev", () => {
169193
await page.goto(`http://localhost:${devPort}/`, {
170194
waitUntil: "networkidle",
171195
});
196+
await expect(page.locator("#index [data-client-entry]")).toHaveCSS(
197+
"padding",
198+
TEST_PADDING_VALUE
199+
);
172200
await expect(page.locator("#index [data-css-modules]")).toHaveCSS(
173201
"padding",
174202
TEST_PADDING_VALUE
@@ -205,6 +233,10 @@ test.describe("Vite CSS dev", () => {
205233
// Ensure no errors on page load
206234
expect(pageErrors).toEqual([]);
207235

236+
await expect(page.locator("#index [data-client-entry]")).toHaveCSS(
237+
"padding",
238+
TEST_PADDING_VALUE
239+
);
208240
await expect(page.locator("#index [data-css-modules]")).toHaveCSS(
209241
"padding",
210242
TEST_PADDING_VALUE
@@ -300,8 +332,28 @@ test.describe("Vite CSS dev", () => {
300332
UPDATED_TEST_PADDING_VALUE
301333
);
302334

335+
// Ensure CSS updates were handled by HMR
303336
await expect(input).toHaveValue("stateful");
304337

338+
// The following change triggers a full page reload, so we check it after all the checks for HMR state preservation
339+
let clientEntryCssContents = await fs.readFile(
340+
path.join(projectDir, "app/entry.client.css"),
341+
"utf8"
342+
);
343+
await fs.writeFile(
344+
path.join(projectDir, "app/entry.client.css"),
345+
clientEntryCssContents.replace(
346+
TEST_PADDING_VALUE,
347+
UPDATED_TEST_PADDING_VALUE
348+
),
349+
"utf8"
350+
);
351+
352+
await expect(page.locator("#index [data-client-entry]")).toHaveCSS(
353+
"padding",
354+
UPDATED_TEST_PADDING_VALUE
355+
);
356+
305357
expect(pageErrors).toEqual([]);
306358
});
307359
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// This file is used to avoid CJS deprecation warnings in Vite 5 since
2+
// @remix-run/dev currently compiles to CJS. By using this interface, we only
3+
// ever access the Vite package via a dynamic import which forces the ESM build.
4+
// "importViteAsync" is expected be called up-front in the first async plugin
5+
// hook, which then unlocks "importViteEsmSync" for use anywhere in the plugin
6+
// and its utils. This file won't be needed when this package is ESM only.
7+
8+
import invariant from "../invariant";
9+
10+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
11+
type Vite = typeof import("vite");
12+
let vite: Vite | undefined;
13+
14+
export async function preloadViteEsm(): Promise<void> {
15+
vite = await import("vite");
16+
}
17+
18+
export function importViteEsmSync(): Vite {
19+
invariant(vite, "importViteEsmSync() called before preloadViteEsm()");
20+
return vite;
21+
}

0 commit comments

Comments
 (0)