Commit Graph

3 Commits

Author SHA1 Message Date
James Anderson 044ccf7c4c fix: allow server-only imports from 'use server' modules (#1707)
PR #1697 added a client-environment guard that throws when a module
imports "server-only", to match Next.js rejecting server-only from
client-reachable code. But Server Actions modules ('use server') live in
the server/action layer, not the client layer, and Next.js explicitly
allows them to `import 'server-only'` (see
test/e2e/app-dir/actions/app/client/actions.js). Because the client
bundle holds references used to invoke the actions, the action module
enters the client graph and the guard threw, failing the entire client
build.

This false positive broke 136 deploy-suite tests across the actions and
app-basepath suites. Exempt modules with a top-level "use server"
directive so genuine client modules are still rejected.

Adds a regression test that builds an App Router 'use server' module
importing server-only, alongside the existing negative test proving
client modules are still rejected.
2026-06-01 08:47:03 +00:00
Steve Faulkner 534487a5bf fix: reject server-only imports from client-reachable modules (#1697)
Match Next.js handling for the server-only marker: imports are allowed
from App Router Server Components and middleware/server targets, but
client-reachable modules must fail instead of silently resolving the
empty shim.

Add an AST-based client-environment transform guard so comments or
string literals do not false-positive. The existing middleware test
continues to prove server-only remains allowed from middleware graphs,
and the new Pages Router fixture mirrors Next.js's compiler error test
for server-only imported from a Pages client graph.
2026-05-30 14:46:47 -05:00
James Anderson ab37cc661e fix(middleware): allow server-only imports (#1376)
Middleware runs server-side, so importing `server-only` is semantically
correct. However, vinext bundles middleware into the SSR environment
via `virtual:vinext-server-entry`, and @vitejs/plugin-rsc's
`rsc:validate-imports` treats any non-RSC environment as a "client"
build, rejecting the bare specifier with:

  'server-only' cannot be imported in client build ('ssr' environment):
   imported by middleware.js
     imported by virtual:vinext-server-entry
       imported by virtual:vinext-app-ssr-entry

Next.js solves this with webpack `issuerLayer` rules: middleware sits
in the `neutralTarget` layer where `server-only` aliases to a no-op
while `client-only` still errors
(packages/next/src/build/webpack-config.ts).

Vite has no per-layer aliasing within an environment, so we mirror the
behavior with import-chain taint tracking: seed a Set with the
middleware entry path, propagate the taint along the resolveId graph,
and short-circuit `server-only` to the existing no-op shim when the
importer is tainted. The plugin uses `enforce: "pre"` so its resolveId
beats plugin-rsc's `order: "pre"` validate-imports filter on the bare
specifier.

Closes #1344
2026-05-21 11:41:53 +01:00