Repository navigation
sideEffects: false; ES bundle - #2468
Conversation
|
This import commonjs from "@rollup/plugin-commonjs";
import {nodeResolve} from "@rollup/plugin-node-resolve";
import {rollup} from "rollup";
import {assert, it} from "vitest";
it("bundled marks have a plot method", async () => {
const entry = {
resolveId: (id) => (id === "entry" ? id : null),
load: (id) => (id === "entry" ? `import {dot} from "./src/index.js"; export default dot().plot;` : null)
};
const bundle = await rollup({input: "entry", plugins: [entry, commonjs(), nodeResolve()]});
const {output} = await bundle.generate({format: "iife", name: "result"});
assert.strictEqual(new Function(`${output[0].code}; return typeof result;`)(), "function");
});ref. rollup/rollup#5538 (comment) for a solution that would work for rollup (but maybe not for other bundlers?). |
|
Ugh, stupid bundlers. Thanks for finding. |
|
So somewhat astonishingly, the behavior depends on whether we add @rollup/plugin-node-resolve… |
|
I guess that’s because it only checks for |
|
Your test still doesn’t work because it only imports the |
|
I think I’ve fixed it, but now I’m blocked by esm-dev/esm.sh#1490; esm.sh isn’t currently serving new packages due to being out of disk space. |
|
esm.sh is back, and I’ve confirmed that this technique now works. It does mean that importing a subclass of Mark by itself (such as dot) won’t include the plot method, though. I’m not sure if that’s fixable… or acceptable. But I don’t think I should overindex on the tree shaking behavior of bundlers; more important that the library loads quickly in the common case. |
|
My plan now is to add a Rollup ES module bundle to this library, and ship that, so that we are not dependent on others’ bundlers for correct behavior. I can then go back to using circular imports (which are perfectly valid according to the specification), but those circular imports won’t be visible outside this library because we’ll only publish a single module. |
|
All D3 modules have been updated, so I’ll try to make this one consistent (if it isn’t already) and then publish. |
… 0.6.18 breaks (#1201) (#1202) problem: @observablehq/plot 0.6.18 (released 2026-11-03) changed its exports default from ./src/index.js to ./dist/plot.js (observablehq/plot#2468, "sideEffects: false; ES bundle"). its "types" field still points to src/index.d.ts, but with moduleResolution "bundler" typescript resolves through exports, finds dist/plot.js with no declaration file beside it, and every plot import fails with ts7016 ("could not find a declaration file for module '@observablehq/plot'"). 0.6.17 is unaffected: its exports default is src/index.js, next to src/index.d.ts. the install-without-lock ci check (rm package-lock.json && npm install) picked up 0.6.18 through the ^0.6.17 range and failed test-compile on every pull request. fix: pin the devdependency to exactly 0.6.17. an exact version is needed: for a 0.x version, ~0.6.17 is the same range as ^0.6.17 and still allows 0.6.18. peerdependencies stays ^0.6.17, so apps can use 0.6.18, which works at runtime (checked with a tarball in data-browser's anvil-cmg explorer: the datasets graph view renders all its charts with no console errors). not done: a tsconfig "paths" entry pointing @observablehq/plot at src/index.d.ts fixes tsc, but storybook's next.js integration applies tsconfig paths to runtime imports too, so the storybook build fails. plot's file layout differs between 0.6.17 (src/*.js) and 0.6.18 (dist/plot.js), so no single path serves both types and runtime. to remove the pin: set the devdependency back to ^0.6.17 (matching peerdependencies), delete node_modules and package-lock.json, run npm install, and confirm the installed plot is the latest release. then run npx tsc, npm run test-compile and npx storybook build. once they pass, upstream has fixed its types (a "types" condition in exports, or a dist/plot.d.ts) and the pin can go. Co-authored-by: Fran McDade <18710366+frano-m@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>
This distributes Plot exclusively as a bundle, rather than shipping the unbundled source files. This in turn allows
sideEffects: false, and allows us to resume using an (internal) circular dependency to more cleanly describe the relationship between Mark and the plot function. This approach further allows esm.sh to bundle Plot more efficiently.