Hi folks,
I've been digging around today trying to figure out why Webpack's tree shaking magic did not appear to be working on Leaflet's code. Digging back through the history here, I see the following historic PRs:
LIn the removal hotfix, there was a comment that "people who want to take advantage of tree shaking can opt-in by adding custom rules resolving Leaflet to dist/leaflet-src.esm.js." I'll freely admit that I'm struggling to figure out how to do that (it may be pretty clear to be fair; I'm really not up to date with the modern Javascript ecosystem.)
Ever since that hotfix, there's been a few PRs attempting to reintroduce module support but they've all been closed with a link back to the removal hotfix, and no further discussion.
The Leaflet/Leaflet.markercluster#874 issue remains open, with recent (January this year) comments floating the ideas of:
L global variableL to the leaflet module, rather than requiring Leaflet to expose L as a global variable.Beyond this chatter on the Markercluster repo, I haven't seen any other discussion on how the Leaflet community is feeling about the lack of ES6 module support and the widespread (?) dependence on a global L. If I may, I'd like to be the one to bring these up for discussion.
Do we know what the remaining blockers are for:
LAnd is there anything we can do make progress on these?
Thanks all,
Rob
It would be great to just add the "module" field to package.json
Hey everyone, I'm new to the project. Working with a team, considering migrating from Mapbox to Leaflet to save on performance. Would really love the ability to import at an ES6 module. Threw me for a loop this morning that I could not.
*Edit: if it's a useful data point, we primarily build with Vue.js
You can import from /dist/leaflet-src.esm.js, but if your toolchain relies on the module field in package.json, as most tools do at the moment, you will be out of luck.
your options are to rewrite the imports at build/run time, or to patch the package.json under node_modules/leaflet to include the line
"module": "dist/leaflet-src.esm.js"
Hopefully our good maintainers will merge my PR soon and we won't have to resort to patch-package shenanigans anymore
In the mean time, put this in scripts/fix-leaflet.mjs
// @ts-check
import pkgJson from '../node_modules/leaflet/package.json';
import { writeFileSync } from 'fs';
writeFileSync(
'./node_modules/leaflet/package.json',
JSON.stringify({
...pkgJson,
module: 'dist/leaflet-src.esm.js',
}, null, 2));
then add this to your postinstall:
"postinstall": "node --experimental-json-modules scripts/fix-leaflet.mjs"
I added a default index.js to the root of the Leaflet package. With this in place, I can just add the raw Leaflet repos to a project (where the dist folder contains only css files). Node automatically falls back to the main source, since the main entry point from package.json does not exist.
valdese-net/Leaflet@ade00ec59dbc494ea9f6b48cba403299d8415971
I have a setup that works pretty well using Parcel v2. I suppose it would work for any bundler.
What I do is have my own module myLeaflet.js where I import only what is required by my own code and the plugins I use. As an example,
/* myLeaflet.js */
import {
Control,
DomEvent,
DomUtil,
Evented,
LatLng,
LatLngBounds,
Layer,
Map
} from "../../node_modules/leaflet/dist/leaflet-src.esm.js"
export {
Control,
DomEvent,
DomUtil,
Evented,
LatLng,
LatLngBounds,
Layer,
Map
}
/*
* The global namespace L is required by a few plugins. We provide one,
* with the bare minimum content that they require.
*/
window.L = {
Control,
DomEvent,
DomUtil
}
In my own code I import what I need from myLeaflet.js rather than leaflet. For the plugins that require the L namespace, a minimal one is there for them. Some leaflet plugins modify an existing Leaflet class and some create their own. As an example of two that are in node_modules I do something like
import { Map } from "./myLeaflet.js"
import "thePlugin"
import "theOtherPlugin"
const L = window.L
const thePlugin = L.thePlugin()
const theOtherPlugin = new L.Control.theOtherPlugin(options)
const map = new Map()
theOtherPlugin.addTo(map)
...
One peculiarity is that, at least for Parcel, it does not work to import the plugins inside myLeaflet.js and export them from there. myLeaflet.js must be imported first, and separately. The act of importing it populates the L namespace.
So there is some pollution of the global namespace with L but not much, and full tree-shaking is enabled.
I think @ebrensi has a really good approach. If Leaflet doesn't pursue ES6 module support in the future and someone finds this thread looking for a good solution, I just want to endorse that idea. Despite relying too much on the global L, it still does a great job of encapsulating the Leaflet code and managing it. This kind of interface between 3rd party libraries and your own code is a responsible way to do things (and ES6 modules kind of solve the problem for us, but without them, a custom solution like this is as good as anything).
Most helpful comment
I have a setup that works pretty well using Parcel v2. I suppose it would work for any bundler.
What I do is have my own module
myLeaflet.jswhere I import only what is required by my own code and the plugins I use. As an example,In my own code I import what I need from
myLeaflet.jsrather thanleaflet. For the plugins that require theLnamespace, a minimal one is there for them. Some leaflet plugins modify an existing Leaflet class and some create their own. As an example of two that are innode_modulesI do something likeOne peculiarity is that, at least for Parcel, it does not work to import the plugins inside
myLeaflet.jsand export them from there.myLeaflet.jsmust be imported first, and separately. The act of importing it populates theLnamespace.So there is some pollution of the global namespace with
Lbut not much, and full tree-shaking is enabled.