Improving plotly.js bundling time using rollup

Feb 4, 2021 7:42 AM

I love Plotly.js because the batteries come included. I’m using this on my site analytics page. It looks nice, is interactive, and has reasonably useful documentation. The one negative aspect of the library is its size — the latest minified bundle (v1.58.4) clocks in at about 3.32MB. It needs to be included in the head of the document to work, which does not jive with how Sapper rehydrates bundles for each route. Plotly also has issues with bundlers like rollup when using the ES module because this is not set to window in the context of ES6 modules (see plotly/plotly.js#3518) for more details.

I applied a janky patch to resolve the issue in my bundler. In my rollup.config.js in my application, I have the following snippet in my client plugins list:

// https://github.com/plotly/plotly.js/issues/3518
replace({
    "}()": "this.d3 = d3;\n}.apply(self);",
    delimiters: ["this.d3 = d3;\n", ";"],
}),

Now I can import the module within the onMount section of a page (which is necessary because the server portion of Sapper does not have the capability to run Plotly).

onMount(async () => {
    const { default: Plotly } = await import("plotly.js/lib/core");
    Plotly.newPlot(...);
})

After implementing this, I noticed that my builds became staggeringly slow. What gives? It turns out that by including plotly.js into my application, the tree that rollup has to shake becomes significantly larger. It’s a little ridiculous.

Dependency tree of the custom plotly.js package
> npm ls --prod
plotly.js-custom@1.0.0 acmiyaguchi.me\packages\plotly.js-custom
`-- plotly.js@1.58.4
  +-- @plotly/d3-sankey@0.7.2
  | +-- d3-array@1.2.4
  | +-- d3-collection@1.0.7
  | `-- d3-shape@1.3.7
  |   `-- d3-path@1.0.9
  +-- @plotly/d3-sankey-circular@0.33.1
  | +-- d3-array@1.2.4 deduped
  | +-- d3-collection@1.0.7 deduped
  | +-- d3-shape@1.3.7 deduped
  | `-- elementary-circuits-directed-graph@1.2.0
  |   `-- strongly-connected-components@1.0.1 deduped
  +-- @plotly/point-cluster@3.1.9
  | +-- array-bounds@1.0.1
  | +-- binary-search-bounds@2.0.5
  | +-- clamp@1.0.1
  | +-- defined@1.0.0
  | +-- dtype@2.0.0
  | +-- flatten-vertex-data@1.0.2
  | | `-- dtype@2.0.0 deduped
  | +-- is-obj@1.0.1
  | +-- math-log2@1.0.1
  | +-- parse-rect@1.2.0
  | | `-- pick-by-alias@1.2.0 deduped
  | `-- pick-by-alias@1.2.0
  +-- @turf/area@6.3.0
  | +-- @turf/helpers@6.3.0
  | `-- @turf/meta@6.3.0
  |   `-- @turf/helpers@6.3.0 deduped
  +-- @turf/bbox@6.3.0
  | +-- @turf/helpers@6.3.0 deduped
  | `-- @turf/meta@6.3.0 deduped
  +-- @turf/centroid@6.3.0
  | +-- @turf/helpers@6.3.0 deduped
  | `-- @turf/meta@6.3.0 deduped
  +-- alpha-shape@1.0.0
  | +-- alpha-complex@1.0.0
  | | +-- circumradius@1.0.0
  | | | `-- circumcenter@1.0.0
  | | |   +-- dup@1.0.0 deduped
  | | |   `-- robust-linear-solve@1.0.0 deduped
  | | `-- delaunay-triangulate@1.1.6 deduped
  | `-- simplicial-complex-boundary@1.0.1
  |   +-- boundary-cells@2.0.2
  |   `-- reduce-simplicial-complex@1.0.0
  |     +-- cell-orientation@1.0.1
  |     +-- compare-cell@1.0.0
  |     `-- compare-oriented-cell@1.0.1
  |       +-- cell-orientation@1.0.1 deduped
  |       `-- compare-cell@1.0.0 deduped
  +-- canvas-fit@1.5.0
  | `-- element-size@1.1.1
  +-- color-alpha@1.0.4
  | `-- color-parse@1.3.8 deduped
  +-- color-normalize@1.5.0
  | +-- clamp@1.0.1 deduped
  | +-- color-rgba@2.1.1 deduped
  | `-- dtype@2.0.0 deduped
  +-- color-parse@1.3.8
  | +-- color-name@1.1.4
  | +-- defined@1.0.0 deduped
  | `-- is-plain-obj@1.1.0
  +-- color-rgba@2.1.1
  | +-- clamp@1.0.1 deduped
  | +-- color-parse@1.3.8 deduped
  | `-- color-space@1.16.0
  |   +-- hsluv@0.0.3
  |   `-- mumath@3.3.4
  |     `-- almost-equal@1.1.0
  +-- convex-hull@1.0.3
  | +-- affine-hull@1.0.0
  | | `-- robust-orientation@1.1.3 deduped
  | +-- incremental-convex-hull@1.0.1
  | | +-- robust-orientation@1.1.3 deduped
  | | `-- simplicial-complex@1.0.0
  | |   +-- bit-twiddle@1.0.2 deduped
  | |   `-- union-find@1.0.2 deduped
  | `-- monotone-convex-hull-2d@1.0.1
  |   `-- robust-orientation@1.1.3 deduped
  +-- country-regex@1.1.0
  +-- d3@3.5.17
  +-- d3-force@1.2.1
  | +-- d3-collection@1.0.7 deduped
  | +-- d3-dispatch@1.0.6
  | +-- d3-quadtree@1.0.7
  | `-- d3-timer@1.0.10
  +-- d3-hierarchy@1.1.9
  +-- d3-interpolate@1.4.0
  | `-- d3-color@1.4.1
  +-- d3-time-format@2.3.0
  | `-- d3-time@1.1.0
  +-- delaunay-triangulate@1.1.6
  | +-- incremental-convex-hull@1.0.1 deduped
  | `-- uniq@1.0.1
  +-- es6-promise@4.2.8
  +-- fast-isnumeric@1.1.4
  | `-- is-string-blank@1.0.1
  +-- gl-cone3d@1.5.2
  | +-- colormap@2.3.2
  | | `-- lerp@1.0.3
  | +-- gl-buffer@2.1.2
  | | +-- ndarray@1.0.19 deduped
  | | +-- ndarray-ops@1.2.2 deduped
  | | `-- typedarray-pool@1.2.0 deduped
  | +-- gl-mat4@1.2.0 deduped
  | +-- gl-shader@4.2.1
  | | +-- gl-format-compiler-error@1.0.3
  | | | +-- add-line-numbers@1.0.1
  | | | | `-- pad-left@1.0.2
  | | | |   `-- repeat-string@1.6.1
  | | | +-- gl-constants@1.0.0
  | | | +-- glsl-shader-name@1.0.0
  | | | | +-- atob-lite@1.0.0
  | | | | `-- glsl-tokenizer@2.1.5 deduped
  | | | `-- sprintf-js@1.1.2
  | | `-- weakmap-shim@1.1.1
  | +-- gl-texture2d@2.1.0
  | | +-- ndarray@1.0.19 deduped
  | | +-- ndarray-ops@1.2.2 deduped
  | | `-- typedarray-pool@1.2.0 deduped
  | +-- gl-vao@1.3.0
  | +-- gl-vec3@1.1.3
  | +-- glsl-inverse@1.0.0
  | +-- glsl-out-of-range@1.0.4
  | +-- glsl-specular-cook-torrance@2.0.1
  | | `-- glsl-specular-beckmann@1.1.2 deduped
  | +-- glslify@7.1.1 deduped
  | `-- ndarray@1.0.19 deduped
  +-- gl-contour2d@1.1.7
  | +-- binary-search-bounds@2.0.5 deduped
  | +-- cdt2d@1.0.0
  | | +-- binary-search-bounds@2.0.5 deduped
  | | +-- robust-in-sphere@1.1.3
  | | | +-- robust-scale@1.0.2 deduped
  | | | +-- robust-subtract@1.0.0 deduped
  | | | +-- robust-sum@1.0.0 deduped
  | | | `-- two-product@1.0.2 deduped
  | | `-- robust-orientation@1.1.3 deduped
  | +-- clean-pslg@1.1.2
  | | +-- big-rat@1.0.4
  | | | +-- bit-twiddle@1.0.2 deduped
  | | | +-- bn.js@4.11.9
  | | | `-- double-bits@1.1.1
  | | +-- box-intersect@1.0.2
  | | | +-- bit-twiddle@1.0.2 deduped
  | | | `-- typedarray-pool@1.2.0 deduped
  | | +-- nextafter@1.0.0
  | | | `-- double-bits@1.1.1 deduped
  | | +-- rat-vec@1.1.1
  | | | `-- big-rat@1.0.4 deduped
  | | +-- robust-segment-intersect@1.0.1
  | | | `-- robust-orientation@1.1.3 deduped
  | | +-- union-find@1.0.2
  | | `-- uniq@1.0.1 deduped
  | +-- gl-buffer@2.1.2 deduped
  | +-- gl-shader@4.2.1 deduped
  | +-- glslify@7.1.1 deduped
  | +-- iota-array@1.0.0
  | +-- ndarray@1.0.19 deduped
  | `-- surface-nets@1.0.2
  |   +-- ndarray-extract-contour@1.0.1
  |   | `-- typedarray-pool@1.2.0 deduped
  |   +-- triangulate-hypercube@1.0.1
  |   | +-- gamma@0.1.0
  |   | +-- permutation-parity@1.0.0
  |   | | `-- typedarray-pool@1.2.0 deduped
  |   | `-- permutation-rank@1.0.0
  |   |   +-- invert-permutation@1.0.0
  |   |   `-- typedarray-pool@1.2.0 deduped
  |   `-- zero-crossings@1.0.1
  |     `-- cwise-compiler@1.1.3 deduped
  +-- gl-error3d@1.0.16
  | +-- gl-buffer@2.1.2 deduped
  | +-- gl-shader@4.2.1 deduped
  | +-- gl-vao@1.3.0 deduped
  | +-- glsl-out-of-range@1.0.4 deduped
  | `-- glslify@7.1.1 deduped
  +-- gl-heatmap2d@1.1.1
  | +-- binary-search-bounds@2.0.5 deduped
  | +-- gl-buffer@2.1.2 deduped
  | +-- gl-shader@4.2.1 deduped
  | +-- glslify@7.1.1 deduped
  | +-- iota-array@1.0.0 deduped
  | `-- typedarray-pool@1.2.0
  |   +-- bit-twiddle@1.0.2 deduped
  |   `-- dup@1.0.0 deduped
  +-- gl-line3d@1.2.1
  | +-- binary-search-bounds@2.0.5 deduped
  | +-- gl-buffer@2.1.2 deduped
  | +-- gl-shader@4.2.1 deduped
  | +-- gl-texture2d@2.1.0 deduped
  | +-- gl-vao@1.3.0 deduped
  | +-- glsl-out-of-range@1.0.4 deduped
  | +-- glslify@7.1.1 deduped
  | `-- ndarray@1.0.19 deduped
  +-- gl-mat4@1.2.0
  +-- gl-mesh3d@2.3.1
  | +-- barycentric@1.0.1
  | | `-- robust-linear-solve@1.0.0
  | |   `-- robust-determinant@1.1.0
  | |     +-- robust-compress@1.0.0
  | |     +-- robust-scale@1.0.2 deduped
  | |     +-- robust-sum@1.0.0 deduped
  | |     `-- two-product@1.0.2 deduped
  | +-- colormap@2.3.2 deduped
  | +-- gl-buffer@2.1.2 deduped
  | +-- gl-mat4@1.2.0 deduped
  | +-- gl-shader@4.2.1 deduped
  | +-- gl-texture2d@2.1.0 deduped
  | +-- gl-vao@1.3.0 deduped
  | +-- glsl-out-of-range@1.0.4 deduped
  | +-- glsl-specular-cook-torrance@2.0.1 deduped
  | +-- glslify@7.1.1 deduped
  | +-- ndarray@1.0.19 deduped
  | +-- normals@1.1.0
  | +-- polytope-closest-point@1.0.0
  | | `-- numeric@1.2.6
  | +-- simplicial-complex-contour@1.0.2
  | | +-- marching-simplex-table@1.0.0
  | | | `-- convex-hull@1.0.3 deduped
  | | +-- ndarray@1.0.19 deduped
  | | +-- ndarray-sort@1.0.1
  | | | `-- typedarray-pool@1.2.0 deduped
  | | `-- typedarray-pool@1.2.0 deduped
  | `-- typedarray-pool@1.2.0 deduped
  +-- gl-plot2d@1.4.5
  | +-- binary-search-bounds@2.0.5 deduped
  | +-- gl-buffer@2.1.2 deduped
  | +-- gl-select-static@2.0.7
  | | +-- bit-twiddle@1.0.2 deduped
  | | +-- gl-fbo@2.0.5 deduped
  | | +-- ndarray@1.0.19 deduped
  | | `-- typedarray-pool@1.2.0 deduped
  | +-- gl-shader@4.2.1 deduped
  | +-- glsl-inverse@1.0.0 deduped
  | +-- glslify@7.1.1 deduped
  | `-- text-cache@4.2.2
  |   `-- vectorize-text@3.2.1 deduped
  +-- gl-plot3d@2.4.7
  | +-- 3d-view@2.0.0
  | | +-- matrix-camera-controller@2.1.3 deduped
  | | +-- orbit-camera-controller@4.0.0
  | | | +-- filtered-vector@1.2.4
  | | | | +-- binary-search-bounds@1.0.0
  | | | | `-- cubic-hermite@1.0.0
  | | | `-- gl-mat4@1.2.0 deduped
  | | `-- turntable-camera-controller@3.0.1
  | |   +-- filtered-vector@1.2.4 deduped
  | |   +-- gl-mat4@1.2.0 deduped
  | |   `-- gl-vec3@1.1.3 deduped
  | +-- a-big-triangle@1.0.3
  | | +-- gl-buffer@2.1.2 deduped
  | | +-- gl-vao@1.3.0 deduped
  | | `-- weak-map@1.0.5
  | +-- gl-axes3d@1.5.3
  | | +-- bit-twiddle@1.0.2 deduped
  | | +-- dup@1.0.0 deduped
  | | +-- extract-frustum-planes@1.0.0
  | | +-- gl-buffer@2.1.2 deduped
  | | +-- gl-mat4@1.2.0 deduped
  | | +-- gl-shader@4.2.1 deduped
  | | +-- gl-state@1.0.0
  | | | `-- uniq@1.0.1 deduped
  | | +-- gl-vao@1.3.0 deduped
  | | +-- gl-vec4@1.0.1 deduped
  | | +-- glslify@7.1.1 deduped
  | | +-- robust-orientation@1.1.3 deduped
  | | +-- split-polygon@1.0.0
  | | | +-- robust-dot-product@1.0.0
  | | | | +-- robust-sum@1.0.0 deduped
  | | | | `-- two-product@1.0.2 deduped
  | | | `-- robust-sum@1.0.0 deduped
  | | `-- vectorize-text@3.2.1 deduped
  | +-- gl-fbo@2.0.5
  | | `-- gl-texture2d@2.1.0 deduped
  | +-- gl-mat4@1.2.0 deduped
  | +-- gl-select-static@2.0.7 deduped
  | +-- gl-shader@4.2.1 deduped
  | +-- gl-spikes3d@1.0.10
  | | +-- gl-buffer@2.1.2 deduped
  | | +-- gl-shader@4.2.1 deduped
  | | +-- gl-vao@1.3.0 deduped
  | | `-- glslify@7.1.1 deduped
  | +-- glslify@7.1.1 deduped
  | +-- has-passive-events@1.0.0 deduped
  | +-- is-mobile@2.2.2 deduped
  | +-- mouse-change@1.4.0 deduped
  | +-- mouse-event-offset@3.0.2 deduped
  | +-- mouse-wheel@1.2.0 deduped
  | +-- ndarray@1.0.19 deduped
  | `-- right-now@1.0.0 deduped
  +-- gl-pointcloud2d@1.0.3
  | +-- gl-buffer@2.1.2 deduped
  | +-- gl-shader@4.2.1 deduped
  | +-- glslify@7.1.1 deduped
  | `-- typedarray-pool@1.2.0 deduped
  +-- gl-scatter3d@1.2.3
  | +-- gl-buffer@2.1.2 deduped
  | +-- gl-mat4@1.2.0 deduped
  | +-- gl-shader@4.2.1 deduped
  | +-- gl-vao@1.3.0 deduped
  | +-- glsl-out-of-range@1.0.4 deduped
  | +-- glslify@7.1.1 deduped
  | +-- is-string-blank@1.0.1 deduped
  | +-- typedarray-pool@1.2.0 deduped
  | `-- vectorize-text@3.2.1
  |   +-- cdt2d@1.0.0 deduped
  |   +-- clean-pslg@1.1.2 deduped
  |   +-- ndarray@1.0.19 deduped
  |   +-- planar-graph-to-polyline@1.0.5
  |   | +-- edges-to-adjacency-list@1.0.0
  |   | | `-- uniq@1.0.1 deduped
  |   | +-- planar-dual@1.0.2
  |   | | +-- compare-angle@1.0.1
  |   | | | +-- robust-orientation@1.1.3 deduped
  |   | | | +-- robust-product@1.0.0
  |   | | | | +-- robust-scale@1.0.2 deduped
  |   | | | | `-- robust-sum@1.0.0 deduped
  |   | | | +-- robust-sum@1.0.0 deduped
  |   | | | +-- signum@0.0.0
  |   | | | `-- two-sum@1.0.0 deduped
  |   | | `-- dup@1.0.0 deduped
  |   | +-- point-in-big-polygon@2.0.0
  |   | | +-- binary-search-bounds@1.0.0
  |   | | +-- interval-tree-1d@1.0.3
  |   | | | `-- binary-search-bounds@1.0.0
  |   | | +-- robust-orientation@1.1.3 deduped
  |   | | `-- slab-decomposition@1.0.2
  |   | |   +-- binary-search-bounds@1.0.0
  |   | |   +-- functional-red-black-tree@1.0.1
  |   | |   `-- robust-orientation@1.1.3 deduped
  |   | +-- robust-orientation@1.1.3 deduped
  |   | +-- robust-sum@1.0.0 deduped
  |   | +-- two-product@1.0.2 deduped
  |   | `-- uniq@1.0.1 deduped
  |   +-- simplify-planar-graph@2.0.1
  |   | +-- robust-orientation@1.1.3 deduped
  |   | `-- simplicial-complex@0.3.3
  |   |   +-- bit-twiddle@0.0.2
  |   |   `-- union-find@0.0.4
  |   +-- surface-nets@1.0.2 deduped
  |   `-- triangulate-polyline@1.0.3
  |     `-- cdt2d@1.0.0 deduped
  +-- gl-select-box@1.0.4
  | +-- gl-buffer@2.1.2 deduped
  | +-- gl-shader@4.2.1 deduped
  | `-- glslify@7.1.1 deduped
  +-- gl-spikes2d@1.0.2
  +-- gl-streamtube3d@1.4.1
  | +-- gl-cone3d@1.5.2 deduped
  | +-- gl-vec3@1.1.3 deduped
  | +-- gl-vec4@1.0.1
  | +-- glsl-inverse@1.0.0 deduped
  | +-- glsl-out-of-range@1.0.4 deduped
  | +-- glsl-specular-cook-torrance@2.0.1 deduped
  | `-- glslify@7.1.1 deduped
  +-- gl-surface3d@1.6.0
  | +-- binary-search-bounds@2.0.5 deduped
  | +-- bit-twiddle@1.0.2
  | +-- colormap@2.3.2 deduped
  | +-- dup@1.0.0
  | +-- gl-buffer@2.1.2 deduped
  | +-- gl-mat4@1.2.0 deduped
  | +-- gl-shader@4.2.1 deduped
  | +-- gl-texture2d@2.1.0 deduped
  | +-- gl-vao@1.3.0 deduped
  | +-- glsl-out-of-range@1.0.4 deduped
  | +-- glsl-specular-beckmann@1.1.2
  | +-- glslify@7.1.1 deduped
  | +-- ndarray@1.0.19 deduped
  | +-- ndarray-gradient@1.0.0
  | | +-- cwise-compiler@1.1.3
  | | | `-- uniq@1.0.1 deduped
  | | `-- dup@1.0.0 deduped
  | +-- ndarray-ops@1.2.2
  | | `-- cwise-compiler@1.1.3 deduped
  | +-- ndarray-pack@1.2.1
  | | +-- cwise-compiler@1.1.3 deduped
  | | `-- ndarray@1.0.19 deduped
  | +-- ndarray-scratch@1.2.0
  | | +-- ndarray@1.0.19 deduped
  | | +-- ndarray-ops@1.2.2 deduped
  | | `-- typedarray-pool@1.2.0 deduped
  | +-- surface-nets@1.0.2 deduped
  | `-- typedarray-pool@1.2.0 deduped
  +-- gl-text@1.1.8
  | +-- bit-twiddle@1.0.2 deduped
  | +-- color-normalize@1.5.0 deduped
  | +-- css-font@1.2.0
  | | +-- css-font-size-keywords@1.0.0
  | | +-- css-font-stretch-keywords@1.0.1
  | | +-- css-font-style-keywords@1.0.1
  | | +-- css-font-weight-keywords@1.0.0
  | | +-- css-global-keywords@1.0.1
  | | +-- css-system-font-keywords@1.0.0
  | | +-- pick-by-alias@1.2.0 deduped
  | | +-- string-split-by@1.0.0
  | | | `-- parenthesis@3.1.7
  | | `-- unquote@1.1.1
  | +-- detect-kerning@2.1.2
  | +-- es6-weak-map@2.0.3
  | | +-- d@1.0.1
  | | | +-- es5-ext@0.10.53 deduped
  | | | `-- type@1.2.0
  | | +-- es5-ext@0.10.53
  | | | +-- es6-iterator@2.0.3 deduped
  | | | +-- es6-symbol@3.1.3 deduped
  | | | `-- next-tick@1.0.0
  | | +-- es6-iterator@2.0.3
  | | | +-- d@1.0.1 deduped
  | | | +-- es5-ext@0.10.53 deduped
  | | | `-- es6-symbol@3.1.3 deduped
  | | `-- es6-symbol@3.1.3
  | |   +-- d@1.0.1 deduped
  | |   `-- ext@1.4.0
  | |     `-- type@2.1.0
  | +-- flatten-vertex-data@1.0.2 deduped
  | +-- font-atlas@2.1.0
  | | `-- css-font@1.2.0 deduped
  | +-- font-measure@1.2.2
  | | `-- css-font@1.2.0 deduped
  | +-- gl-util@3.1.3
  | | +-- is-browser@2.1.0 deduped
  | | +-- is-firefox@1.0.3
  | | +-- is-plain-obj@1.1.0 deduped
  | | +-- number-is-integer@1.0.1
  | | | `-- is-finite@1.1.0
  | | +-- object-assign@4.1.1 deduped
  | | +-- pick-by-alias@1.2.0 deduped
  | | `-- weak-map@1.0.5 deduped
  | +-- is-plain-obj@1.1.0 deduped
  | +-- object-assign@4.1.1
  | +-- parse-rect@1.2.0 deduped
  | +-- parse-unit@1.0.1
  | +-- pick-by-alias@1.2.0 deduped
  | +-- regl@1.7.0 deduped
  | +-- to-px@1.0.1 deduped
  | `-- typedarray-pool@1.2.0 deduped
  +-- glslify@7.1.1
  | +-- bl@2.2.1
  | | +-- readable-stream@2.3.7
  | | | +-- core-util-is@1.0.2
  | | | +-- inherits@2.0.4 deduped
  | | | +-- isarray@1.0.0
  | | | +-- process-nextick-args@2.0.1
  | | | +-- safe-buffer@5.1.2
  | | | +-- string_decoder@1.1.1
  | | | | `-- safe-buffer@5.1.2
  | | | `-- util-deprecate@1.0.2
  | | `-- safe-buffer@5.2.1
  | +-- concat-stream@1.6.2
  | | +-- buffer-from@1.1.1
  | | +-- inherits@2.0.4
  | | +-- readable-stream@2.3.7
  | | | +-- core-util-is@1.0.2 deduped
  | | | +-- inherits@2.0.4 deduped
  | | | +-- isarray@1.0.0
  | | | +-- process-nextick-args@2.0.1 deduped
  | | | +-- safe-buffer@5.1.2
  | | | +-- string_decoder@1.1.1
  | | | | `-- safe-buffer@5.1.2 deduped
  | | | `-- util-deprecate@1.0.2 deduped
  | | `-- typedarray@0.0.6
  | +-- duplexify@3.7.1
  | | +-- end-of-stream@1.4.4
  | | | `-- once@1.4.0
  | | |   `-- wrappy@1.0.2
  | | +-- inherits@2.0.4 deduped
  | | +-- readable-stream@2.3.7
  | | | +-- core-util-is@1.0.2 deduped
  | | | +-- inherits@2.0.4 deduped
  | | | +-- isarray@1.0.0
  | | | +-- process-nextick-args@2.0.1 deduped
  | | | +-- safe-buffer@5.1.2
  | | | +-- string_decoder@1.1.1
  | | | | `-- safe-buffer@5.1.2 deduped
  | | | `-- util-deprecate@1.0.2 deduped
  | | `-- stream-shift@1.0.1
  | +-- falafel@2.2.4
  | | +-- acorn@7.4.1
  | | +-- foreach@2.0.5
  | | +-- isarray@2.0.5
  | | `-- object-keys@1.1.1
  | +-- from2@2.3.0
  | | +-- inherits@2.0.4 deduped
  | | `-- readable-stream@2.3.7
  | |   +-- core-util-is@1.0.2 deduped
  | |   +-- inherits@2.0.4 deduped
  | |   +-- isarray@1.0.0
  | |   +-- process-nextick-args@2.0.1 deduped
  | |   +-- safe-buffer@5.1.2
  | |   +-- string_decoder@1.1.1
  | |   | `-- safe-buffer@5.1.2 deduped
  | |   `-- util-deprecate@1.0.2 deduped
  | +-- glsl-resolve@0.0.1
  | | +-- resolve@0.6.3
  | | `-- xtend@2.2.0
  | +-- glsl-token-whitespace-trim@1.0.0
  | +-- glslify-bundle@5.1.1
  | | +-- glsl-inject-defines@1.0.3
  | | | +-- glsl-token-inject-block@1.1.0
  | | | +-- glsl-token-string@1.0.1 deduped
  | | | `-- glsl-tokenizer@2.1.5 deduped
  | | +-- glsl-token-defines@1.0.0
  | | | `-- glsl-tokenizer@2.1.5 deduped
  | | +-- glsl-token-depth@1.1.2
  | | +-- glsl-token-descope@1.0.2
  | | | +-- glsl-token-assignments@2.0.2
  | | | +-- glsl-token-depth@1.1.2 deduped
  | | | +-- glsl-token-properties@1.0.1
  | | | `-- glsl-token-scope@1.1.2 deduped
  | | +-- glsl-token-scope@1.1.2
  | | +-- glsl-token-string@1.0.1
  | | +-- glsl-token-whitespace-trim@1.0.0 deduped
  | | +-- glsl-tokenizer@2.1.5
  | | | `-- through2@0.6.5
  | | |   +-- readable-stream@1.0.34
  | | |   | +-- core-util-is@1.0.2 deduped
  | | |   | +-- inherits@2.0.4 deduped
  | | |   | +-- isarray@0.0.1
  | | |   | `-- string_decoder@0.10.31
  | | |   `-- xtend@4.0.2 deduped
  | | +-- murmurhash-js@1.0.0 deduped
  | | `-- shallow-copy@0.0.1
  | +-- glslify-deps@1.3.2
  | | +-- @choojs/findup@0.2.1
  | | | `-- commander@2.20.3 deduped
  | | +-- events@3.2.0
  | | +-- glsl-resolve@0.0.1 deduped
  | | +-- glsl-tokenizer@2.1.5 deduped
  | | +-- graceful-fs@4.2.4
  | | +-- inherits@2.0.4 deduped
  | | +-- map-limit@0.0.1
  | | | `-- once@1.3.3
  | | |   `-- wrappy@1.0.2 deduped
  | | `-- resolve@1.19.0 deduped
  | +-- minimist@1.2.5
  | +-- resolve@1.19.0
  | | +-- is-core-module@2.2.0
  | | | `-- has@1.0.3
  | | |   `-- function-bind@1.1.1
  | | `-- path-parse@1.0.6
  | +-- stack-trace@0.0.9
  | +-- static-eval@2.1.0
  | | `-- escodegen@1.14.3
  | |   +-- esprima@4.0.1
  | |   +-- estraverse@4.3.0
  | |   +-- esutils@2.0.3
  | |   +-- optionator@0.8.3
  | |   | +-- deep-is@0.1.3
  | |   | +-- fast-levenshtein@2.0.6
  | |   | +-- levn@0.3.0
  | |   | | +-- prelude-ls@1.1.2 deduped
  | |   | | `-- type-check@0.3.2 deduped
  | |   | +-- prelude-ls@1.1.2
  | |   | +-- type-check@0.3.2
  | |   | | `-- prelude-ls@1.1.2 deduped
  | |   | `-- word-wrap@1.2.3
  | |   `-- source-map@0.6.1
  | +-- through2@2.0.5
  | | +-- readable-stream@2.3.7
  | | | +-- core-util-is@1.0.2 deduped
  | | | +-- inherits@2.0.4 deduped
  | | | +-- isarray@1.0.0
  | | | +-- process-nextick-args@2.0.1 deduped
  | | | +-- safe-buffer@5.1.2
  | | | +-- string_decoder@1.1.1
  | | | | `-- safe-buffer@5.1.2 deduped
  | | | `-- util-deprecate@1.0.2 deduped
  | | `-- xtend@4.0.2 deduped
  | `-- xtend@4.0.2
  +-- has-hover@1.0.1
  | `-- is-browser@2.1.0
  +-- has-passive-events@1.0.0
  | `-- is-browser@2.1.0 deduped
  +-- image-size@0.7.5
  +-- is-mobile@2.2.2
  +-- mapbox-gl@1.10.1
  | +-- @mapbox/geojson-rewind@0.5.0
  | | +-- concat-stream@2.0.0
  | | | +-- buffer-from@1.1.1 deduped
  | | | +-- inherits@2.0.4 deduped
  | | | +-- readable-stream@3.6.0
  | | | | +-- inherits@2.0.4 deduped
  | | | | +-- string_decoder@1.3.0
  | | | | | `-- safe-buffer@5.2.1 deduped
  | | | | `-- util-deprecate@1.0.2 deduped
  | | | `-- typedarray@0.0.6 deduped
  | | `-- minimist@1.2.5 deduped
  | +-- @mapbox/geojson-types@1.0.2
  | +-- @mapbox/jsonlint-lines-primitives@2.0.2
  | +-- @mapbox/mapbox-gl-supported@1.5.0
  | +-- @mapbox/point-geometry@0.1.0
  | +-- @mapbox/tiny-sdf@1.2.2
  | +-- @mapbox/unitbezier@0.0.0
  | +-- @mapbox/vector-tile@1.3.1
  | | `-- @mapbox/point-geometry@0.1.0 deduped
  | +-- @mapbox/whoots-js@3.1.0
  | +-- csscolorparser@1.0.3
  | +-- earcut@2.2.2
  | +-- geojson-vt@3.2.1
  | +-- gl-matrix@3.3.0
  | +-- grid-index@1.1.0
  | +-- minimist@1.2.5 deduped
  | +-- murmurhash-js@1.0.0
  | +-- pbf@3.2.1
  | | +-- ieee754@1.2.1
  | | `-- resolve-protobuf-schema@2.1.0
  | |   `-- protocol-buffers-schema@3.5.1
  | +-- potpack@1.0.1
  | +-- quickselect@2.0.0
  | +-- rw@1.3.3
  | +-- supercluster@7.1.2
  | | `-- kdbush@3.0.0
  | +-- tinyqueue@2.0.3
  | `-- vt-pbf@3.1.1
  |   +-- @mapbox/point-geometry@0.1.0 deduped
  |   +-- @mapbox/vector-tile@1.3.1 deduped
  |   `-- pbf@3.2.1 deduped
  +-- matrix-camera-controller@2.1.3
  | +-- binary-search-bounds@1.0.0
  | +-- gl-mat4@1.2.0 deduped
  | +-- gl-vec3@1.1.3 deduped
  | `-- mat4-interpolate@1.0.4
  |   +-- gl-mat4@1.2.0 deduped
  |   +-- gl-vec3@1.1.3 deduped
  |   +-- mat4-decompose@1.0.4
  |   | +-- gl-mat4@1.2.0 deduped
  |   | `-- gl-vec3@1.1.3 deduped
  |   +-- mat4-recompose@1.0.4
  |   | `-- gl-mat4@1.2.0 deduped
  |   `-- quat-slerp@1.0.1
  |     `-- gl-quat@1.0.0
  |       +-- gl-mat3@1.0.0
  |       +-- gl-vec3@1.1.3 deduped
  |       `-- gl-vec4@1.0.1 deduped
  +-- mouse-change@1.4.0
  | `-- mouse-event@1.0.5
  +-- mouse-event-offset@3.0.2
  +-- mouse-wheel@1.2.0
  | +-- right-now@1.0.0 deduped
  | +-- signum@1.0.0
  | `-- to-px@1.0.1 deduped
  +-- ndarray@1.0.19
  | +-- iota-array@1.0.0 deduped
  | `-- is-buffer@1.1.6
  +-- ndarray-linear-interpolate@1.0.0
  +-- parse-svg-path@0.1.2
  +-- polybooljs@1.2.0
  +-- regl@1.7.0
  +-- regl-error2d@2.0.11
  | +-- array-bounds@1.0.1 deduped
  | +-- color-normalize@1.5.0 deduped
  | +-- flatten-vertex-data@1.0.2 deduped
  | +-- object-assign@4.1.1 deduped
  | +-- pick-by-alias@1.2.0 deduped
  | +-- to-float32@1.0.1
  | `-- update-diff@1.1.0
  +-- regl-line2d@3.1.0
  | +-- array-bounds@1.0.1 deduped
  | +-- array-find-index@1.0.2
  | +-- array-normalize@1.1.4
  | | `-- array-bounds@1.0.1 deduped
  | +-- color-normalize@1.5.0 deduped
  | +-- earcut@2.2.2 deduped
  | +-- es6-weak-map@2.0.3 deduped
  | +-- flatten-vertex-data@1.0.2 deduped
  | +-- glslify@7.1.1 deduped
  | +-- object-assign@4.1.1 deduped
  | +-- parse-rect@1.2.0 deduped
  | +-- pick-by-alias@1.2.0 deduped
  | `-- to-float32@1.0.1 deduped
  +-- regl-scatter2d@3.2.3
  | +-- @plotly/point-cluster@3.1.9 deduped
  | +-- array-range@1.0.1
  | +-- array-rearrange@2.2.2
  | +-- clamp@1.0.1 deduped
  | +-- color-id@1.1.0
  | | `-- clamp@1.0.1 deduped
  | +-- color-normalize@1.5.0 deduped
  | +-- color-rgba@2.1.1 deduped
  | +-- flatten-vertex-data@1.0.2 deduped
  | +-- glslify@7.1.1 deduped
  | +-- image-palette@2.1.0
  | | +-- color-id@1.1.0 deduped
  | | +-- pxls@2.3.2
  | | | +-- arr-flatten@1.1.0
  | | | +-- compute-dims@1.1.0
  | | | | +-- utils-copy@1.1.1
  | | | | | +-- const-pinf-float64@1.0.0
  | | | | | +-- object-keys@1.1.1 deduped
  | | | | | +-- type-name@2.0.2
  | | | | | +-- utils-copy-error@1.0.1
  | | | | | | +-- object-keys@1.1.1 deduped
  | | | | | | `-- utils-copy@1.1.1 deduped
  | | | | | +-- utils-indexof@1.0.0
  | | | | | | +-- validate.io-array-like@1.0.2
  | | | | | | | +-- const-max-uint32@1.0.2
  | | | | | | | `-- validate.io-integer-primitive@1.0.0 deduped
  | | | | | | `-- validate.io-integer-primitive@1.0.0
  | | | | | |   `-- validate.io-number-primitive@1.0.0
  | | | | | +-- utils-regex-from-string@1.0.0
  | | | | | | +-- regex-regex@1.0.0
  | | | | | | `-- validate.io-string-primitive@1.0.1
  | | | | | +-- validate.io-array@1.0.6 deduped
  | | | | | +-- validate.io-buffer@1.0.2
  | | | | | `-- validate.io-nonnegative-integer@1.0.0
  | | | | |   `-- validate.io-integer@1.0.5 deduped
  | | | | +-- validate.io-array@1.0.6
  | | | | +-- validate.io-matrix-like@1.0.2
  | | | | +-- validate.io-ndarray-like@1.0.0
  | | | | `-- validate.io-positive-integer@1.0.0
  | | | |   `-- validate.io-integer@1.0.5
  | | | |     `-- validate.io-number@1.0.3
  | | | +-- flip-pixels@1.0.2
  | | | +-- is-browser@2.1.0 deduped
  | | | +-- is-buffer@2.0.5
  | | | `-- to-uint8@1.4.1
  | | |   +-- arr-flatten@1.1.0 deduped
  | | |   +-- clamp@1.0.1 deduped
  | | |   +-- is-base64@0.1.0
  | | |   +-- is-float-array@1.0.0
  | | |   `-- to-array-buffer@3.2.0
  | | |     +-- flatten-vertex-data@1.0.2 deduped
  | | |     +-- is-blob@2.1.0
  | | |     `-- string-to-arraybuffer@1.0.2
  | | |       +-- atob-lite@2.0.0
  | | |       `-- is-base64@0.1.0 deduped
  | | `-- quantize@1.0.2
  | +-- is-iexplorer@1.0.0
  | +-- object-assign@4.1.1 deduped
  | +-- parse-rect@1.2.0 deduped
  | +-- pick-by-alias@1.2.0 deduped
  | +-- to-float32@1.0.1 deduped
  | `-- update-diff@1.1.0 deduped
  +-- regl-splom@1.0.14
  | +-- array-bounds@1.0.1 deduped
  | +-- array-range@1.0.1 deduped
  | +-- color-alpha@1.0.4 deduped
  | +-- flatten-vertex-data@1.0.2 deduped
  | +-- parse-rect@1.2.0 deduped
  | +-- pick-by-alias@1.2.0 deduped
  | +-- raf@3.4.1
  | | `-- performance-now@2.1.0
  | `-- regl-scatter2d@3.2.3 deduped
  +-- right-now@1.0.0
  +-- robust-orientation@1.1.3
  | +-- robust-scale@1.0.2
  | | +-- two-product@1.0.2 deduped
  | | `-- two-sum@1.0.0
  | +-- robust-subtract@1.0.0
  | +-- robust-sum@1.0.0
  | `-- two-product@1.0.2
  +-- sane-topojson@4.0.0
  +-- strongly-connected-components@1.0.1
  +-- superscript-text@1.0.0
  +-- svg-path-sdf@1.1.3
  | +-- bitmap-sdf@1.0.3
  | | `-- clamp@1.0.1 deduped
  | +-- draw-svg-path@1.0.0
  | | +-- abs-svg-path@0.1.1
  | | `-- normalize-svg-path@0.1.0
  | +-- is-svg-path@1.0.2
  | +-- parse-svg-path@0.1.2 deduped
  | `-- svg-path-bounds@1.0.1
  |   +-- abs-svg-path@0.1.1 deduped
  |   +-- is-svg-path@1.0.2 deduped
  |   +-- normalize-svg-path@1.1.0
  |   | `-- svg-arc-to-cubic-bezier@3.2.0
  |   `-- parse-svg-path@0.1.2 deduped
  +-- tinycolor2@1.4.2
  +-- to-px@1.0.1
  | `-- parse-unit@1.0.1 deduped
  +-- topojson-client@3.1.0
  | `-- commander@2.20.3
  +-- webgl-context@2.2.0
  | `-- get-canvas-context@1.0.2
  `-- world-calendars@1.0.3
    `-- object-assign@4.1.1 deduped

I had an idea to create a custom package of plotly.js that exports the core library, which would be bundled and expose a browser package. It turns out that it helped a fair bit. The set up was simple. My index.js contains two lines that re-exports the module.

import Plotly from "plotly.js/lib/core";
export default Plotly;

In my main application, I install it into my packages.json as so:

"dependencies": {
    ...
    "plotly.js-custom": "file:../packages/plotly.js-custom",
},

See the full package here.

It was faster, but I wanted to figure out whether this was real or if I was imagining things from having spent too long figuring out how to make a rollup package. Here’s some of the data I collected. All reported figures are in seconds. The summarized data is the mean plus or minus the standard deviation (MEAN ± SD) seconds.

The first table is the time it took for npm run build to complete. I did 3 trials for 3 different commits: a time before plotly, the naive direct approach, the custom package approach.

Build time via npm run build

no plotly direct esm custom esm
32.5 70 81
32.5 71 80
32 74 84
32.3±0.29 71.7±2.08 81.7±2.08

I also took a look at the incremental builds via npm run dev. Once the build is fully created, I hit save on a page to trigger a rebuild. Each rebuild includes a bundle for the server, the client, and the service worker.

Incremental build time via npm run dev

no plotly direct esm custom esm
server 1.4±0.12 8.±0.16 1.1±0.1
client 3.5±0.23 8.1±0.21 5.4±0.16
total 4.9±0.35 16.1±0.36 6.5±0.23

The time I care about is the incremental build time. This is where I spent the majority of my time. It’s annoying when it takes a full 16 seconds before I can see a new change to a page on the site. This feedback is important when trying out new layouts or playing with the functionality of an unfamiliar library. The custom ES module cut that time to 6.5 seconds, mostly by preventing the server bundle from ever having to traverse the dependency tree for plotly. It also does marginally better than the direct ES module by 2.7 seconds.

The refactor did not entirely line up with my expectations on the other hand. I thought I would be able to create a single bundle for plotly that included all of the dependencies in one fat script. This is not the case with rollup (or ES modules) because the full knowledge of the dependency tree is how the bundler can generate efficient code. The size of the bundle would probably bloat up if I included all the dependencies inline (like the 3MB minified source). The size is not too big of a deal because Sapper only includes on the pages that need it. It would have been nice to shave off few extra seconds on those incremental builds.

I’m happy enough with this setup to not touch it for a while. I may look into smaller plotting libraries that don’t contain as much baggage, but it would require learning yet another tool. Plotly is a great tool, all things considered. With reasonable bundle sizes and incremental builds, this is good enough for me.

Full data

Incremental build times

Incremental build times for custom plotly ES module

server client total
1.2 5.6 6.8
1.1 5.2 6.3
0.919 5.3 6.2
1.1 5.4 6.5
1.1 5.5 6.6
1.1±0.1 5.4±0.16 6.5±0.23

Incremental build times for direct plotly ES module

server client total
8.2 8.4 16.6
7.8 7.9 15.7
7.9 7.9 15.8
8.1 8.1 16.2
8 8 16
8.±0.16 8.1±0.21 16.1±0.36

Incremental build times for no plotly

server client total
1.2 3.1 4.3
1.5 3.6 5.1
1.5 3.7 5.2
1.4 3.5 4.9
1.4 3.5 4.9
1.4±0.12 3.5±0.23 4.9±0.35

Command log output
commit c5ac12f90f97458b4d3c6ecf2ac51dc5bfee26ba (HEAD -> main)
Author: Anthony Miyaguchi <acmiyaguchi@gmail.com>
Date:   Wed Feb 3 22:17:21 2021 -0800

    Add node_modules to ignores

> sapper dev

✔ server (7.1s)
✔ client (19.7s)
✔ service worker (45ms)

> Listening on http://localhost:3000

src\routes\analytics\Plot.svelte changed. rebuilding...

src\routes\analytics\Plot.svelte changed. rebuilding...
✔ server (1.2s)
✔ client (5.6s)
✔ service worker (44ms)

src\routes\analytics\Plot.svelte changed. rebuilding...

src\routes\analytics\Plot.svelte changed. rebuilding...
✔ server (1.1s)
✔ client (5.2s)
✔ service worker (34ms)

src\routes\analytics\Plot.svelte changed. rebuilding...

src\routes\analytics\Plot.svelte changed. rebuilding...
✔ server (919ms)
✔ client (5.3s)
✔ service worker (35ms)

src\routes\analytics\Plot.svelte changed. rebuilding...

src\routes\analytics\Plot.svelte changed. rebuilding...
✔ server (1.1s)
✔ client (5.4s)
✔ service worker (33ms)

src\routes\analytics\Plot.svelte changed. rebuilding...

src\routes\analytics\Plot.svelte changed. rebuilding...
✔ server (1.1s)
✔ client (5.5s)
✔ service worker (32ms)

npm run build

> Finished in 1m10s. Type node **sapper**/build to run the app.
> Finished in 1m11s. Type node **sapper**/build to run the app.
> Finished in 1m14s. Type node **sapper**/build to run the app.

commit cc5e80ab3aa7934679c2a58a9cf83a0f8cc7ad4e (origin/main)
Author: Anthony Miyaguchi <acmiyaguchi@gmail.com>
Date: Wed Feb 3 00:14:11 2021 -0800

    Add plotly plot for analytics

> sapper dev

✔ server (28.3s)
✔ client (28.4s)
✔ service worker (38ms)

> Listening on http://localhost:3000

src\routes\analytics\Plot.svelte changed. rebuilding...

src\routes\analytics\Plot.svelte changed. rebuilding...
✔ server (8.2s)
✔ client (8.4s)
✔ service worker (30ms)

src\routes\analytics\Plot.svelte changed. rebuilding...

src\routes\analytics\Plot.svelte changed. rebuilding...
✔ server (7.8s)
✔ client (7.9s)
✔ service worker (35ms)

src\routes\analytics\Plot.svelte changed. rebuilding...

src\routes\analytics\Plot.svelte changed. rebuilding...
✔ server (7.9s)
✔ client (7.9s)
✔ service worker (31ms)

src\routes\analytics\Plot.svelte changed. rebuilding...

src\routes\analytics\Plot.svelte changed. rebuilding...
✔ server (8.1s)
✔ client (8.1s)
✔ service worker (32ms)

src\routes\analytics\Plot.svelte changed. rebuilding...

src\routes\analytics\Plot.svelte changed. rebuilding...
✔ server (8.0s)
✔ client (8.0s)
✔ service worker (34ms)

npm run build

> Finished in 1m21s. Type node **sapper**/build to run the app.
> Finished in 1m20s. Type node **sapper**/build to run the app.
> Finished in 1m24s. Type node **sapper**/build to run the app.

commit 05af63470f6f590b90dbc7e3a7ae987776651067
Author: Anthony Miyaguchi <acmiyaguchi@gmail.com>
Date: Tue Feb 2 23:10:50 2021 -0800

    Update analytics page with more tables

> sapper dev

✔ server (26.0s)
✔ client (26.1s)
✔ service worker (35ms)

> Listening on http://localhost:3000

src\routes\analytics\index.svelte changed. rebuilding...

src\routes\analytics\index.svelte changed. rebuilding...
✔ server (1.2s)
✔ client (3.1s)
✔ service worker (31ms)

src\routes\analytics\index.svelte changed. rebuilding...

src\routes\analytics\index.svelte changed. rebuilding...
✔ server (1.5s)
✔ client (3.6s)
✔ service worker (33ms)

src\routes\analytics\index.svelte changed. rebuilding...

src\routes\analytics\index.svelte changed. rebuilding...
✔ server (1.5s)
✔ client (3.7s)
✔ service worker (33ms)

src\routes\analytics\index.svelte changed. rebuilding...

src\routes\analytics\index.svelte changed. rebuilding...
✔ server (1.4s)
✔ client (3.5s)
✔ service worker (32ms)

src\routes\analytics\index.svelte changed. rebuilding...

src\routes\analytics\index.svelte changed. rebuilding...
✔ server (1.4s)
✔ client (3.5s)
✔ service worker (30ms)

npm run build

> Finished in 32.5s. Type node **sapper**/build to run the app.
> Finished in 32.5s. Type node **sapper**/build to run the app.
> Finished in 32.0s. Type node **sapper**/build to run the app.