Skip to content

extract Pages Functions compiler into a standalone package#14785

Open
dario-piotrowicz wants to merge 4 commits into
mainfrom
dario/DEVX-2407/pages-function-package
Open

extract Pages Functions compiler into a standalone package#14785
dario-piotrowicz wants to merge 4 commits into
mainfrom
dario/DEVX-2407/pages-function-package

Conversation

@dario-piotrowicz

@dario-piotrowicz dario-piotrowicz commented Jul 21, 2026

Copy link
Copy Markdown
Member

New package @cloudflare/pages-functions at packages/pages-functions/ that extracts the Pages Functions compilation logic from Wrangler into a standalone, published package with both a programmatic API and CLI.

What it does: Takes a functions/ directory and compiles it into a deployable Cloudflare Worker in a target dist/ directory.

What stays in Wrangler: _worker.js advanced mode, Pages Plugins, Pages deployment/upload, config parsing, watch mode, local dev middleware, telemetry, and authentication.

Public API

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

const result = await buildPagesFunctions({
	functionsDirectory: "./functions",
	outputDirectory: "./dist",
});
// result.entryPointPath — compiled Worker
// result.routesJSON    — generated _routes.json
// result.modules       — collected WASM/text/binary modules

CLI

npx @cloudflare/pages-functions build ./functions --outdir ./dist

Note

Across the PR you can see various comments like:

// Re-export from @cloudflare/pages-functions — this file is kept for
// backward compatibility with existing Wrangler-internal imports and to keep
// the initial migration minimal without changing lots and lots of files
// TODO(dario): after the initial pages-functions migration remove these re-exports

These re-exports just here to try to minimizes the diff size and preserves all existing Wrangler-internal import paths.
I will remove these in a followup PR.


  • 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 refactoring, possibly to document later on

A picture of a cute animal (not mandatory, but encouraged)

@changeset-bot

changeset-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bd5f45c

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

This PR includes changesets to release 1 package
Name Type
@cloudflare/pages-functions Minor

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

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

✅ All changesets look good

@pkg-pr-new

pkg-pr-new Bot commented Jul 21, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/autoconfig@14785

@cloudflare/config

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/config@14785

create-cloudflare

npm i https://pkg.pr.new/cloudflare/workers-sdk/create-cloudflare@14785

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/deploy-helpers@14785

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/kv-asset-handler@14785

miniflare

npm i https://pkg.pr.new/cloudflare/workers-sdk/miniflare@14785

@cloudflare/pages-functions

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/pages-functions@14785

@cloudflare/pages-shared

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/pages-shared@14785

@cloudflare/unenv-preset

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/unenv-preset@14785

@cloudflare/vite-plugin

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/vite-plugin@14785

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/vitest-pool-workers@14785

@cloudflare/workers-auth

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/workers-auth@14785

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/workers-editor-shared@14785

@cloudflare/workers-utils

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/workers-utils@14785

wrangler

npm i https://pkg.pr.new/cloudflare/workers-sdk/wrangler@14785

commit: bd5f45c

ask-bonk[bot]

This comment was marked as resolved.

@ask-bonk

This comment was marked as resolved.

