Skip to content

Update invalid exports messaging #13049

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 18, 2025
Merged
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/weak-mayflies-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@react-router/dev": patch
---

[REMOVE] Update invalid route export messages
11 changes: 11 additions & 0 deletions docs/how-to/pre-rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,14 @@ sirv-cli build/client --single index.html
# If you pre-rendered the `/` route
sirv-cli build/client --single __spa-fallback.html
```

### Invalid Exports

When pre-rendering with `ssr:false`, React Router will error at build time if you have invalid exports to help prevent some mistakes that can be easily overlooked.

- `headers`/`action` functions are prohibited in all routes because there will be no runtime server on which to run them
- When using `ssr:false` without a `prerender` config (SPA Mode), a `loader` is permitted on the root route only
- When using `ssr:false` with a `prerender` config, a `loader` is permitted on any route matched by a `prerender` path
- If you are using a `loader` on a pre-rendered route that has child routes, you will need to make sure the parent `loaderData` can be determined at run-time properly by either:
- Pre-rendering all child routes so that the parent `loader` can be called at build-time for each child route path and rendered into a `.data` file, or
- Use a `clientLoader` on the parent that can be called at run-time for non-pre-rendered child paths
22 changes: 9 additions & 13 deletions integration/vite-prerender-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -805,9 +805,9 @@ test.describe("Prerendering", () => {
let result = build({ cwd });
let stderr = result.stderr.toString("utf8");
expect(stderr).toMatch(
"Prerender: 2 invalid route export(s) in `routes/a` when prerendering " +
"with `ssr:false`: headers, action. " +
"See https://reactrouter.com/how-to/pre-rendering for more information."
"Prerender: 2 invalid route export(s) in `routes/a` when pre-rendering " +
"with `ssr:false`: `headers`, `action`. " +
"See https://reactrouter.com/how-to/pre-rendering#invalid-exports for more information."
);
});

Expand All @@ -833,10 +833,9 @@ test.describe("Prerendering", () => {
let result = build({ cwd });
let stderr = result.stderr.toString("utf8");
expect(stderr).toMatch(
"Prerender: 1 invalid route export in `routes/b` when using `ssr:false` " +
"with `prerender` because the route is never prerendered so the loader " +
"will never be called. See https://reactrouter.com/how-to/pre-rendering " +
"for more information."
"Prerender: 1 invalid route export in `routes/b` when pre-rendering " +
"with `ssr:false`: `loader`. " +
"See https://reactrouter.com/how-to/pre-rendering#invalid-exports for more information."
);
});

Expand All @@ -860,12 +859,9 @@ test.describe("Prerendering", () => {
let result = build({ cwd });
let stderr = result.stderr.toString("utf8");
expect(stderr).toMatch(
"Prerender: 1 invalid route export in `routes/a` when using `ssr:false` " +
"with `prerender`: `loader`. This is because the route has non-pre-rendered " +
"children paths and does not have it's own `clientLoader` to be used " +
"when those paths are hydrated as a SPA. You can fix this error by adding " +
"a `clientLoader` to the route or by pre-rendering the children paths. " +
"See https://reactrouter.com/how-to/pre-rendering for more information."
"Prerender: 1 invalid route export in `routes/a` when pre-rendering " +
"with `ssr:false`: `loader`. " +
"See https://reactrouter.com/how-to/pre-rendering#invalid-exports for more information."
);
});

Expand Down
27 changes: 11 additions & 16 deletions packages/react-router-dev/vite/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2516,7 +2516,7 @@ async function prerenderResourceRoute(
throw new Error(
`Prerender (resource): Received a ${response.status} status code from ` +
`\`entry.server.tsx\` while prerendering the \`${normalizedPath}\` ` +
`path.\n${content.toString('utf8')}`
`path.\n${content.toString("utf8")}`
);
}

Expand Down Expand Up @@ -2662,35 +2662,30 @@ async function validateSsrFalsePrerenderExports(
if (exports.includes("action")) invalidApis.push("action");
if (invalidApis.length > 0) {
errors.push(
`Prerender: ${invalidApis.length} invalid route export(s) in ` +
`\`${route.id}\` when prerendering with \`ssr:false\`: ` +
`${invalidApis.join(", ")}. ` +
"See https://reactrouter.com/how-to/pre-rendering for more information."
`Prerender: ${invalidApis.length} invalid route export(s) in \`${route.id}\` ` +
"when pre-rendering with `ssr:false`: " +
`${invalidApis.map((a) => `\`${a}\``).join(", ")}. ` +
"See https://reactrouter.com/how-to/pre-rendering#invalid-exports for more information."
);
}

// `loader` is only valid if the route is matched by a `prerender` path
if (!prerenderedRoutes.has(routeId)) {
if (exports.includes("loader")) {
errors.push(
`Prerender: 1 invalid route export in \`${route.id}\` when ` +
"using `ssr:false` with `prerender` because the route is never " +
"prerendered so the loader will never be called. " +
"See https://reactrouter.com/how-to/pre-rendering for more information."
`Prerender: 1 invalid route export in \`${route.id}\` ` +
"when pre-rendering with `ssr:false`: `loader`. " +
"See https://reactrouter.com/how-to/pre-rendering#invalid-exports for more information."
);
}

let parentRoute = route.parentId ? manifest.routes[route.parentId] : null;
while (parentRoute && parentRoute.id !== "root") {
if (parentRoute.hasLoader && !parentRoute.hasClientLoader) {
errors.push(
`Prerender: 1 invalid route export in \`${parentRoute.id}\` when using ` +
"`ssr:false` with `prerender`: `loader`. This is because the route " +
"has non-pre-rendered children paths and does not have it's own " +
"`clientLoader` to be used when those paths are hydrated as a SPA. " +
"You can fix this error by adding a `clientLoader` to the route or " +
"by pre-rendering the children paths. " +
"See https://reactrouter.com/how-to/pre-rendering for more information."
`Prerender: 1 invalid route export in \`${parentRoute.id}\` when ` +
"pre-rendering with `ssr:false`: `loader`. " +
"See https://reactrouter.com/how-to/pre-rendering#invalid-exports for more information."
);
}
parentRoute =
Expand Down
Loading