Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/orange-lobsters-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@react-router/dev": patch
---

fix(dev): add param to route css so it is not deduped by react
2 changes: 1 addition & 1 deletion .github/workflows/close-no-repro-issues.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
uses: pnpm/[email protected]

- name: ⎔ Setup node
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
# required for --experimental-strip-types
node-version: 22
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deduplicate-lock-file.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
uses: pnpm/action-setup@v4

- name: ⎔ Setup node
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version-file: ".nvmrc"
cache: "pnpm"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
uses: pnpm/action-setup@v4

- name: ⎔ Setup node
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version-file: ".nvmrc"
cache: pnpm
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
uses: pnpm/action-setup@v4

- name: ⎔ Setup node
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version-file: ".nvmrc"
cache: pnpm
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-experimental.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
uses: pnpm/action-setup@v4

- name: ⎔ Setup node
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version-file: ".nvmrc"
cache: "pnpm"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
uses: pnpm/action-setup@v4

- name: ⎔ Setup node
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version-file: ".nvmrc"
cache: "pnpm"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-stage-2-alpha.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
uses: pnpm/action-setup@v4

- name: ⎔ Setup node
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version-file: ".nvmrc"
cache: "pnpm"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
uses: pnpm/action-setup@v4

- name: ⎔ Setup node
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version-file: ".nvmrc"
cache: "pnpm"
Expand Down Expand Up @@ -84,7 +84,7 @@ jobs:
uses: pnpm/action-setup@v4

- name: ⎔ Setup node
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version-file: ".nvmrc"
cache: "pnpm"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/shared-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
uses: pnpm/action-setup@v4

- name: ⎔ Setup node
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version-file: ".nvmrc"
cache: "pnpm"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/shared-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
uses: pnpm/action-setup@v4

- name: ⎔ Setup node ${{ matrix.node }}
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
cache: "pnpm"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
uses: pnpm/action-setup@v4

- name: ⎔ Setup node
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
cache: pnpm
Expand Down
2 changes: 2 additions & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
- johnpangalos
- jonkoops
- joseph0926
- joshuaellis
- jplhomer
- jrakotoharisoa
- jrestall
Expand Down Expand Up @@ -308,6 +309,7 @@
- pawelblaszczyk5
- pcattori
- penx
- peterneave
- petersendidit
- phildl
- phryneas
Expand Down
2 changes: 1 addition & 1 deletion docs/api/data-routers/createHashRouter.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/do
[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react_router.createHashRouter.html)

