Skip to content

Conversation

@ascorbic
Copy link
Contributor

@ascorbic ascorbic commented Jan 21, 2026

Fixes https://jira.cfdata.org/browse/DEVX-2407

Extracts the Pages functions-to-worker compilation logic from wrangler into a standalone @cloudflare/pages-functions package.

This is part of the Autoconfig Pages work (RM-26054). The goal is to enable converting Pages projects with a functions directory into Workers projects that can be deployed with the standard wrangler deploy flow.

What this package does

Takes a functions directory:

functions/
├── _middleware.ts
├── index.ts
└── api/
    └── [id].ts

And compiles it to a worker entrypoint that can be bundled and deployed like any regular worker:

import { compileFunctions } from "@cloudflare/pages-functions";

const result = await compileFunctions("./functions");
// result.code - generated worker entrypoint
// result.routes - parsed route configuration
// result.routesJson - _routes.json for Pages deployment

What's extracted

  • Filepath routing (scanning functions dir, extracting exports)
  • Route configuration and sorting
  • Routes transformation (_routes.json generation)
  • Routes consolidation and optimization
  • Runtime code (route matching, middleware chain execution)
  • Code generation (produces a bundleable worker entrypoint)

What stays in wrangler

  • Bundling (esbuild integration via bundleWorker)
  • The assets: import plugin
  • CLI commands (pages functions build, etc.)
  • Deployment logic

Test coverage

  • 71 unit tests covering filepath routing, codegen, route transformation
  • 9 runtime tests using vitest-pool-workers that test actual request handling in the workers runtime (route matching, middleware execution, 404 handling)

Next steps

A follow-up PR will wire this into wrangler's buildFunctions() to replace the duplicated code.


  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: Internal package, README included

Open with Devin

Extracts the Pages functions-to-worker compilation logic from wrangler
into a standalone package. This is the first step towards Autoconfig
Pages (DEVX-2407).
@changeset-bot
Copy link

changeset-bot bot commented Jan 21, 2026

🦋 Changeset detected

Latest commit: 22dfdee

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

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

- Resolve path-to-regexp path at codegen time using require.resolve()
- Wrangler/esbuild bundles it when building the final worker
- Remove esbuild dependency (no longer needed for bundling)
- Return 404 when no route matches and fallback binding unavailable
- Add vitest-pool-workers runtime tests (9 tests covering route
  matching, middleware execution, and 404 handling)
@ascorbic ascorbic force-pushed the pages-functions-package branch from 5b356a3 to 99322a1 Compare January 21, 2026 15:04
Manual test fixture for the @cloudflare/pages-functions package.
Demonstrates compiling a functions directory and running with wrangler.
@ascorbic ascorbic force-pushed the pages-functions-package branch from 99322a1 to e437fac Compare January 21, 2026 15:13
@ascorbic ascorbic changed the title [pages-functions] Extract Pages functions build logic into its own package Extract Pages functions build logic into its own package Jan 21, 2026
@ascorbic ascorbic marked this pull request as ready for review January 21, 2026 16:12
@ascorbic ascorbic requested a review from a team as a code owner January 21, 2026 16:12
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 4 additional flags.

Open in Devin Review

"extends": "@cloudflare/workers-tsconfig",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
Copy link
Member

Choose a reason for hiding this comment

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

is there any reason why this is NodeNext?
(this seems to me quite inconsistent with the monorepo where Bundler is generally used)

Copy link
Contributor Author

@ascorbic ascorbic Jan 23, 2026

Choose a reason for hiding this comment

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

I tried switching to bundler, but had to change back so the CLI would work without bundling.

import {
consolidateRoutes,
MAX_FUNCTIONS_ROUTES_RULES,
} from "./routes-consolidation.js";
Copy link
Member

Choose a reason for hiding this comment

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

the .js imports are, I believe, very inconsistent with the monorepo (because NodeNext is used as the moduleResolution) is this intentional?

- Change compileFunctions() to accept project directory instead of functions directory
- Add functionsDir option (default: 'functions')
- Add pages-functions CLI for command-line compilation
- Update README with CLI documentation and accurate API reference
- Restructure test fixtures to match project-based API
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View issue and 8 additional flags in Devin Review.

Open in Devin Review

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View issue and 11 additional flags in Devin Review.

Open in Devin Review

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View issue and 12 additional flags in Devin Review.

Open in Devin Review

"undefined",
];

const reservedKeywordRegex = new RegExp(`^${RESERVED_KEYWORDS.join("|")}$`);
Copy link
Contributor

Choose a reason for hiding this comment

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

🟡 Incorrect regex construction causes valid identifiers to be rejected

The reservedKeywordRegex in identifiers.ts is incorrectly constructed using ^${RESERVED_KEYWORDS.join("|")}$ which produces the pattern ^do|if|in|for|...|undefined$. Due to regex operator precedence, the ^ anchor only applies to the first alternative ("do") and the $ anchor only applies to the last alternative ("undefined"). The middle alternatives match anywhere in the string.

This causes identifiers like "doSomething", "forLoop", or "infinity" to incorrectly match as reserved keywords:

const reservedKeywordRegex = new RegExp(`^${RESERVED_KEYWORDS.join("|"))}$`);
reservedKeywordRegex.test("doSomething"); // true (should be false)
reservedKeywordRegex.test("forLoop");     // true (should be false)

The fix should wrap the alternation in a group: `^(${RESERVED_KEYWORDS.join("|")})

While this bug is copied from the existing wrangler code (packages/wrangler/src/pages/functions/identifiers.ts:55), it's being added as new code in this package. In practice, the bug has limited impact because the code only processes exports matching onRequest* pattern, which don't start with reserved keywords. However, any future use of isValidIdentifier() with identifiers starting with short reserved keywords would fail incorrectly.

Recommendation: Change line 59 to: const reservedKeywordRegex = new RegExp(\^(${RESERVED_KEYWORDS.join("|")})$`);` to properly group the alternation.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

2 participants