feat(node): filter non-entrypoint Node.js files in /api directory#15873
Conversation
Add static entrypoint detection for Node.js files in `/api/`, mirroring #14493 which did the same for Python. Helper/utility files that don't export a valid handler are no longer treated as API routes, avoiding unnecessary Vercel Function creation. Gated behind `VERCEL_NODE_FILTER_ENTRYPOINTS=1` env var for safe incremental rollout. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: a483f6f The changes in this PR will be included in the next version bump. This PR includes changesets to release 16 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Low Risk — New feature gated behind env var with safe default (returns true on error) — no behavior change without opt-in.
Assessed at a483f6f. |
📦 CLI Tarball ReadyThe Vercel CLI tarball for this PR is now available! Quick TestYou can test this PR's CLI directly by running: npx https://vercel-c5b43k9s6.vercel.sh/tarballs/vercel.tgz --helpUse in vercel.jsonTo use this CLI version in your project builds, add to your {
"build": {
"env": {
"VERCEL_CLI_VERSION": "vercel@https://vercel-c5b43k9s6.vercel.sh/tarballs/vercel.tgz"
}
}
}Python Runtime WheelA Python Workers WheelA |
🧪 Unit Test StrategyComparing: Strategy: Code changed outside of a package - running all unit tests Affected packages - 40 (100%)
Results
This comment is automatically generated based on the affected testing strategy |
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## vercel@50.43.0 ### Minor Changes - Add Deployment Checks support to `deploy --prod`. Shows "Running Checks..." spinner when checks are pending, detects check failures before alias promotion, and displays failed check run details with links to logs. ([#15884](#15884)) - Extend marketplace integration CLI parity: add `vercel integration installations` to list team installations (with optional `--integration` filter and JSON output), align `vercel integration update` argument parsing with other subcommands (parse only tokens after `update`, so the integration slug is the first positional), and ship related help/telemetry updates. ([#15849](#15849)) - Add `vercel project protection` actions for automation protection bypass via PATCH `/v1/projects/.../protection-bypass` (`--protection-bypass`). ([#15862](#15862)) - Add `vercel project protection` actions for customer support code visibility (`--customer-support-code-visibility`). ([#15860](#15860)) - Add `vercel project protection` actions for Git fork protection (`--git-fork-protection`). ([#15861](#15861)) - [services] migrate python workers to Queues V3 API ([#15885](#15885)) - [cli] Add `vercel flags override` subcommand to encrypt and decrypt flag override tokens for the `vercel-flag-overrides` cookie ([#15875](#15875)) ### Patch Changes - Persist CLI telemetry across invocations with bounded-time sessions, stable installation device IDs, and per-invocation identifiers. ([#15872](#15872)) - Update the `vercel metrics` CLI to use the V2 observability metrics API with `--metric`-based schema inspection and querying. ([#15876](#15876)) - Improve `vercel integration add` command to support fallback to the discover API and first-party integrations. ([#15788](#15788)) - refactor(cli): remove FF_AUTO_PROVISION_INSTALL feature flag and dead code from `integration add` ([#15871](#15871)) - Updated dependencies \[[`5e02289f927050a6c1025cc0edb7eda607fd5e73`](5e02289), [`2e15ee828f14de4a849a462429ca03feab161174`](2e15ee8), [`a31c84d1bda56a60da6d7bc6d611b0b18ba3bf57`](a31c84d)]: - @vercel/detect-agent@1.2.2 - @vercel/build-utils@13.14.1 - @vercel/backends@0.0.58 - @vercel/elysia@0.1.61 - @vercel/express@0.1.71 - @vercel/fastify@0.1.64 - @vercel/go@3.4.7 - @vercel/h3@0.1.70 - @vercel/hono@0.2.64 - @vercel/hydrogen@1.3.6 - @vercel/koa@0.1.44 - @vercel/nestjs@0.2.65 - @vercel/next@4.16.5 - @vercel/node@5.7.3 - @vercel/python@6.29.0 - @vercel/redwood@2.4.12 - @vercel/remix-builder@5.7.2 - @vercel/ruby@2.3.2 - @vercel/rust@1.1.0 - @vercel/static-build@2.9.11 ## @vercel/client@17.3.0 ### Minor Changes - Add Deployment Checks support to `deploy --prod`. Shows "Running Checks..." spinner when checks are pending, detects check failures before alias promotion, and displays failed check run details with links to logs. ([#15884](#15884)) ### Patch Changes - Updated dependencies \[[`2e15ee828f14de4a849a462429ca03feab161174`](2e15ee8), [`a31c84d1bda56a60da6d7bc6d611b0b18ba3bf57`](a31c84d)]: - @vercel/build-utils@13.14.1 ## @vercel/backends@0.0.58 ### Patch Changes - Updated dependencies \[[`2e15ee828f14de4a849a462429ca03feab161174`](2e15ee8), [`a31c84d1bda56a60da6d7bc6d611b0b18ba3bf57`](a31c84d)]: - @vercel/build-utils@13.14.1 ## @vercel/build-utils@13.14.1 ### Patch Changes - Restore `finalizeLambda()` to return `zipPath: null` for the default in-memory path, preserving the existing caller-facing result contract while keeping custom ZIP strategies supported. ([#15887](#15887)) - feat(node): filter non-entrypoint Node.js files in `/api` directory ([#15873](#15873)) ## @vercel/cervel@0.0.45 ### Patch Changes - Updated dependencies \[]: - @vercel/backends@0.0.58 ## @vercel/detect-agent@1.2.2 ### Patch Changes - Detect Cursor agent execution when `CURSOR_EXTENSION_HOST_ROLE=agent-exec` is set so tools launched from Cursor still report the `cursor-cli` agent when `CURSOR_AGENT` is not present. ([#15879](#15879)) ## @vercel/elysia@0.1.61 ### Patch Changes - Updated dependencies \[]: - @vercel/node@5.7.3 ## @vercel/express@0.1.71 ### Patch Changes - Updated dependencies \[]: - @vercel/node@5.7.3 - @vercel/cervel@0.0.45 ## @vercel/fastify@0.1.64 ### Patch Changes - Updated dependencies \[]: - @vercel/node@5.7.3 ## @vercel/fs-detectors@5.15.1 ### Patch Changes - feat(node): filter non-entrypoint Node.js files in `/api` directory ([#15873](#15873)) - Updated dependencies \[[`2e15ee828f14de4a849a462429ca03feab161174`](2e15ee8), [`a31c84d1bda56a60da6d7bc6d611b0b18ba3bf57`](a31c84d)]: - @vercel/build-utils@13.14.1 ## @vercel/gatsby-plugin-vercel-builder@2.1.11 ### Patch Changes - Updated dependencies \[[`2e15ee828f14de4a849a462429ca03feab161174`](2e15ee8), [`a31c84d1bda56a60da6d7bc6d611b0b18ba3bf57`](a31c84d)]: - @vercel/build-utils@13.14.1 ## @vercel/h3@0.1.70 ### Patch Changes - Updated dependencies \[]: - @vercel/node@5.7.3 ## @vercel/hono@0.2.64 ### Patch Changes - Updated dependencies \[]: - @vercel/node@5.7.3 ## @vercel/koa@0.1.44 ### Patch Changes - Updated dependencies \[]: - @vercel/node@5.7.3 ## @vercel/nestjs@0.2.65 ### Patch Changes - Updated dependencies \[]: - @vercel/node@5.7.3 ## @vercel/node@5.7.3 ### Patch Changes - Updated dependencies \[[`2e15ee828f14de4a849a462429ca03feab161174`](2e15ee8), [`a31c84d1bda56a60da6d7bc6d611b0b18ba3bf57`](a31c84d)]: - @vercel/build-utils@13.14.1 ## @vercel/static-build@2.9.11 ### Patch Changes - Updated dependencies \[]: - @vercel/gatsby-plugin-vercel-builder@2.1.11 ## @vercel/python-workers@0.0.14 ### Patch Changes - [services] migrate python workers to Queues V3 API ([#15885](#15885)) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary - Fixes a bug introduced in #15873 where `VERCEL_NODE_FILTER_ENTRYPOINTS=1` incorrectly filters out root-level platform files like `middleware.ts` - Root cause: `isNodeEntrypoint()` checks for standard API handler exports (`export default`, HTTP methods, `fetch`, `module.exports`), but root-level platform files use different signatures (e.g., `export function middleware()`). Files that don't match any pattern are silently dropped. - Fix: scope the Node.js entrypoint filter to `fileName.startsWith('api/')` — the filter was only ever intended for helper files inside `api/`, and `maybeGetApiBuilder()` already gates entry to `api/` files + root-level platform files at line 443. This avoids needing to enumerate every platform file pattern (middleware, proxy, etc.) as a negation. ## What was affected Any non-Next.js project with `VERCEL_NODE_FILTER_ENTRYPOINTS=1` that uses root-level `middleware.ts`/`middleware.js`. Next.js projects are unaffected because `@vercel/next` handles middleware via its own manifest-based pipeline, and root-level middleware is already excluded for Next.js at line 439. ## Test plan - [x] `middleware.ts` is preserved when entrypoint filter is active - [x] Monorepo workspace: helpers under `api/` are correctly filtered - [x] Monorepo workspace: middleware is preserved alongside filtered helpers - [x] Existing tests pass (entrypoint filtering, env var gating) - [x] Prettier and lint pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… in strings (#16524) ## Summary Follow-up bug fix for the `VERCEL_NODE_FILTER_ENTRYPOINTS` entrypoint filtering added in #15873. `isNodeEntrypoint()` decides whether a file under `api/` is a Vercel Function by stripping comments and then regex-matching for handler export patterns. The comment stripper was not aware of string, template, or regex literals, so a comment-like sequence **inside a literal** could be misread as a real comment. In particular, a `/*` inside a string (e.g. the `*/*` in an `Accept` header) was treated as the start of a block comment, deleting everything up to the next real `*/`. That frequently swallowed the handler export and silently dropped the function from the build. ### Reproduction ```js module.exports.config = { maxDuration: 60 }; const ACCEPT = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'; export async function GET() { return new Response('Hello from IG!', { headers: { 'Accept': ACCEPT }, }); /* done */ } ``` The `/*` inside `*/*` opens a "block comment" that runs until `/* done */`, deleting the `export async function GET` declaration. The file no longer matches any export pattern and is dropped. ## Fix Identify handler exports with the `es-module-lexer` / `cjs-module-lexer` pair (already dependencies of `@vercel/build-utils` and already used in the build pipeline, e.g. `@vercel/node`'s dev server) instead of regex-based comment stripping. The lexers are token-aware, so `/*`, `//`, and `*/` inside strings, template literals, and regex literals are never mistaken for comments — eliminating this entire class of false negatives. The two handler shapes that are not expressible as named exports — `module.exports = <fn>` and a server that calls `.listen()` — are matched against comment-stripped source. That stripper now consumes string/template/regex literals as whole tokens, so their contents can't be misread either. If the ES module lexer cannot parse a file (e.g. a `.tsx` handler with JSX in its body), detection falls back to the existing safe default (treat as an entrypoint), so a real function is never dropped. ## Test plan - [x] Added regression tests: `*/*` `Accept` headers, `/*` and `*/` inside string / template / regex literals, plus a genuinely block-commented export (still correctly filtered out). - [x] All existing `isNodeEntrypoint()` unit tests pass (42 total). - [x] No behavior change unless `VERCEL_NODE_FILTER_ENTRYPOINTS=1`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
/api/, mirroring [python] only create api builders for.pyfiles that export an app or handler #14493 which did the same for PythonVERCEL_NODE_FILTER_ENTRYPOINTS=1environment variable for safe incremental rolloutHow it works
A new
isNodeEntrypoint()function in@vercel/build-utilsuses regex-based heuristics to detect valid handler export patterns in.js,.mjs,.ts, and.tsxfiles:export default function handler(req, res) {}module.exports = (req, res) => {}export function GET(request) {}export function fetch(request) {}export { handler as default }http.createServer(...)Files that don't match any pattern (e.g., utility functions, type-only files, config exports) are skipped in
maybeGetApiBuilder()and won't produce a Vercel Function.Test plan
isNodeEntrypoint()covering valid entrypoints, non-entrypoints, and edge cases🤖 Generated with Claude Code