Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ES6 modules & Rollup #4989

Merged
merged 54 commits into from
Jan 30, 2017
Merged

ES6 modules & Rollup #4989

merged 54 commits into from
Jan 30, 2017

Conversation

mourner
Copy link
Member

@mourner mourner commented Sep 30, 2016

This an in-progress PR that switches Leaflet to use ES6 modules system and Rollup for bundling. Closes #3229. It's A LOT of tedious work going through all the code, but it looks like it's going to be a major improvement.

  • Dependencies will be imported explicitly in each file instead of depending on global namespaces, making the code easier to maintain and reason about.
  • Things like L.Util.bind will turn into just bind, which means pretty much all references to Leaflet functions/classes will be properly minified, reducing the bundle size significantly (in theory).
  • I love the syntax, it just comes along very naturally.
  • We'll switch to a rollup-watch-based debug workflow, eliminating the document.write hack.
  • People who use ES6 modules or Rollup in their apps will be able to benefit from tree shaking (import stuff they need directly and discard all the rest).

cc @IvanSanchez @patrickarlt @olanod @tmcw @perliedman @yohanboniface @hyperknot

Status:

  • src/**
  • publish.sh script
  • Manually check pointer event handling hacks
  • Manually check touch event handling hacks
  • Cleanup old build script & custom builds system
  • Move unrunnable L.Path tests from suites/layer/vector/PathSpec.js to somewhere else
  • Tweak build script to output file sizes
  • Rename output files from leaflet-rollup-src.js to leaflet-src.js
  • Wait until Do not output getter defs when in legacy mode rollup/rollup#1069 is fixed (IE8 is broken until then)
  • Rollup config/plugin to replace L.version to a string including revision

import {LatLngBounds, toLatLngBounds} from '../geo/LatLngBounds';

import {
any3d as isAny3D,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at least within iD and Studio, we've tried to avoid as statements so that it's easier to track down code across the whole codebase. Minor nitpick, and not necessarily something you want to adopt, but I think it's worked well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'd like to avoid as too, but the problem is that without namespaces, some of the exported names become awkward, and I wanted each file to export names that will end up in the namespace. See a397b63 for how the Browser module looked, for example. Maybe we'll want to migrate to new names, but in a breaking way, in future.

@@ -15,7 +15,7 @@ import {wrapNum} from '../../core/Util';
* [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
*/