@dario-piotrowicz
dario-piotrowicz force-pushed the dario/DEVX-2407/pages-function-package branch 2 times, most recently from addd36d to 37ffd72 Compare July 21, 2026 16:56
* Carries a `code` from {@link PagesFunctionsErrorCode} so callers can
* distinguish different failure modes without parsing the message string.
*/
export class PagesFunctionsError extends Error {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The package defines PagesFunctionsError (with a code from PagesFunctionsErrorCode, a const object + union type (enum-like)) and PagesFunctionsBuildError (a subclass). All classifiable errors carry an error code so Wrangler can map them to the original FatalError/UserError/FunctionsBuildError classes with the original per-error telemetry labels.

see packages/wrangler/src/pages/buildFunctions.ts

Comment thread packages/wrangler/src/pages/buildFunctions.ts
@dario-piotrowicz
dario-piotrowicz force-pushed the dario/DEVX-2407/pages-function-package branch from 37ffd72 to 6b7720b Compare July 22, 2026 11:41

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This file was moved, with some modifications to packages/pages-functions/src/routing/filepath-routing.ts

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This file comes from packages/wrangler/src/pages/functions/filepath-routing.ts with some small modifications (mainly the introduction of PagesFunctionsErrors)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This file is copied from packages/wrangler/src/pages/paths.ts but with getBasePath() and readableRelative() removed (since they are wrangler specific)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Maybe we should move this code to workers-utils? 🤔

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Pretty much the exact same as packages/wrangler/src/pages/functions/identifiers.ts (which is pretty much being removed in this PR) just with some JSDoc comments added

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Replaced by packages/pages-functions/src/routing/identifiers.ts (this just re-exports the file's exports)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The code comes from packages/wrangler/src/pages/functions/routes-validation.ts with some small changes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The code in this file comes from packages/wrangler/src/pages/functions/routes.ts with some small modifications (in particular the use of PagesFunctionsErrors)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The code from this file was moved to packages/pages-functions/src/routing/routes.ts, the what remains here is a wrapper around writeRoutesModule that converts PagesFunctionsErrors into Wrangler UserErrors

import { existsSync } from "node:fs";
import { mkdir, writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { parseArgs } from "node:util";

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Note: for simplicity this uses parseArgs directly from node, I think this is ok for such a utility, if someone disagrees we can use yars to be more consistent with wrangler

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This file is pretty much unchanged, some of its imports have been changed from local Wrangler files to files from the new pages-functions package.

Some related changes (i.e. wrapping and converting PagesFunctionsError to Wrangler Error classes) have also been applied.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

As shown by github this is a simple file move with some minor changes

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

As shown by github this is a simple file move with some minor changes

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

As shown by github this is a simple file move with some minor changes

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Most of the tests here have been moved to packages/pages-functions/src/__tests__/routes-validation.test.ts only the wrangler-specific tests remain here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

These tests come from packages/wrangler/src/__tests__/pages/routes-validation.test.ts

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

As shown by github this is a simple file move with some minor changes

@dario-piotrowicz
dario-piotrowicz force-pushed the dario/DEVX-2407/pages-function-package branch from 6646a77 to 17b1caa Compare July 22, 2026 14:32
@dario-piotrowicz
dario-piotrowicz marked this pull request as ready for review July 22, 2026 15:27
@workers-devprod
workers-devprod requested review from a team and emily-shen and removed request for a team July 22, 2026 15:28
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/add-pages-functions-package.md: [@cloudflare/wrangler]
  • packages/pages-functions/bin/pages-functions: [@cloudflare/wrangler]
  • packages/pages-functions/package.json: [@cloudflare/wrangler]
  • packages/pages-functions/scripts/deps.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/tests/build.test.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/tests/filepath-routing.test.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/tests/identifiers.test.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/tests/paths.test.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/tests/routes-consolidation.test.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/tests/routes-module.test.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/tests/routes-transformation.test.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/tests/routes-validation.test.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/build.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/cli.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/index.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/paths.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/routing/constants.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/routing/filepath-routing.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/routing/identifiers.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/routing/index.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/routing/routes-consolidation.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/routing/routes-transformation.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/routing/routes-validation.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/routing/routes.ts: [@cloudflare/wrangler]
  • packages/pages-functions/src/templates/pages-template-worker.ts: [@cloudflare/wrangler]
  • packages/pages-functions/tsconfig.json: [@cloudflare/wrangler]
  • packages/pages-functions/tsdown.config.ts: [@cloudflare/wrangler]
  • packages/pages-functions/turbo.json: [@cloudflare/wrangler]
  • packages/pages-functions/vitest.config.mts: [@cloudflare/wrangler]
  • packages/wrangler/package.json: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/pages/functions-build.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/pages/routes-validation.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/pages/buildFunctions.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/pages/constants.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/pages/functions/buildWorker.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/pages/functions/filepath-routing.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/pages/functions/identifiers.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/pages/functions/routes-consolidation.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/pages/functions/routes-transformation.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/pages/functions/routes-validation.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/pages/functions/routes.ts: [@cloudflare/wrangler]
  • pnpm-lock.yaml: [@cloudflare/wrangler]
  • tools/deployments/tests/validate-changesets.test.ts: [@cloudflare/wrangler]

devin-ai-integration[bot]

This comment was marked as resolved.

@dario-piotrowicz
dario-piotrowicz force-pushed the dario/DEVX-2407/pages-function-package branch from 0ae4985 to 35bc26f Compare July 22, 2026 16:56

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 new potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment on lines +75 to +91
// Write _routes.json if requested or by default alongside the output
const routesOutputPath =
values["routes-output"] || resolve(outdir, "_routes.json");
await mkdir(dirname(routesOutputPath), { recursive: true });
await writeFile(
routesOutputPath,
JSON.stringify(result.routesJSON, null, 2)
);

console.log(`Compiled Worker successfully to ${outdir}/index.js`);
console.log(
` Routes: ${result.filepathRoutingConfig.routes.length} route(s) found`
);
if (result.modules.length > 0) {
console.log(` Modules: ${result.modules.length} additional module(s)`);
}
console.log(` Routes JSON written to ${routesOutputPath}`);

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.

🟡 Compiled worker from the CLI omits imported WASM/text/binary files, so it cannot be deployed

The command-line build writes only the worker script and its routes file (writeFile at packages/pages-functions/src/cli.ts:79) but never writes the collected extra module files, so any functions project that imports a .wasm, .txt, .html, .sql, or .bin file produces an output folder whose worker references files that do not exist.

Impact: A user who compiles functions that import WASM/text/binary assets gets a broken output that fails to deploy, even though the CLI prints "Compiled Worker successfully".

Why the module files are missing from the output

During bundling, createModuleCollectorPlugin (packages/pages-functions/src/build.ts:326-360) intercepts these imports, hashes their contents, records them in collectedModules, and returns external: true (packages/pages-functions/src/build.ts:312-315). This leaves a bare import like from "12345678-module.wasm" in the emitted index.js (asserted by the test at packages/pages-functions/src/__tests__/build.test.ts:199-200) but does NOT write the file to disk — the content lives only in the returned result.modules[].content array (packages/pages-functions/src/build.ts:307-311).

The programmatic API expects its caller to persist result.modules, but the CLI (packages/pages-functions/src/cli.ts:65-91) only writes _routes.json and logs result.modules.length; it never writes each module's content next to index.js. Consequently the emitted worker imports module files that are absent from outdir, and a subsequent wrangler deploy cannot resolve them.

Prompt for agents
In packages/pages-functions/src/cli.ts, after calling buildPagesFunctions, the CLI writes _routes.json but never persists the collected non-JS modules. The build step (packages/pages-functions/src/build.ts) marks imported .wasm/.txt/.html/.sql/.bin files as external and returns their bytes in result.modules (each { name, content, type }), leaving bare imports (e.g. from "<hash>-module.wasm") in the emitted index.js. Because those files are never written to outdir, the compiled worker references modules that do not exist on disk, so wrangler deploy will fail. Fix by iterating result.modules in the CLI and writing each module's content to outdir using its name (join(outdir, module.name)) before printing the success message. Ensure directories are created as needed.
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