Skip to content

fix(build): apply default server externals#2257

Merged
james-elicx merged 3 commits into
mainfrom
codex/fix-twoslash-default-server-externals
Jun 23, 2026
Merged

fix(build): apply default server externals#2257
james-elicx merged 3 commits into
mainfrom
codex/fix-twoslash-default-server-externals

Conversation

@james-elicx

Copy link
Copy Markdown
Member

Summary

  • apply Next.js-compatible default server externals to Node server builds
  • keep TypeScript external so its standard-library declarations remain resolvable at runtime
  • respect transpilePackages and reject conflicting explicit external/transpile configuration

Next.js parity

Targets both twoslash HTTP 500 failures in the latest deploy-suite report. Bundling TypeScript detached it from its lib.*.d.ts files; externalizing the Next.js default package set restores twoslash hover evaluation.

This independently reproduced implementation overlaps open PR #2115.

Validation

  • production vinext-owned twoslash fixture: default and ESNext hover results
  • 363 targeted assertions across focused runs
  • scoped vp check
  • package build
  • four independent review rounds; final review clean

Full suites are deferred to CI.

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@pkg-pr-new

pkg-pr-new Bot commented Jun 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2257
npm i https://pkg.pr.new/vinext@2257

commit: 9c122f4

@github-actions

Copy link
Copy Markdown
Contributor
Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
workers-cache preview production
web preview production

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: fix(build): apply default server externals

I verified this against the authoritative Next.js source and the package list matches exactly. Solid PR. A few notes below — none are blocking, one is worth a second look.

Verified correct against Next.js

  • Package list parity. The 79-entry DEFAULT_SERVER_EXTERNAL_PACKAGES in server-external-packages.ts is byte-for-byte identical to next/dist/lib/server-external-packages.jsonc in the installed Next.js (16.2.7). I diffed both sets programmatically — zero entries differ. (Minor: the header comment says "Next.js 16.2.6" while the repo ships 16.2.7; the lists are identical, but you may want to bump the comment to avoid confusion.)

  • Conflict semantics match Next.js. next/dist/build/webpack-config.js:667-678 throws The packages specified in the 'transpilePackages' conflict with the 'serverExternalPackages': ... only for the intersection of user-explicit serverExternalPackages and transpilePackages. mergeServerExternalPackages reproduces this exactly (userPackages.filter(name => transpiled.has(name))), including the error message string.

  • Merge/override semantics match Next.js. webpack-config.js:681 computes EXTERNAL_PACKAGES.concat(serverExternalPackages).filter(pkg => !transpilePackages.includes(pkg)). The PR filters transpiled only out of the defaults, relying on the conflict check to guarantee no user package overlaps transpiled — functionally equivalent, and typescript staying external is the intended fix for the twoslash 500s.

Worth a second look

  • Client dep-optimizer exclude now spans all 79 packages (index.ts:2434-2438). Previously this optimizeDeps.exclude only received the (usually empty) user serverExternalPackages; it now receives the full default set. The comment there only motivates server-only conditional-export packages (file-type-style). The default list includes packages that can legitimately be imported from client components in some apps (e.g. shiki, autoprefixer, postcss). Excluding them from the client optimizer means they get resolved lazily rather than prebundled, which at worst causes a late re-optimization + full reload on first client use rather than a hard failure. This matches Next.js's optOutBundlingPackages treatment, so it's defensible — but it's a wider behavioral change than the PR description implies, and the inline comment no longer fully explains why the entire default set is excluded. Consider a one-line comment update noting these are the Next.js default server externals.

Minor / non-blocking

  • index.ts:2043 now merges config.ssr.external into the top-level viteConfig.ssr.external array alongside nextServerExternal. This is a genuine improvement (user ssr.external was previously dropped on the Pages-Router/single-build path), and duplicates with the default list are harmless since Vite dedupes external matching. Just calling it out as an in-scope behavioral change not mentioned in the summary.

Testing

  • Ran tests/server-external-packages.test.ts + tests/next-config.test.ts (205 passed) and a scoped tsc --noEmit on the package (clean).
  • The new app-router-production-build.test.ts case is a strong end-to-end check — it builds, asserts typescript is in vinext-externals.json, and exercises the TS language service through a real production route handler for both default and ESNext lib configs. Good port of Next.js's test/e2e/twoslash.

Overall: behavior matches Next.js, tests are well-targeted, and the fix is sound. The only thing I'd action is tightening the comments around the widened client-optimizer exclusion and the 16.2.6/16.2.7 version note.

@ask-bonk

ask-bonk Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2257.

Summary

I reviewed the PR by verifying it against the authoritative Next.js source (next/dist/build/webpack-config.js and server-external-packages.jsonc) in node_modules. The implementation is correct and matches Next.js behavior precisely:

Verified correct:

  • The 79-package default list is byte-for-byte identical to the installed Next.js 16.2.7 list (diffed programmatically — zero differences).
  • The transpilePackages conflict check and error message match Next.js exactly.
  • The merge/override semantics (defaults + user externals, minus transpiled) are functionally equivalent to Next.js's optOutBundlingPackages computation.
  • Ran the new unit tests (205 passed) and a scoped type-check (clean).

Flagged for a second look (non-blocking):

  • The client dep-optimizer exclude now spans all 79 default packages (previously just the usually-empty user list). This aligns with Next.js's optOutBundlingPackages treatment but is a wider behavioral change than the PR description implies; the inline comment no longer fully explains it.
  • Header comment says "16.2.6" while the repo ships 16.2.7 (lists identical, just a stale note).
  • index.ts:2043 now also merges user config.ssr.external on the single-build path — a genuine improvement, but an in-scope change not mentioned in the summary.

No correctness issues found. The fix is sound and well-tested, including a strong end-to-end production-build test that exercises the TypeScript language service through a real route handler.

github run

@james-elicx james-elicx marked this pull request as ready for review June 22, 2026 00:54
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared f8d5595 against base 92884ee using alternating same-runner rounds. Next.js was unchanged and skipped.

1 improved · 0 regressed · 5 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 117.2 KB 117.2 KB ⚫ -0.0%
Client entry size (gzip) vinext 112.6 KB 112.6 KB ⚫ 0.0%
Dev server cold start vinext 2.38 s 2.37 s ⚫ -0.5%
Production build time vinext 3.02 s 2.91 s 🟢 -3.6%
RSC entry closure size (gzip) vinext 92.8 KB 92.8 KB ⚫ +0.0%
Server bundle size (gzip) vinext 156.6 KB 156.6 KB ⚫ +0.0%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

@james-elicx james-elicx enabled auto-merge (squash) June 23, 2026 10:19
@james-elicx james-elicx merged commit 2cbd2a0 into main Jun 23, 2026
58 checks passed
@james-elicx james-elicx deleted the codex/fix-twoslash-default-server-externals branch June 23, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant