score:3

Accepted answer

D3's docs on the geographic path generators have you covered (emphasis mine):

The geographic path generator, d3.geoPath, is similar to the shape generators in d3-shape: given a GeoJSON geometry or feature object, it generates an SVG path data string or renders the path to a Canvas.

You can pass a GeoJSON feature into the path generator and have it create the path data—i.e. the path commands—which are then assigned to the d property of the <path> element. This is basically what happens when coding

.data(features)
// ... enter, append, etc...
.attr("d", path); 

This statement can be rewritten as:

.data(features)
// ... enter, append, etc...
.attr("d", d => path(d));

or, even more explicitly:

.data(features)
// ... enter, append, etc...
.attr("d", feature => path(feature));

Looking at the latter code snippet it becomes clear that D3—via its data binding means—creates a <path> element for every feature and passes that feature, i.e. the datum bound the the <path> element, to the path generator path to create the d property's path command string.

With this knowledge you can easily create the path data strings for all your features without creating any DOM nodes:

const pathData = features.map(feature => path(feature));

More concisely, you can just pass the generator function to .map():

const pathData = features.map(path);

Related Query