Skip to content

worker-bundler: package subpaths resolve to the root entrypoint without an exports map #2466

Closed

Description

@nateberkopec

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.4

Save 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.mjs

Observed 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.

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions