Skip to content

feat(node): filter non-entrypoint Node.js files in /api directory#15873

Merged
smaeda-ks merged 2 commits into
mainfrom
shohei/node-builder-api
Apr 9, 2026
Merged

feat(node): filter non-entrypoint Node.js files in /api directory#15873
smaeda-ks merged 2 commits into
mainfrom
shohei/node-builder-api

Conversation

@smaeda-ks

Copy link
Copy Markdown
Member

Summary

How it works

A new isNodeEntrypoint() function in @vercel/build-utils uses regex-based heuristics to detect valid handler export patterns in .js, .mjs, .ts, and .tsx files:

Pattern Example
ESM default export export default function handler(req, res) {}
CJS module.exports module.exports = (req, res) => {}
Named HTTP method exports export function GET(request) {}
Fetch export export function fetch(request) {}
Re-exports export { handler as default }
Server handler 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

  • 34 unit tests for isNodeEntrypoint() covering valid entrypoints, non-entrypoints, and edge cases
  • 3 integration tests in fs-detectors verifying builder creation/skipping with actual files on disk
  • Verified all 116+ existing handler files across Node builder and CLI test fixtures match at least one detection pattern
  • Verified feature is inactive without env var (zero behavior change by default)

🤖 Generated with Claude Code

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-bot

changeset-bot Bot commented Apr 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a483f6f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
@vercel/build-utils Patch
@vercel/fs-detectors Patch
@vercel/backends Patch
vercel Patch
@vercel/client Patch
@vercel/elysia Patch
@vercel/express Patch
@vercel/fastify Patch
@vercel/gatsby-plugin-vercel-builder Patch
@vercel/h3 Patch
@vercel/hono Patch
@vercel/koa Patch
@vercel/nestjs Patch
@vercel/node Patch
@vercel/static-build Patch
@vercel/cervel Patch

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

@vercel

vercel Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Low Risk — New feature gated behind env var with safe default (returns true on error) — no behavior change without opt-in.

  • node-entrypoint.ts: new isNodeEntrypoint() function with regex-based handler detection
  • detect-builders.ts: conditionally skip non-entrypoint Node.js files when VERCEL_NODE_FILTER_ENTRYPOINTS=1
  • unit.node-entrypoint.test.ts: 34 unit tests covering valid/invalid entrypoint patterns

Assessed at a483f6f.

@github-actions

github-actions Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

📦 CLI Tarball Ready

The Vercel CLI tarball for this PR is now available!

Quick Test

You can test this PR's CLI directly by running:

npx https://vercel-c5b43k9s6.vercel.sh/tarballs/vercel.tgz --help

Use in vercel.json

To use this CLI version in your project builds, add to your vercel.json:

{
  "build": {
    "env": {
      "VERCEL_CLI_VERSION": "vercel@https://vercel-c5b43k9s6.vercel.sh/tarballs/vercel.tgz"
    }
  }
}

Python Runtime Wheel

A vercel-runtime wheel was also built for this PR.
To use in your Python project builds, also set this environment variable:

VERCEL_RUNTIME_PYTHON="vercel-runtime @ https://vercel-c5b43k9s6.vercel.sh/tarballs/vercel_runtime-0.13.0.dev1775625704+00c991c-py3-none-any.whl"

Python Workers Wheel

A vercel-workers wheel was also built for this PR.
To use in your Python project builds, also set this environment variable:

VERCEL_WORKERS_PYTHON="vercel-workers @ https://vercel-c5b43k9s6.vercel.sh/tarballs/vercel_workers-0.1.0.dev1775625704+00c991c-py3-none-any.whl"

@github-actions

github-actions Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

🧪 Unit Test Strategy

Comparing: 6c1dcb5a483f6f (view diff)

Strategy: Code changed outside of a package - running all unit tests

⚠️ All unit tests will run because global code changes could impact all packages.

Affected packages - 40 (100%)
  1. @vercel-internals/get-package-json
  2. @vercel/backends
  3. @vercel/build-utils
  4. @vercel/cervel
  5. @vercel/cli-auth
  6. @vercel/client
  7. @vercel/config
  8. @vercel/detect-agent
  9. @vercel/edge
  10. @vercel/elysia
  11. @vercel/error-utils
  12. @vercel/express
  13. @vercel/fastify
  14. @vercel/firewall
  15. @vercel/frameworks
  16. @vercel/fs-detectors
  17. @vercel/functions
  18. @vercel/gatsby-plugin-vercel-builder
  19. @vercel/go
  20. @vercel/h3
  21. @vercel/hono
  22. @vercel/hydrogen
  23. @vercel/koa
  24. @vercel/nestjs
  25. @vercel/next
  26. @vercel/node
  27. @vercel/oidc
  28. @vercel/oidc-aws-credentials-provider
  29. @vercel/python
  30. @vercel/python-analysis
  31. @vercel/redwood
  32. @vercel/related-projects
  33. @vercel/remix-builder
  34. @vercel/routing-utils
  35. @vercel/ruby
  36. @vercel/rust
  37. @vercel/static-build
  38. @vercel/static-config
  39. examples
  40. vercel

Results

  • Unit tests: All affected packages will run unit tests
  • E2E tests: Running in parallel via E2E Tests workflow
  • Type checks: All affected packages will run type checks

This comment is automatically generated based on the affected testing strategy

@smaeda-ks smaeda-ks marked this pull request as ready for review April 8, 2026 05:36
@smaeda-ks smaeda-ks requested review from a team and agadzik as code owners April 8, 2026 05:36
@smaeda-ks smaeda-ks marked this pull request as draft April 8, 2026 10:41
@smaeda-ks smaeda-ks marked this pull request as ready for review April 8, 2026 11:32
Comment thread packages/build-utils/src/node-entrypoint.ts
Comment thread packages/build-utils/src/node-entrypoint.ts
@smaeda-ks smaeda-ks merged commit a31c84d into main Apr 9, 2026
202 checks passed
@smaeda-ks smaeda-ks deleted the shohei/node-builder-api branch April 9, 2026 14:27
healeycodes pushed a commit that referenced this pull request Apr 10, 2026
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>
smaeda-ks added a commit that referenced this pull request Apr 14, 2026
## 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>
smaeda-ks added a commit that referenced this pull request Jun 4, 2026
… 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>
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.

2 participants