Skip to content

build(type-plus): drop the redundant esm/package.json marker - #666

Open
unional wants to merge 1 commit into
mainfrom
chore/drop-esm-module-marker
Open

build(type-plus): drop the redundant esm/package.json marker#666
unional wants to merge 1 commit into
mainfrom
chore/drop-esm-module-marker

Conversation

@unional

@unional unional commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

What

  • build:esm no longer runs emit-module-type-marker.mjs, so esm/package.json is no longer emitted.
  • The cjs/package.json marker now writes {"type":"commonjs","sideEffects":false}.
  • scripts/emit-module-type-marker.mjs drops its two-arg form (it only ever has one job now) and its header comment is rewritten to explain why cjs/ needs a marker and esm/ must not have one.

Why

The package root declares "type": "module", so everything under esm/ is already ESM by inheritance. The esm/package.json marker shipped in 8.0.0-beta.10 (e58fac6) "for symmetry" and restated what was already true.

It was not free. packages/type-plus/package.json carries "sideEffects": false. Bundlers read that field from the nearest package.json describing a module, and esm/package.json became that file for every ESM entry point — while carrying no sideEffects field of its own. So bundlers fell back to assuming side effects on exactly the build consumers tree-shake.

Tree-shaking evidence

Built the package, symlinked it into a throwaway project, and bundled this consumer:

import { reduceWhile } from 'type-plus'
console.log(reduceWhile([1, 2, 3], (a, b) => a + b, 0, () => true))

webpack 5.110.3, mode: 'production', optimization.minimize: false, ESM output:

bundle modules kept
with esm/package.json 32,779 bytes 21
without it 707 bytes 2

With the marker present webpack pulled in esm/index.js and dragged along nominal/{index,brand,flavor,constants,nominal_match}.js, testing/{stub,test_type}.js, assertion/assert_type.js and the rest of the barrel. Without it, only array/reduce_while.js survives. The regression is real.

One correction to the premise: rollup 4 with @rollup/plugin-node-resolve produced byte-identical output (522 bytes) in both configurations. Its own static side-effect analysis eliminated the unused modules regardless of the sideEffects hint, so this is a webpack-class regression, not a universal one. The changeset says so rather than overclaiming.

require() regression check

The cjs marker is load-bearing — without it Node reads cjs/index.js as ESM and its require() calls resolve against the caller. Verified against the actual clean build output, not in theory:

$ node req.cjs
require ok: function function 54 exports
$ node -e "import('type-plus').then(...)"
import ok: function 54 exports

Both entry points resolve, and re-running the webpack test after the change reproduces the 707-byte bundle.

Verification

  • pnpm -w turbo build lint test — 4/4 green
  • pnpm --filter type-plus test:type — 5.4, 5.5, 5.6, 6.0 and latest green

patch changeset included (the package is in beta pre mode on the open 8.0.0 train).

🤖 Generated with Claude Code

https://claude.ai/code/session_019f2P5bUGxqe1niopUctKqP

The package root already declares `"type": "module"`, so `esm/*.js` are ESM
by inheritance and the marker restated what was already true. It also became
the nearest `package.json` for every ESM entry point, shadowing the root
`"sideEffects": false` and costing webpack consumers tree-shaking.

The `cjs/package.json` marker stays — it is what keeps Node from reading
`cjs/index.js` as ESM — and now carries `"sideEffects": false` itself, since
it shadows the root field for CJS consumers the same way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f2P5bUGxqe1niopUctKqP
@changeset-bot

changeset-bot Bot commented Sep 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e4a3bc0

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

This PR includes changesets to release 1 package
Name Type
type-plus Patch

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

@codecov

codecov Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (5413511) to head (e4a3bc0).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #666   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           43        43           
  Lines          232       232           
  Branches        51        51           
=========================================
  Hits           232       232           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

1 participant