Description
Version: void / @void/react 1.2.1 (also 1.1.12); main at a6de782.
Repro
pnpm create void(React, static output:output: 'static'invoid.config.ts).pages/index.island.tsx:import { Frame } from './_ui/frame'; export default function Home() { return <Frame><h1>Hello</h1></Frame>; }
pages/_ui/frame.tsx, a shared server-only component that is not a page or an island:export const SECRET_COPY = 'server-only text'; export function Frame({ children }: { children: React.ReactNode }) { return <main data-x={SECRET_COPY}>{children}</main>; }
vp build, thenls dist/client/assetsandgrep -l 'server-only text' dist/client/assets/*.js.
Expected: an island-only app emits no pages-client bundle, and frame.tsx (not a route, because the route scanner skips _ segments) never reaches dist/client.
Actual: pages-client-*.js plus a chunk for every page module and every import (frame-*.js) are emitted to dist/client/assets, and the server-only strings are in them. In our app that is 18 files and 351 KB instead of 5 files and 33 KB, and 42 strings that were meant to stay server-side.
Cause: packages/react/src/plugin-pages.ts hasNonIslandPages() skips only basenames that start with _:
if (name.startsWith('_') || name.startsWith('layout.')) continue;entry.name for pages/_ui/frame.tsx is frame.tsx. The pages scanner (scan.ts) skips any segment that starts with _ (except _layouts), so the two disagree. The Solid plugin has the same check.
Suggested fix: test every path segment relative to dir, as the scanner does:
const rel = relative(dir, join(entry.parentPath, name));
if (rel.split(sep).some((s) => s.startsWith('_')) || name.startsWith('layout.')) continue;This is adjacent to #559, which adds the .server. exclusion to the same line.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity
fix(pages): exclude private helpers from the client entry decision (v…