export var Base = {
export var Base = extend({}, {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why extend an empty object here here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind that, I was confused by eslint throwing an error like A function with a name starting with an uppercase letter should only be used as a constructor new-cap, but did not notice it was due to misuse of the Bounds constructor

@@ -29,7 +29,8 @@
"test": "jake test",
"build": "jake build",
"release": "./build/publish.sh",
"rollup": "rollup src/Leaflet.js -f umd -n L > bundle.js"
"lint": "node_modules/.bin/eslint --fix src/",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node_modules/.bin is not necessary in package.json scripts — it's in path automatically.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somehow my Linux setup doesn't work like that. 😩

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@IvanSanchez so eslint doesn't work but rollup and jake do? That's weird.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my case, I put rollup and jake manually in my $PATH because I was tired of running them locally (but they have to be updated/upgraded separately). Not so with eslint.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well this is weird, it shouldn't be like that... But I'm sure others had a problem like this, should be googleable...

@nathancahill
Copy link
Member

I have the same experience. ./node_modules/.bin has to be specified to take priority over globally installed executables.


import gitRev from 'git-rev-sync'

// TODO: There should be a way to skip the git branch+rev when doing a production build

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process.env.NODE_ENV or a rollup production configuration would do the trick.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but I'll wait for @mourner's input on the issue. This will affect the build/publish.sh script.

Copy link
Member Author

@mourner mourner Oct 20, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it? As far as I see, gitRev is only needed for the L.version property and copyright comment, and publish.sh takes the clean semver version from package.json.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mean is that publish.sh will probably need a third rollup config file, in order to skip the git rev.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can either set an env var (read from the config), or just leave git rev in the copyright and L.version — no big deal.

@@ -30,7 +31,7 @@ export {Class} from './core/Class';

import {Evented} from './core/Events';
export {Evented};
export var Mixin = {Events: Evented.prototype};
// export var Mixin = {Events: Evented.prototype};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accidental? I did this for backwards compat.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mourner I was not aware this was a backwards-compat issue, sorry 'bout that :-(

@IvanSanchez
Copy link
Member

I tried to make some clever use of rollup in the browser, but hit a lot of roadblocks. Thus, rely on npm run-script watch or yarn run watch for the time being. The script includes a hack in the JS banner so devs are reminded to use a fresh bundle.

@mourner
Copy link
Member Author

mourner commented Oct 21, 2016

Let's rebase to fix the Canvas conflict, and also maybe ditch all uses of as in favor of importing namespaces (which, as @IvanSanchez showed, are still neatly unrolled by rollup).

@IvanSanchez
Copy link
Member

@mourner Rebased. I'll take a break from this branch, and will let someone else rewrite the named DomUtil, Util and DomEvent imports.

@mourner
Copy link
Member Author

mourner commented Oct 25, 2016

@IvanSanchez BTW what's up with the unrunnable Path tests?

@IvanSanchez
Copy link
Member

@mourner L.Path is no longer exported (because it should be something like an un-instantiable abstract class), thus the tests involving new L.Path cannot work.

Maybe we should export L.Path as well, but I'm unsure.

@IvanSanchez IvanSanchez force-pushed the rollup2 branch 2 times, most recently from 792e3ea to 1b124f0 Compare November 3, 2016 08:42
@IvanSanchez IvanSanchez force-pushed the rollup2 branch 2 times, most recently from 1a686a6 to addaa88 Compare December 23, 2016 11:56
@IvanSanchez
Copy link
Member

After some rebasing hell and some fiddling with the VML code, the IE8 builds work again.

@@ -5,7 +5,7 @@
<div class="announcement">Jan 23, 2017 — <a href="http://leafletjs.com/2017/01/23/leaflet-1.0.3.html">Leaflet 1.0.3</a>, a bugfix release, is out.</div>

<p>Leaflet is the leading open-source JavaScript library for mobile-friendly interactive maps.
Weighing just about <abbr title="33 KB gzipped &mdash; that's 123 KB minified and 218 KB in the source form, with 10 KB of CSS (2 KB gzipped) and 11 KB of images.">33 KB of JS</abbr>,
Weighing just about <abbr title="38 KB gzipped &mdash; that's 133 KB minified and 378 KB in the source form, with 10 KB of CSS (2 KB gzipped) and 11 KB of images.">38 KB of JS</abbr>,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoa, is leaflet-src.js really 378kb?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because we added docstrings to everything, and that kinda doubles the size of the file.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I forgot about that :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

per@linfro:~/Documents/Projects/Leaflet$ npm run build

> leaflet@1.0.3 build /home/per/Documents/Projects/Leaflet
> jake build


Bundling and compressing 67 files...
	Uncompressed: 372.64 KB (new)
	Saved to dist/leaflet-src.js
	Compressed: 141.92 KB (new)
	Saved to dist/leaflet.js
	Gzipped: 37.97 KB

@IvanSanchez IvanSanchez changed the title WIP: ES6 modules & Rollup ES6 modules & Rollup Jan 25, 2017
Copy link
Member

@perliedman perliedman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reviewed this by actually trying to use this branch in a pretty large Leaflet based application. The application uses quite a few plugins as well.

From what I can tell, this branch now works with this application, I can't notice any differences between this an Leaflet 1.0.2, which we used before.

I think we need a more proper code review as well, but this at least indicates there are no obvious gotchas if we merge this.

Copy link
Contributor

@simon04 simon04 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding index.js files to subdirectories/namespace allows to streamline the import/export in the main file. I commented on a few lines with specific suggestions.

} else if (typeof define === 'function' && define.amd) {
define(L);
}
export {Control, control};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a file ./control/index.js and doing the preparation of Control/control there would streamline the code in Leaflet.js down to

export {Control, control} from './control';


console.log('Bundling and compressing ' + files.length + ' files...');

var copy = fs.readFileSync('src/copyright.js', 'utf8').replace('{VERSION}', version),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file copyright.js can be removed.

import {TouchZoom} from './map/handler/Map.TouchZoom';
Map.TouchZoom = TouchZoom;

export {Map, createMap as map} from './map/Map';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a file ./map/index.js and doing the preparation of Map there would streamline the code in Leaflet.js down to

export {Map, map} from './map';

import {TileLayerWMS, tileLayerWMS} from './layer/tile/TileLayer.WMS';
TileLayer.WMS = TileLayerWMS;
tileLayer.wms = tileLayerWMS;
export {TileLayer, tileLayer};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a file ./layer/tile/index.js and doing the preparation of TileLayer/tileLayer there would streamline the code in Leaflet.js down to

export {GridLayer, gridLayer, TileLayer, tileLayer} from './layer/tile';

GeoJSON.latLngsToCoords = latLngsToCoords;
GeoJSON.getFeature = getFeature;
GeoJSON.asFeature = asFeature;
export {GeoJSON, geoJSON, geoJson};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add the static functions to the GeoJSON class in ./layer/GeoJSON.js? Or prepare and export a GeoJSON_with_static_functions object in ./layer/GeoJSON.js?

CRS.EPSG4326 = EPSG4326;
CRS.Simple = Simple;

export {CRS};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a file ./geo/crs/index.js and doing the preparation of CRS there would streamline the code in Leaflet.js down to

export {CRS} from './geo/crs';


// geo/projection

import * as Projection from './geo/projection/Projection';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename Projection.js to index.js to comply with other suggestions and the node.js module import.

export {Icon};

export {DivIcon, divIcon} from './layer/marker/DivIcon';
export {Marker, marker} from './layer/marker/Marker';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Combine all markers in ./layer/marker/index.js and export them here all at once:

export{Icon, icon, DivIcon, divIcon, Marker, marker} from './layer/marker';

export {Circle, circle} from './layer/vector/Circle';
export {Polyline, polyline} from './layer/vector/Polyline';
export {Polygon, polygon} from './layer/vector/Polygon';
export {Rectangle, rectangle} from './layer/vector/Rectangle';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Combine all vector layer classes in ./layer/vector/index.js and export them here all at once:

export{Renderer, Canvas, canvas, SVG, svg, Path, CircleMarker, circleMarker, Circle, circle, Polyline, polyline, Polygon polygon, Rectange rectangle} from './layer/vector';

@IvanSanchez
Copy link
Member

@simon04 You make a few good points! As far as I'm aware, Rollup (et al) can tree-shake better if the modules are not assigned, but just imported/exported.

However, right now I think it'd be better to just merge this as it is, and worry about cleaner ES6 module exports later.

As long as the ES5 bundle works exactly the same, all that can be postponed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Switch to a ES6 module system with Rollup
7 participants