Create a new [data router](https://api.reactrouter.com/v7/interfaces/react_router.DataRouter.html) that manages the application
path via the URL [`hash`]https://developer.mozilla.org/en-US/docs/Web/API/URL/hash).
path via the URL [`hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash).

## Signature

Expand Down
183 changes: 183 additions & 0 deletions integration/css-lazy-loading-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import { test, expect } from "@playwright/test";

import { PlaywrightFixture } from "./helpers/playwright-fixture.js";
import type { Fixture, AppFixture } from "./helpers/create-fixture.js";
import {
createAppFixture,
createFixture,
css,
js,
} from "./helpers/create-fixture.js";

let fixture: Fixture;
let appFixture: AppFixture;

test.beforeEach(async ({ context }) => {
await context.route(/\.data$/, async (route) => {
await new Promise((resolve) => setTimeout(resolve, 50));
route.continue();
});
});

test.beforeAll(async () => {
fixture = await createFixture({
files: {
"app/routes.ts": js`
import { type RouteConfig, index, route } from "@react-router/dev/routes";

export default [
index("routes/home.tsx"),
route("company", "routes/layout.tsx", [
route("books", "routes/books/route.tsx"),
route("publishers", "routes/publishers/route.tsx"),
]),
] satisfies RouteConfig;
`,

"app/components/Icon.module.css": css`
.icon {
width: 20px;
height: 20px;
background-color: green;
}
`,

"app/components/Icon.tsx": js`
import styles from "./Icon.module.css";

export const Icon = () => {
return <div data-testid="icon" className={styles.icon} />;
}
`,

"app/components/LazyIcon.tsx": js`
import { lazy, Suspense } from "react";

const Icon = lazy(() =>
import("../components/Icon").then((m) => ({ default: m.Icon }))
);

const LazyIcon = ({ show }: { show: boolean }) => {
if (!show) return null;

return (
<Suspense fallback={<div>Loading...</div>}>
<Icon />
</Suspense>
);
};

export { LazyIcon };
`,

"app/routes/home.tsx": js`
import { redirect } from "react-router";

export const loader = () => {
return redirect("/company/books");
};
`,

"app/routes/layout.tsx": js`
import { Link, Outlet } from "react-router";

import { LazyIcon } from "../components/LazyIcon";
import { useState, useEffect } from "react";

export default function Layout() {
const [hydrated, setHydrated] = useState(false);
const [show, setShow] = useState(false);

useEffect(() => {
setShow(true);
},[])

return (
<div style={{ border: "1px solid blue" }}>
<h1>Layout</h1>
<nav>
<Link to="/company/books">Books</Link>
<Link to="/company/publishers">Publishers</Link>
</nav>
<div>
<LazyIcon show={show} />
</div>
<div style={{ border: "1px solid red" }}>
<Outlet />
</div>
</div>
);
}
`,

"app/routes/books/route.tsx": js`
import { Icon } from "../../components/Icon";

export default function BooksRoute() {
return (
<>
<h1>Books</h1>
<div>
<Icon />
</div>
</>
);
}

`,

"app/routes/publishers/route.tsx": js`
export default function PublishersRoute() {
return <h1>Publishers</h1>;
}
`,
},
});

// This creates an interactive app using playwright.
appFixture = await createAppFixture(fixture);
});

test.afterAll(() => {
appFixture.close();
});

test("should preserve the CSS from the lazy loaded component even when it's in the route css manifest", async ({
page,
}) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/");

expect(await page.getByTestId("icon").all()).toHaveLength(1);

// check the head for a link to the css that includes the word `Icon`
const links1 = await page.$$("link");
let found1 = false;
for (const link of links1) {
const href = await link.getAttribute("href");
if (href?.includes("Icon") && href.includes("css")) {
found1 = true;
}
}

expect(found1).toBe(true);

// wait for the loading to be gone before checking the lazy loaded component has resolved
await expect(page.getByText("Loading...")).toHaveCount(0);
expect(await page.getByTestId("icon").all()).toHaveLength(2);

await app.clickLink("/company/publishers");

expect(await page.getByTestId("icon").all()).toHaveLength(1);

const links2 = await page.$$("link");
let found2 = false;
for (const link of links2) {
const href = await link.getAttribute("href");
if (href?.includes("Icon") && href.includes("css")) {
found2 = true;
}
}

expect(found2).toBe(true);
});
4 changes: 2 additions & 2 deletions integration/vite-dot-server-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ test.describe("Vite / route / server-only module referenced by client", () => {

` But other route exports in 'app/routes/_index.tsx' depend on '${specifier}'.`,

" See https://remix.run/docs/en/main/guides/vite#splitting-up-client-and-server-code",
" See https://reactrouter.com/explanation/code-splitting#removal-of-server-code",
].forEach(expect(stderr).toMatch);
});
}
Expand Down Expand Up @@ -206,7 +206,7 @@ test.describe("Vite / non-route / server-only module referenced by client", () =

` '${specifier}' imported by 'app/reexport-server-only.ts'`,

" See https://remix.run/docs/en/main/guides/vite#splitting-up-client-and-server-code",
" See https://reactrouter.com/explanation/code-splitting#removal-of-server-code",
].forEach(expect(stderr).toMatch);
});
}
Expand Down
6 changes: 3 additions & 3 deletions packages/react-router-dev/vite/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ const getReactRouterManifestBuildAssets = (
: null,
chunks
.flatMap((e) => e.css ?? [])
.map((href) => `${ctx.publicPath}${href}`),
.map((href) => `${ctx.publicPath}${href}#route=true`),
]
.flat(1)
.filter(isNonNullable),
Expand Down Expand Up @@ -2084,7 +2084,7 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
"",
` But other route exports in '${importerShort}' depend on '${id}'.`,
"",
" See https://remix.run/docs/en/main/guides/vite#splitting-up-client-and-server-code",
" See https://reactrouter.com/explanation/code-splitting#removal-of-server-code",
"",
].join("\n"),
);
Expand All @@ -2096,7 +2096,7 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
"",
` '${id}' imported by '${importerShort}'`,
"",
" See https://remix.run/docs/en/main/guides/vite#splitting-up-client-and-server-code",
" See https://reactrouter.com/explanation/code-splitting#removal-of-server-code",
"",
].join("\n"),
);
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router-fs-routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { normalizeSlashes } from "./normalizeSlashes";
/**
* Creates route config from the file system using a convention that matches
* [Remix v2's route file
* naming](https://remix.run/docs/en/v2/file-conventions/routes-files), for use
* naming](https://v2.remix.run/docs/file-conventions/routes), for use
* within `routes.ts`.
*/
export async function flatRoutes(
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router-node/sessions/fileStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ interface FileSessionStorageOptions {
* The advantage of using this instead of cookie session storage is that
* files may contain much more data than cookies.
*
* @see https://remix.run/utils/sessions#createfilesessionstorage-node
* @see https://api.reactrouter.com/v7/functions/_react_router_node.createFileSessionStorage
*/
export function createFileSessionStorage<Data = SessionData, FlashData = Data>({
cookie,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router-remix-routes-option-adapter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type { DefineRoutesFunction, DefineRouteFunction };

/**
* Adapts routes defined using [Remix's `routes` config
* option](https://remix.run/docs/en/v2/file-conventions/vite-config#routes) to
* option](https://v2.remix.run/docs/file-conventions/vite-config#routes) to
* React Router's config format, for use within `routes.ts`.
*/
export async function remixRoutesOptionAdapter(
Expand Down
Loading