Skip to content

fix: add "type": "module" + rename CJS bundle to .cjs (fixes #233) - #237

Merged
rustedgrail merged 1 commit into
dagrejs:masterfrom
Roy-Gal-Git:fix/esm-package-type-233
Aug 3, 2026
Merged

fix: add "type": "module" + rename CJS bundle to .cjs (fixes #233)#237
rustedgrail merged 1 commit into
dagrejs:masterfrom
Roy-Gal-Git:fix/esm-package-type-233

Conversation

@Roy-Gal-Git

Copy link
Copy Markdown

Problem

@dagrejs/graphlib points exports.import at dist/graphlib.esm.js (ESM syntax: export { Graph, alg, json, version }), but the package has no "type": "module". Runtimes that don't auto-detect ESM syntax in .js files — e.g. tsx, or Node without --experimental-detect-module — treat that ESM bundle as CommonJS. The named exports never materialize: import * as graphlib yields only { default }, Graph / alg / json are undefined, and new Graph() throws TypeError. Reported in #233.

Fix — "Option A", done correctly

Add "type": "module" to package.json so Node treats .js files as ESM, which makes the existing dist/graphlib.esm.js resolve its named exports correctly.

The required companion — why naive Option A breaks require(): the CJS bundle is a .js file whose body uses module.exports. Adding "type": "module" makes Node treat every .js file as ESM, so require('@dagrejs/graphlib') would start throwing ERR_REQUIRE_ESM. To keep require() working, the CJS bundle is renamed dist/graphlib.cjs.jsdist/graphlib.cjs: the .cjs extension is pinned to CommonJS regardless of the package type field. This PR does bothtype: module and the .cjs rename — so neither import nor require regresses.

Files

  • package.json — add "type": "module"; main and exports.".".requiredist/graphlib.cjs. (module and exports.".".import stay on dist/graphlib.esm.js, now correctly ESM.)
  • build.ts — CJS esbuild outfiledist/graphlib.cjs (sourcemap follows as graphlib.cjs.map).
  • dist/ — rebuilt: graphlib.cjs + graphlib.cjs.map replace graphlib.cjs.js + .map; the ESM and IIFE bundles are unchanged.
  • MakefileBUILD_FILES updated to the new CJS name (it referenced the old .cjs.js).

IIFE browser bundles are unaffected

dist/graphlib.js and dist/graphlib.min.js stay .js. They are loaded via <script>, are not in the Node exports map, and "type": "module" does not affect browser script-tag loading.

Regression test

test/bundle-test.ts imports ../index (source) via ts-jest, so it cannot catch a published-dist-format bug. This PR adds test/dist-exports.test.ts, which exercises the built dist through real runtimes by importing it at an absolute path — the exact resolution that reads the type field from the dist file's nearest package.json:

  • ESM (tsx): a temp .mts imports dist/graphlib.esm.js and asserts Graph / alg / json / version are the expected types and new Graph() works. Fails on the current code (only default is exported → SyntaxError: does not provide an export named 'Graph'); passes after this change.
  • CJS (node): a temp .cjs require()s dist/graphlib.cjs and asserts new Graph() works. Guards the CJS regression that a naive Option A would introduce. Stays green before and after.

Both pass under the full jest suite.

Validation

  • Reproduced on a clean clone: ESM/tsx case fails, CJS case passes.
  • After the fix: both cases pass; require('@dagrejs/graphlib') under node and a named import under tsx both work via the exports map.
  • npm test (jest, 258 tests) green; npm run lint clean; npm run build green.

As requested in #233 (comment)"If you already have the fix, open a pull request and I'll merge it. I'm a fan of Option A."

Closes #233.

🤖 Generated with Claude Code

)

@dagrejs/graphlib points exports.import at dist/graphlib.esm.js (ESM syntax)
but has no "type": "module", so runtimes that don't auto-detect ESM (tsx,
Node without --experimental-detect-module) treat that .js file as CommonJS.
Named imports then resolve to undefined and `new Graph()` throws TypeError.

Add "type": "module" so the ESM bundle is interpreted as ESM, and rename the
CJS bundle from dist/graphlib.cjs.js to dist/graphlib.cjs — the .cjs extension
is pinned to CommonJS regardless of package type, so `require()` keeps working.
A naive "Option A" (type: module without the .cjs rename) would break
require() with ERR_REQUIRE_ESM, since the CJS bundle is a .js file using
module.exports.

The IIFE browser bundles (dist/graphlib.js, dist/graphlib.min.js) stay .js:
they are loaded via <script>, are not in the Node exports map, and are
unaffected by "type": "module".

Adds test/dist-exports.test.ts, which exercises the BUILT dist via real
runtimes (tsx for ESM, node for CJS) by importing it at an absolute path —
test/bundle-test.ts imports source via ts-jest and cannot catch a dist-format
bug. The ESM case fails on the current code (only `default` is exported) and
passes after this change; the CJS case stays green throughout.

Closes dagrejs#233.

Co-Authored-By: Claude <noreply@anthropic.com>
@rustedgrail
rustedgrail merged commit 30d2a05 into dagrejs:master Aug 3, 2026
1 check passed
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.

Missing "type": "module" causes import * as to yield only { default } in ESM+CJS hybrid runtimes (e.g. tsx)

3 participants