Skip to content

Commit

Permalink
fix(remix-dev/vite): attach CSS from shared chunks to routes (#7952)
Browse files Browse the repository at this point in the history
Co-authored-by: Mark Dalgleish <[email protected]>
hi-ogawa and markdalgleish authored Nov 9, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent a12f636 commit d437e79
Showing 3 changed files with 94 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/itchy-rabbits-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

Attach CSS from shared chunks to routes in Vite build
50 changes: 49 additions & 1 deletion integration/vite-build-test.ts
Original file line number Diff line number Diff line change
@@ -107,7 +107,7 @@ test.describe("Vite build", () => {
import { useEffect, useState } from "react";
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { serverOnly1, serverOnly2 } from "../utils.server";
export const loader = () => {
@@ -138,6 +138,32 @@ test.describe("Vite build", () => {
<MdxComponent />
`,
"app/routes/code-split1.tsx": js`
import { CodeSplitComponent } from "../code-split-component";
export default function CodeSplit1Route() {
return <div id="code-split1"><CodeSplitComponent /></div>;
}
`,
"app/routes/code-split2.tsx": js`
import { CodeSplitComponent } from "../code-split-component";
export default function CodeSplit2Route() {
return <div id="code-split2"><CodeSplitComponent /></div>;
}
`,
"app/code-split-component.tsx": js`
import classes from "./code-split.module.css";
export function CodeSplitComponent() {
return <span className={classes.test}>ok</span>
}
`,
"app/code-split.module.css": js`
.test {
background-color: rgb(255, 170, 0);
}
`,
},
});

@@ -206,4 +232,26 @@ test.describe("Vite build", () => {

expect(pageErrors).toEqual([]);
});

test("supports code-split css", async ({ page }) => {
let pageErrors: unknown[] = [];
page.on("pageerror", (error) => pageErrors.push(error));

let app = new PlaywrightFixture(appFixture, page);
await app.goto("/code-split1");
expect(
await page
.locator("#code-split1 span")
.evaluate((e) => window.getComputedStyle(e).backgroundColor)
).toBe("rgb(255, 170, 0)");

await app.goto("/code-split2");
expect(
await page
.locator("#code-split2 span")
.evaluate((e) => window.getComputedStyle(e).backgroundColor)
).toBe("rgb(255, 170, 0)");

expect(pageErrors).toEqual([]);
});
});
48 changes: 40 additions & 8 deletions packages/remix-dev/vite/plugin.ts
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import {
type Connect,
type Plugin as VitePlugin,
type Manifest as ViteManifest,
type ManifestChunk,
type ResolvedConfig as ResolvedViteConfig,
type ViteDevServer,
type UserConfig as ViteUserConfig,
@@ -120,38 +121,69 @@ const getHash = (source: BinaryLike, maxLength?: number): string => {

const resolveBuildAssetPaths = (
pluginConfig: ResolvedRemixVitePluginConfig,
manifest: ViteManifest,
viteManifest: ViteManifest,
absoluteFilePath: string
): Manifest["entry"] & { css: string[] } => {
let rootRelativeFilePath = path.relative(
pluginConfig.rootDirectory,
absoluteFilePath
);
let manifestKey = normalizePath(rootRelativeFilePath);
let manifestEntry = manifest[manifestKey];
let entryChunk = viteManifest[manifestKey];

if (!manifestEntry) {
let knownManifestKeys = Object.keys(manifest)
if (!entryChunk) {
let knownManifestKeys = Object.keys(viteManifest)
.map((key) => '"' + key + '"')
.join(", ");
throw new Error(
`No manifest entry found for "${manifestKey}". Known manifest keys: ${knownManifestKeys}`
);
}

let chunks = resolveDependantChunks(viteManifest, entryChunk);

return {
module: `${pluginConfig.publicPath}${manifestEntry.file}`,
module: `${pluginConfig.publicPath}${entryChunk.file}`,
imports:
manifestEntry.imports?.map((imported) => {
return `${pluginConfig.publicPath}${manifest[imported].file}`;
dedupe(chunks.flatMap((e) => e.imports ?? [])).map((imported) => {
return `${pluginConfig.publicPath}${viteManifest[imported].file}`;
}) ?? [],
css:
manifestEntry.css?.map((href) => {
dedupe(chunks.flatMap((e) => e.css ?? [])).map((href) => {
return `${pluginConfig.publicPath}${href}`;
}) ?? [],
};
};

function resolveDependantChunks(
viteManifest: ViteManifest,
entryChunk: ManifestChunk
): ManifestChunk[] {
let chunks = new Set<ManifestChunk>();

function walk(chunk: ManifestChunk) {
if (chunks.has(chunk)) {
return;
}

if (chunk.imports) {
for (let importKey of chunk.imports) {
walk(viteManifest[importKey]);
}
}

chunks.add(chunk);
}

walk(entryChunk);

return Array.from(chunks);
}

function dedupe<T>(array: T[]): T[] {
return [...new Set(array)];
}

const writeFileSafe = async (file: string, contents: string): Promise<void> => {
await fs.mkdir(path.dirname(file), { recursive: true });
await fs.writeFile(file, contents);

0 comments on commit d437e79

Please sign in to comment.