Skip to content

[BUG]: v4.0.0 publishes uncompiled .ts sources that are required extensionless — package fails to load under Node's CJS resolver #7995

Description

@slaweet

Description

v4.0.0 ships six uncompiled TypeScript sources inside the published tarball's src/, but the surrounding CommonJS files still require() them without an extension. Node's CJS resolver does not try .ts, so any consumer that resolves plotly.js through Node's own resolution — rather than through a bundler configured to resolve .ts — fails to load the package at all.

The main entry is affected, so this is not limited to subpath imports:

Error: Cannot find module './mod'
Require stack:
- .../plotly.js/src/lib/index.js
- .../plotly.js/build/plotcss.js
- .../plotly.js/src/core.js
- .../plotly.js/lib/core.js
- .../plotly.js/lib/index.js

The .ts files published under src/ in 4.0.0:

  • src/lib/mod.ts
  • src/lib/regex.ts
  • src/lib/clean_number.ts
  • src/lib/sort_object_keys.ts
  • src/lib/custom_country_codes.ts
  • src/plots/map/get_map_fit_bounds.ts
  • (also src/components/modebar/attributes.ts, src/traces/image/attributes.ts)

They are required extensionless from ~20 .js files, e.g.:

Requiring file Line
src/lib/index.js:78 var modModule = require('./mod');
src/lib/index.js:116 lib.sortObjectKeys = require('./sort_object_keys').default;
src/lib/index.js:181 var regexModule = require('./regex');
src/lib/index.js:217 lib.cleanNumber = require('./clean_number').default;
src/lib/coerce.js:13-14 require('./regex').counter / require('./mod').modHalf
src/lib/angles.js:3, src/lib/dates.js:7, src/lib/geometry2d.js:3 require('./mod')
src/lib/geo_location_utils.js:16 require('./custom_country_codes')
src/plots/map/layout_defaults.js:8 require('./get_map_fit_bounds')
src/plots/cartesian/constants.js:3, src/components/grid/index.js:4, src/components/colorscale/attributes.js:4-5, src/plots/geo/layout_attributes.js:8, src/plots/map/constants.js:3, src/traces/scatter3d/attributes.js:14, src/traces/scattergl/attributes.js:10 require('../../lib/regex') / require('../../lib/sort_object_keys')

This appears to be a side effect of #7680 ("Enable TypeScript compatibility within the library"). Nothing in the v4.0.0 release notes indicates that Node/CJS consumers are expected to switch to dist/, so I assume it is unintended rather than a documented breaking change.

Bundlers that resolve .ts by default (Vite/Rollup/rolldown, webpack with .ts in resolve.extensions) are unaffected — production builds succeed. What breaks is anything using Node's resolution: plain require(), and — the way I hit it — test runners that externalize node_modules to Node's CJS loader (Vitest server.deps default, Jest without a .ts-aware moduleFileExtensions). Neither server.deps.inline: [/plotly\.js/] nor deps.optimizer.web.include: ['plotly.js'] works around it on the Vitest side.

Steps to reproduce

$ mkdir plotly-repro && cd plotly-repro && npm init -y && npm i plotly.js@4.0.0

$ node -e "require.resolve('plotly.js/src/lib/mod')"
Error: Cannot find module 'plotly.js/src/lib/mod'

(require.resolve('plotly.js/src/lib/mod') is exactly the resolution src/lib/index.js:78 performs internally. node_modules/plotly.js/src/lib/mod.ts exists on disk.)

Loading the package the normal way fails the same way — this needs a DOM-ish global only to get as far as the failing require:

$ node -e "global.self = global; global.window = global; require('plotly.js')"
Error: Cannot find module './mod'
Require stack:
- .../plotly.js/src/lib/index.js
- .../plotly.js/build/plotcss.js
- .../plotly.js/src/core.js
- .../plotly.js/lib/core.js
- .../plotly.js/lib/index.js

Same for require('plotly.js/lib/core').

For contrast, on plotly.js@3.7.0 the identical command resolves the whole module graph and only fails later, at runtime, on a genuinely missing DOM API (document is not defined) — i.e. module resolution succeeded.

Notes

  • Environment: plotly.js 4.0.0, Node 24.13.1, Linux, pnpm 10, Vite 8.2.1. Also reproduced through Vitest 4.1.10 (Unhandled Rejection: Error: Cannot find module './mod', which makes the test run exit non-zero even when every test passes).
  • Possible fixes, in rough order of preference:
    1. Compile the .ts sources to .js during prepublishOnly so the published src/ is all JavaScript, as in v3.
    2. Keep the .ts sources but add explicit extensions at the require sites (require('./mod.ts') won't help Node either), or emit sibling .js files next to them.
    3. If shipping raw .ts is intentional, an exports map pointing Node consumers at dist/ plus a note in the v4 migration docs would at least make the new contract explicit.
  • Happy to test a patch or open a PR for option 1 if that's the direction you'd like.

Activity

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

Metadata

Metadata

Assignees

Labels

P2considered for next cyclebugsomething brokensize: 3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions