Repository navigation
worker-bundler: package subpaths resolve to the root entrypoint without an exports map #2466
Description
This was written by an agent. Model: gpt-6.1-sol.
Summary
@cloudflare/worker-bundler 0.2.4 resolves a package subpath to the package's root entrypoint when the package has a main field but no exports map.
For example, fixture/internal resolves to fixture/index.js, even though fixture/internal/index.js exists. This can break real dependencies such as ajv-formats, which imports both ajv and ajv/dist/compile/codegen and expects different exports from them.
Expected and actual behavior
Expected rewritten import:
import { internalValue } from "/node_modules/fixture/internal/index.js";Actual rewritten import:
import { internalValue } from "/node_modules/fixture/index.js";The public API reproduction below needs no Worker account, credentials, network calls at runtime, or other application. It uses bundle: false, which also runs under Node. I ran it with Node 26.10.0 and the published 0.2.4 package.
Runnable reproduction
Prerequisite: Node 22 or later and npm.
In an empty directory:
npm init -y
npm install --ignore-scripts @cloudflare/worker-bundler@0.2.4Save this as repro.mjs:
import assert from "node:assert/strict";
import { createWorker } from "@cloudflare/worker-bundler";
const result = await createWorker({
bundle: false,
entryPoint: "index.js",
files: {
"index.js": 'import { internalValue } from "fixture/internal"; export default internalValue;',
"node_modules/fixture/package.json": JSON.stringify({ name: "fixture", main: "index.js" }),
"node_modules/fixture/index.js": 'export const rootValue = "root";',
"node_modules/fixture/internal/index.js": 'export const internalValue = "internal";'
}
});
const output = result.modules[result.mainModule];
console.log(output);
assert.match(output, /fixture\/internal\/index\.js/, "deep import must resolve to the internal module, not the root entrypoint");
console.log("PASS: deep import resolves to the requested module");Run:
node repro.mjsObserved output, followed by exit code 1:
[worker-bundler] createWorker(): This package is experimental and its API may change without notice.
import { internalValue } from "/node_modules/fixture/index.js"; export default internalValue;
AssertionError [ERR_ASSERTION]: deep import must resolve to the internal module, not the root entrypoint
Cause and tested fix
In packages/worker-bundler/src/resolver.ts, resolvePackage() tries the package's legacy main/module entrypoint before trying a requested subpath. The root-entrypoint fallback should apply only to root imports:
-if (legacyEntry && typeof legacyEntry === "string") {
+if (!subpath && legacyEntry && typeof legacyEntry === "string") {I tested this on the 0.2.4 release source. Before the change, five of six new regression cases fail. After the change, all six pass: root imports, directory subpaths, file subpaths, scoped subpaths, missing subpaths, and import rewriting.
This is distinct from the Workers-safe JSON Schema validator changes in #1902: it fails at module resolution, before a validator runs. I found no existing subpath-resolution report in the issue and PR searches.
Activity
Release 0.2.5-nate.2 with a machine-readable source patch
Track the pinned Executor worker-bundler workaround (#814)
nateberkopec commentedon Oct 5, 2026
thx bro ❤️