Skip to content

feat: add exports map, sideEffects flag, and named country exports for tree-shaking - #166

Open
jsumnerp wants to merge 1 commit into
koblas:mainfrom
jsumnerp:feat/exports-map-tree-shaking
Open

feat: add exports map, sideEffects flag, and named country exports for tree-shaking#166
jsumnerp wants to merge 1 commit into
koblas:mainfrom
jsumnerp:feat/exports-map-tree-shaking

Conversation

@jsumnerp

Copy link
Copy Markdown

Problem

Importing anything from stdnum currently bundles every country's validators (~205KB minified / ~50KB gzip), because the stdnum object eagerly references all of them and the package publishes no other entry points. Consumers who need a handful of validators resort to fragile deep imports into lib/esm/... internals.

This PR finishes what the exports_NOT_YET sketch in package.json started (its trailing-slash form was deprecated and later removed by Node, so it wouldn't work as-is).

Changes

  1. exports map — official subpaths for every validator and country:

    import { validate } from 'stdnum/gb/nino';   // single validator
    import * as GB from 'stdnum/gb';             // whole country
    • The node condition resolves to the CJS build for both require and import in Node. This is deliberate: lib/esm is bundler-only today (the package has no "type": "module" and the ESM output uses extensionless relative imports, which Node's ESM resolver rejects), so pointing Node at it would break. Node behaviour is exactly what main gives today.
    • Bundlers (no node condition when targeting browsers) hit import → the ESM build, same as today's module field.
    • Identity passthroughs keep all existing deep imports working: stdnum/lib/esm/*, stdnum/lib/cjs/* (with or without .js), and stdnum/src/*.
    • Top-level main/module/types fields are unchanged, so old resolvers (including TS node10) are unaffected.
  2. "sideEffects": false — lets bundlers drop unused modules.

  3. Named per-country exports in src/index.tsimport { GB } from 'stdnum' is now tree-shakeable. The stdnum object export is unchanged for full backward compatibility.

  4. README section documenting the tree-shakeable forms.

Verification

  • npm test: 1611/1611 pass (under TZ=UTC; mx/curp has a pre-existing timezone-sensitive assertion that fails on non-UTC machines on main too)
  • npm run lint, npm run prepublishOnly (both tsconfigs), prettier --check: clean
  • Smoke-tested against the packed tarball:
    • Node CJS: require('stdnum'), require('stdnum/gb/nino'), require('stdnum/lib/cjs/gb/nino') ± .js — all work
    • Node ESM: import { stdnum, GB } from 'stdnum' and import { validate } from 'stdnum/gb/nino' — work (resolve to CJS via the node condition)
    • TypeScript: consumer type-checks under moduleResolution: bundler and node16
    • esbuild browser bundles: barrel { stdnum } 205KB (unchanged), named { GB } 8KB, subpath stdnum/gb/nino 5KB, legacy stdnum/lib/esm/gb/nino still resolves

🤖 Generated with Claude Code

…r tree-shaking

Importing the stdnum barrel object bundles every country's validators
(~205KB minified) even when only a few are used, because the eagerly-built
object defeats tree-shaking and the package exposes no other entry points.

- Add an "exports" map (replacing the sketched "exports_NOT_YET"): official
  per-validator subpaths (stdnum/gb/nino) with a "node" condition so Node
  keeps resolving to the CJS build (lib/esm is bundler-only: no type:module
  and extensionless relative imports). Identity passthroughs preserve
  existing deep imports (stdnum/lib/esm/..., stdnum/lib/cjs/..., stdnum/src/...).
- Add "sideEffects": false so bundlers can drop unused modules.
- Add named per-country exports (import { GB } from 'stdnum') as a
  tree-shakeable alternative to the stdnum object, which is unchanged.

Measured with esbuild (minify, browser): barrel 205KB, named country
import 8KB, single-validator subpath 5KB.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

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