$npx -y skills add vercel/next.js --skill dce-edgeDCE-safe require() patterns and edge runtime constraints. Use when writing conditional require() calls, guarding Node-only imports (node:stream etc.), or editing define-env-plugin.ts / app-render / stream-utils for edge builds. Covers if/else branching for webpack DCE, TypeScript
| 1 | # DCE + Edge |
| 2 | |
| 3 | Use this skill when changing conditional `require()` paths, Node-only imports, or edge/runtime branching. |
| 4 | |
| 5 | ## DCE-Safe `require()` Pattern |
| 6 | |
| 7 | Webpack only DCEs a `require()` when it sits inside the dead branch of an `if/else` whose condition DefinePlugin can evaluate at compile time. |
| 8 | |
| 9 | ```ts |
| 10 | // CORRECT - webpack can eliminate the dead branch |
| 11 | if (process.env.__NEXT_USE_NODE_STREAMS) { |
| 12 | require('node:stream') |
| 13 | } else { |
| 14 | // web path |
| 15 | } |
| 16 | ``` |
| 17 | |
| 18 | What does NOT work: |
| 19 | |
| 20 | - **Early-return/throw guards**: webpack doesn't do control-flow analysis for throws/returns, so the `require()` is still traced. |
| 21 | - **Bare `if` without `else`**: works for inline `node:*` specifiers but NOT for `require('./some-module')` that pulls a new file into the module graph. |
| 22 | |
| 23 | Always test edge changes with `pnpm test-start-webpack` on `test/e2e/app-dir/app/standalone.test.ts` (has edge routes), not with `NEXT_SKIP_ISOLATE=1` which skips the full webpack compilation. |
| 24 | |
| 25 | ## TypeScript + DCE Interaction |
| 26 | |
| 27 | Use `if/else` (not two independent `if` blocks) when assigning a variable conditionally on `process.env.X`. TypeScript cannot prove exhaustiveness across `if (flag) { x = a }; if (!flag) { x = b }` and will error with "variable used before being assigned". The `if/else` pattern satisfies both TypeScript (definite assignment) and webpack DCE. |
| 28 | |
| 29 | ## Compile-Time Switcher Pattern |
| 30 | |
| 31 | Platform-specific code (node vs web) can use a single `.ts` switcher module that conditionally `require()`s either `.node.ts` or `.web.ts` into a typed variable, then re-exports the shared runtime API as named exports. Keep the branch as `if/else` so DefinePlugin can dead-code-eliminate the unused `require()`. Keep shared types canonical in `.node.ts`, with `.web.ts` importing them via `import type` and the switcher re-exporting types as needed. Examples: `stream-ops.ts` and `debug-channel-server.ts`. |
| 32 | |
| 33 | ## `NEXT_RUNTIME` Is Not a Feature Flag |
| 34 | |
| 35 | In user-project webpack server compilers, `process.env.NEXT_RUNTIME` is inlined to `'nodejs'`. Guarding Node-only `require('node:*')` paths with `NEXT_RUNTIME === 'nodejs'` does **not** prune anything. For feature-gated codepaths, guard on the real feature define (e.g. `process.env.__NEXT_USE_NODE_STREAMS`). |
| 36 | |
| 37 | ## Edge Runtime Constraints |
| 38 | |
| 39 | Edge routes do NOT use pre-compiled runtime bundles. They are compiled by the user's webpack/Turbopack, so `define-env.ts` controls DCE. Feature flags that gate `node:*` imports must be forced to `false` for edge builds in `define-env.ts` (`isEdgeServer ? false : flagValue`), otherwise webpack will try to resolve `node:stream` etc. and fail. |
| 40 | |
| 41 | ## `app-page.ts` Template Gotchas |
| 42 | |
| 43 | - `app-page.ts` is a build template compiled by the user's bundler. Any `require()` in this file is traced by webpack/turbopack at `next build` time. You cannot require internal modules with relative paths because they won't be resolvable from the user's project. Instead, export new helpers from `entry-base.ts` and access them via `entryBase.*` in the template. |
| 44 | - Template helpers should stay out of `RenderResult`. If `app-page.ts` needs a Node-stream-only utility, prefer a small dedicated helper module in `server/stream-utils/` (with DCE-safe `if/else` + `require()`). |
| 45 | |
| 46 | ## Verification |
| 47 | |
| 48 | - Validate edge bundling regressions with `pnpm test-start-webpack test/e2e/app-dir/app/standalone.test.ts` |
| 49 | - For module-resolution/build-graph fixes, verify without `NEXT_SKIP_ISOLATE=1` |
| 50 | |
| 51 | ## Related Skills |
| 52 | |
| 53 | - `$flags` - flag wiring (config/schema/define-env/runtime env) |
| 54 | - `$react-vendoring` - entry-base boundaries and vendored React |
| 55 | - `$runtime-debug` - reproduction and verification workflow |