Skip to content

Emit real ESM output and drop dead pnpm build config#55

Merged
keiloktql merged 1 commit into
mainfrom
chore/exports-map-and-supply-chain-config
Jul 21, 2026
Merged

Emit real ESM output and drop dead pnpm build config#55
keiloktql merged 1 commit into
mainfrom
chore/exports-map-and-supply-chain-config

Conversation

@keiloktql

@keiloktql keiloktql commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

The two follow-ups I flagged in #54 and deliberately kept out of it.

The builder-bob warnings turned out to be pointing at a real defect, not a style preference. With esm disabled, bob emits ESM syntax into lib/module/*.js but writes no type marker. Node resolves the import condition to those files and then parses them as CommonJS. Node 24 papers over this with module syntax detection, which is why nothing looked broken — but with detection disabled, which is Node 18/20 behaviour, importing the package fails outright:

# old layout, node --no-experimental-detect-module
ERR_REQUIRE_CYCLE_MODULE | Cannot require() ES Module .../lib/module/index.js

# this branch, same flags
ERR_MODULE_NOT_FOUND | Cannot find package 'react-native'   <- expected, peer dep absent in the probe

Key changes

Real ESM output

  • esm: true on the commonjs and module targets. It is a per-target option, not top-level — bob's schema rejects it at the root, and the typescript target derives it by checking whether any other target sets it, so it needs no option of its own.
  • bob now writes {"type":"commonjs"} / {"type":"module"} markers into each output dir, rewrites relative imports to carry .js extensions, and splits declarations into lib/typescript/{commonjs,module}.
  • exports['.'] nests types under each condition instead of alongside them, and the top-level types follows the declarations to lib/typescript/commonjs/index.d.ts.
  • Build is now warning-free (was 3).

Dead pnpm config

  • onlyBuiltDependencies has zero references in pnpm 11.1.1 — it, ignoredBuiltDependencies and neverBuiltDependencies were all replaced by the allowBuilds map. The empty array was doing nothing, under a comment that overstated it. Removed; the comment now describes what pnpm 11 actually enforces.
  • Worth stating plainly: this weakens nothing. pnpm 11 blocks every unlisted lifecycle script by default and hard-fails the install until each gets an explicit verdict, which is stricter than the empty allowlist was. I checked the rest of the file too — blockExoticSubdeps, minimumReleaseAge, trustPolicy, trustPolicyExclude and overrides are all still live settings.

Verification

Against a packed tarball extracted into a scratch consumer, so this exercises the published artifact rather than the working tree:

Check Result
require.resolve lib/commonjs/index.js
import.meta.resolve lib/module/index.js
ESM parse, detection disabled resolves (old layout throws ERR_REQUIRE_CYCLE_MODULE)
tsc moduleResolution: node16 lib/typescript/commonjs/index.d.ts
tsc moduleResolution: bundler lib/typescript/module/index.d.ts
pnpm typecheck / lint / test clean / clean / 212 passed
pnpm build 0 warnings, all three targets emitted
pnpm install --frozen-lockfile clean, lockfile unchanged by the workspace edit

CI's test -d lib/{commonjs,module,typescript} assertions still hold.

Note for reviewers

This changes the published package layout, so it is the one part of these changes that consumers can observe. Metro is unaffected — React Native resolves through the react-native field to src. The declaration files move from lib/typescript/index.d.ts to lib/typescript/{commonjs,module}/index.d.ts; anything deep-importing that path would break, though it is not a documented entry point. Worth landing before the next release rather than after.

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Two follow-ups flagged but deliberately left out of the dependency bump.

builder-bob 0.41 started warning that the esm option is disabled while
exports['.'].import and exports['.'].require are both set, and that
exports['.'].types should not be set alongside them. The warnings were
pointing at a real defect, not a style preference: with esm disabled, bob
emits ESM syntax into lib/module/*.js but writes no `type` marker, so
Node resolves the import condition and then parses those files as
CommonJS. Node 24 papers over it with module syntax detection; with
detection off — which is Node 18/20 behaviour — importing the package
fails outright with ERR_REQUIRE_CYCLE_MODULE.

Enabling esm on the commonjs and module targets makes bob write the
`type` markers, rewrite relative imports to carry .js extensions, and
split declarations into lib/typescript/{commonjs,module}. The typescript
target picks esm up from the other targets, so it needs no option of its
own. The exports map now nests types under each condition, as bob asked.

Verified against a packed tarball resolved from a scratch consumer:
require() lands on lib/commonjs, import() on lib/module, and with syntax
detection disabled the old layout throws while this one resolves. tsc
picks up the commonjs declarations under moduleResolution node16 and the
module ones under bundler.

Separately, onlyBuiltDependencies has zero references in pnpm 11.1.1 —
it, ignoredBuiltDependencies and neverBuiltDependencies were all replaced
by the allowBuilds map. The empty array was dead config with a comment
that overstated it. Removed, and the comment now describes what pnpm 11
actually enforces. Every other setting in the file is still live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@keiloktql keiloktql self-assigned this Jul 21, 2026
@keiloktql
keiloktql merged commit 6940eeb into main Jul 21, 2026
10 checks passed
@keiloktql
keiloktql deleted the chore/exports-map-and-supply-chain-config branch July 21, 2026 03:11
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