How to keep a notebook working

Here’s an example of how to use Rollup to bundle Leaflet, its plugins, and its CSS into an ES module that you can simply import into the browser.

I’m using several Rollup plugins here:

  • @rollup/plugin-node-resolve - to bundle stuff in node_modules
  • @rollup/plugin-commonjs - because Leaflet plugins use CommonJS
  • rollup-plugin-styles - to bake the CSS into JavaScript and inject it
  • rollup-plugin-terser - to minify the JavaScript

The last two are optional. You could load the CSS separately if you prefer, but I figured it was nice to have it injected automatically so you don’t have to think about it separately. And you could avoid the minification if you want to, say, use the debugger and step into Leaflet code.

The Rollup config looks like this:

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import styles from "rollup-plugin-styles";
import {terser} from "rollup-plugin-terser";

export default [
  {
    input: "src/index.js",
    output: {
      file: "dist/leaflet.js",
      format: "es"
    },
    plugins: [
      resolve(),
      commonjs(),
      styles(),
      terser()
    ]
  }
];

Then the main entry point src/index.js looks like this:

import L from "leaflet";

import "leaflet-fullscreen";
import "leaflet-timedimension";
import "leaflet.sync";
import "leaflet.control.resizer";

import "leaflet/dist/leaflet.css";
import "leaflet-fullscreen/dist/leaflet.fullscreen.css";
import "leaflet-timedimension/dist/leaflet.timedimension.control.min.css";
import "leaflet.control.resizer/L.Control.Resizer.css";

export default L;

I’m using side-effect imports here: each plugin mutates the L object created by Leaflet, so just by importing it, the L object will get decorated with the extra methods declared by the plugins. (In modern JavaScript, we’d prefer to avoid that and instead import symbols from the plugins, so that plugins don’t have to share a single namespace.)

Lastly I published this bundle to npm so that I can load it into a notebook like so:

L = (await import("https://cdn.jsdelivr.net/npm/rollup-leaflet-example@0.0.1/dist/leaflet.js")).default

Live example:

Stepping back for a second, the problem here is the historical lack of standardization in how JavaScript is distributed. We have ES modules now, but there are many older libraries that have not yet been updated to the new standard. The require primitive that Observable provides is intended to help load this old style of library, but it’s always going to be a mess. I’d love to be able to put a layer of abstraction on top of this to make things “just work” out of the box, but it’s difficult to do generally. In the meantime, doing some bundling yourself is probably your best bet to modernize libraries and make them load reliably (and fast). Good luck.

4 Likes