D3 7.0 to boost compatibility with Node.js by going all-in on ECMAScript modules


Popular data visualisation library D3 just received a major version bump, improving compatibility with JavaScript runtime environment Node.js.

Svelte creator Rich Harris nudged the change into being in February, when he pointed out that there currently isn’t a way to have D3 programs that both run in Node.js, and can be typechecked and bundled. The reason for this is largely down to earlier versions not being compatible with Node-projects using “type”: “module” in their package.json file, a notation allowed since v12 of the runtime.

After some discussion and consideration of what other projects are doing, D3 creator Mike Bostock decided to resolve the issue by having D3 7.0 “go all-in” on ECMAScript modules (ESM). These have been available for a while, but only gained momentum last month, since running ESM needs Node.js 12 or greater to work and April 30th marked the end of life for Node.js 10

To implement the change, Bostock apparently followed open source dev Sindre Sorhus’s approach of using “type”: “module”, while dropping conditional exports, the CommonJS entry point and support for require completely. While the combination of CommonJS and require was the way to go when using modules for quite some time, we’ll probably see a move to ESM in the next few years, since it promises a unified syntax, browser compatibility, top-level await, and asynchronous loading, amongst other things.

The new setup means D3 7.0 needs Node.js 12 or higher to work, so teams should check what this entails for their code before making the switch. Other changes that could lead to older code not working as expected are the adoption of Bostock’s InternMap project for ordinal scale domains, and the adjustment of d3.bin to ignore nulls and d3.ascending and d3.descending to no longer consider null comparable.

However, v7.0 is not all breaking changes: among other things, the library now includes a function to find out the mode — the value that appears most often — of an iterable. It also comes with additional versions of the group and rollup functions, which return a flat array instead of nested structures, making it easier to call functions on all groups.

Developers now have the option of using transition.selectChild([selector]) and transition.selectChildren([selector]) to find the first child or all children of a selected element that match a selector string. The selector can be specified as a selector string or function, with the mechanism returning a transition on the selected element/s.

Further details and information on fixed issues with axis ticks, zoom and drag events can be found in the release notes.