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:
- Compile the
.ts sources to .js during prepublishOnly so the published src/ is all JavaScript, as in v3.
- 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.
- 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.
Description
v4.0.0 ships six uncompiled TypeScript sources inside the published tarball's
src/, but the surrounding CommonJS files stillrequire()them without an extension. Node's CJS resolver does not try.ts, so any consumer that resolvesplotly.jsthrough Node's own resolution — rather than through a bundler configured to resolve.ts— fails to load the package at all.The
mainentry is affected, so this is not limited to subpath imports:The
.tsfiles published undersrc/in 4.0.0:src/lib/mod.tssrc/lib/regex.tssrc/lib/clean_number.tssrc/lib/sort_object_keys.tssrc/lib/custom_country_codes.tssrc/plots/map/get_map_fit_bounds.tssrc/components/modebar/attributes.ts,src/traces/image/attributes.ts)They are required extensionless from ~20
.jsfiles, e.g.:src/lib/index.js:78var modModule = require('./mod');src/lib/index.js:116lib.sortObjectKeys = require('./sort_object_keys').default;src/lib/index.js:181var regexModule = require('./regex');src/lib/index.js:217lib.cleanNumber = require('./clean_number').default;src/lib/coerce.js:13-14require('./regex').counter/require('./mod').modHalfsrc/lib/angles.js:3,src/lib/dates.js:7,src/lib/geometry2d.js:3require('./mod')src/lib/geo_location_utils.js:16require('./custom_country_codes')src/plots/map/layout_defaults.js:8require('./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:10require('../../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
.tsby default (Vite/Rollup/rolldown, webpack with.tsinresolve.extensions) are unaffected — production builds succeed. What breaks is anything using Node's resolution: plainrequire(), and — the way I hit it — test runners that externalizenode_modulesto Node's CJS loader (Vitestserver.depsdefault, Jest without a.ts-awaremoduleFileExtensions). Neitherserver.deps.inline: [/plotly\.js/]nordeps.optimizer.web.include: ['plotly.js']works around it on the Vitest side.Steps to reproduce
(
require.resolve('plotly.js/src/lib/mod')is exactly the resolutionsrc/lib/index.js:78performs internally.node_modules/plotly.js/src/lib/mod.tsexists 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:Same for
require('plotly.js/lib/core').For contrast, on
plotly.js@3.7.0the 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
Unhandled Rejection: Error: Cannot find module './mod', which makes the test run exit non-zero even when every test passes)..tssources to.jsduringprepublishOnlyso the publishedsrc/is all JavaScript, as in v3..tssources but add explicit extensions at the require sites (require('./mod.ts')won't help Node either), or emit sibling.jsfiles next to them..tsis intentional, anexportsmap pointing Node consumers atdist/plus a note in the v4 migration docs would at least make the new contract explicit.