-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Bundling pre-ES6 modules #45
Comments
I've gone back and forth on this. For the time being I think it's better to focus solely on ES6 modules, at least until we iron out all the kinks, but long term we definitely need a sane way to handle legacy module formats. In theory, it can be done by overriding the var myLib = exports;
myLib.foo = 'foo';
augment(myLib);
function augment(lib) { lib.bar = 'bar'; } ...so a strategy of wrapping rather than rewriting is probably better. Luckily @guybedford has spent a lot of time thinking about these problems, and has done some initial research on incorporating Rollup's strategy inside JSPM: systemjs/builder#199. This seems like the best route forward, since JSPM is a soup-to-nuts solution (repository, bundler, module loader polyfill etc all talking the same language) that already handles legacy formats. For right now though, my workaround is to have a two-phase bundling process, exactly as you suggest. First, I bundle my app code (plus any npm depedencies that expose |
Actually, @callumlocke, if you want an to import lodash into your ES6 project, you can just include the ES6 build of lodash. On npm it's called lodash-es. It has a "jsnext:main" field defined, so it should be ready to go. I've been able import it in my bundles, and once #47 is approved, the only hiccup I've encountered with it so far should be resolved. |
I'm pretty confused, does this replace Webpack? Thanks! |
The wiki says it better than I can. |
That sounds good! Just to clarify, with this rollup-then-browserify approach, would your source code contain contain a mixture of |
@callumlocke you could, though more likely you'd write your app code all as ES6... import external from 'third-party';
import foo from './my-utils';
/* ... *. ...and create a CommonJS bundle with Rollup, specifying which modules are external, then hand it over to Browserify: rollup --input main.js\
--format cjs\
--external third-party,other-third-party\
--output tmp/bundle.js
browserify tmp/bundle.js > dist/bundle.js That's what I do, anyway! |
Ahh ok, I did try that at first, but rollup errored out when it failed to resolve a third party import (which didn't have a So basically the |
@callumlocke Yes, that's correct. What Rollup can't pull into the bundle will be "imported" using the idioms of the target module format. // ES2015
import * as bar from './bar.js';
// CommonJS
var bar = require( './bar.js' );
// AMD
define(['./bar.js'], function (bar) {
...
});
// IIFE, use the `globals` option to tweak global names
(function (bar) {
...
)(bar); And |
Hello @Rich-Harris (and others), Thank you for rollup. I'm using it in two of my new project. Related to this issue. I'd really like to use rollup to pull in a few small helper utilities when building for the browser (for example a I should add while I love JSPM for building apps I don't want to use JSPM (or Browserify) for building modules at this point. Rollup is much cleaner. Edit: I also saw what you were doing with the *-jsnext repos. |
Could there be a new option like |
Importing an individual method using e.g. Looks like this is because the module doesn't |
@callumlocke If you look, the main entry point for the lib just imports a bunch of smaller modules. What I've been doing (if I don't want to import the entire gigantic library) is just import the modules I want:
It's been working great for me. |
Depending on how many helpers we're talking about, copy and paste might be the best option, if it's the difference between being able to keep the bundle entirely inside Rollup and having to pass it off to Browserify for a second step. Though I'm still wondering about the possibility of somehow transforming CommonJS modules into ES6 during the transformation phase. Long-term though, the best option is to start creating ES6 modules and open sourcing them! 😀
Maybe... with Esperanto (Rollup's predecessor) I found that being permissive caused more problems than it solved (e.g. if you got the wrong number of {
entry: 'app.js',
format: 'cjs',
external: (function () {
var dependencies = require( './package.json' ).dependencies;
return Object.keys( dependencies ).filter( function ( name ) {
return !require( name + '/package.json' )[ 'jsnext:main' ];
});
}())
} An option would be simpler.
That's a shame. It would be great to be able to use lodash as a 'true' ES6 module and get the tree-shaking benefits without having to remember (or lookup) the fact that
It's more a limitation of static analysis in as dynamic a language as JavaScript, unfortunately. @timshannon FYI in more recent versions of Rollup, if you have lodash installed as an npm module, you can do this if it's easier: import findIndex from "lodash/array/findIndex"; |
This is how I've been doing it. Until lodash handles is exports properly. |
For those interested, here is the test case I whipped up to test rollup with On Mon, Oct 12, 2015 at 11:33 AM, John-David Dalton <
|
@jdalton Awesome! It might be as straightforward as creating a similar build that cuts everything except the export block from I could try throwing a @megawac Thanks. It's likely that the example-main.js version is pulling in all the extraneous clutter because of everything that needs to get added to |
@Rich-Harris nice snippet, I'll use that at some point. I can see the problems with doing it magically. An alternative would be for rollup not to error out straight away, but carry on building the import tree, and then throw one big error at the end with a message listing all of the imports that failed due to a lack of jsnext:main. Then I could just copy and paste this list into my own |
I'll try adding extra |
We're making some progress here as of the 0.20.0 release and the rollup-plugin-commonjs! |
I've updated the es branch. |
That's beautiful! 😄 |
@jdalton nice commit! I've done some experiments, and it is now possible to import individual functions using the nicer syntax: But it looks like rollup's tree-shaking doesn't work with this at all – you still need to import from individual function files if you want an optimal bundle. Not sure if this is a problem with rollup or a problem with the way lodash-es is organised. @Rich-Harris? The following experiments were done with Importing from lodash-es mainimport {chunk, zipObject} from 'lodash-es';
console.log(zipObject(chunk(['a', 'b', 'c', 'd'], 2))); Result: ~429kB Importing from categoriesimport {chunk, zipObject} from 'lodash-es/array';
console.log(zipObject(chunk(['a', 'b', 'c', 'd'], 2))); Bundle size: ~92kB Importing individual functionsimport chunk from 'lodash-es/array/chunk';
import zipObject from 'lodash-es/array/zipObject';
console.log(zipObject(chunk(['a', 'b', 'c', 'd'], 2))); Bundle size: ~4kB |
Excited about the new Lodash build. I haven't tried it yet, but my hunch is that unnecessary code is being included because Rollup is being overly cautious about side-effects (see #179). If that's the case, this gives us a powerful incentive to get that issue resolved... |
Maybe further down the dep chain. |
I've updated the es branch to work with Babel 6. |
Is there something I can do with how I generate lodash-es modules to make tree-shaking easier? |
@jdalton Finally had a chance to play around with this a bit more. The tl;dr is that for the time being, Lodash users should continue to import individual files: import zipObject from 'lodash-es/array/zipObject.js'; There definitely isn't a silver bullet in terms of small changes to Lodash that would no longer trigger Rollup's over-eager side-effect detection. There are lots of small things like this... /** Used to resolve the decompiled source of functions. */
var fnToString = Function.prototype.toString;
/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;
/** Used to detect if a method is native. */
var reIsNative = RegExp('^' +
fnToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
.replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
); ...that add up to a lot of 'unnecessarily' included code. (In this case, even though Rollup is smart enough to realise that calling Beyond that, side-effects multiply – once Rollup thinks it needs to include one chunk of code, that has cascading effects in terms of what other bits of code it thinks it need to include. It's possible to be a lot more ruthless, but not without risking breaking things. So while there are things that Lodash could do to make things marginally easier static-analysis wise, I'm hesitant to suggest them – they would probably involve rather large changes and it still wouldn't be as efficient as importing directly from individual files. Having said that, most of the false positives appear to fall into two or three categories. I'm very hopeful that we'll be able to introduce some much more sophisticated static analysis that would make Rollup much less over-cautious:
The good news is that Lodash is an absolute goldmine in terms of battle-testing some of these theories. |
Thanks for digging in! All of this side-effects detection is way more complicated than I thought the rollup detection would be. Is there a way to just detect In the case of lodash-es |
Perhaps custom compiler metadata could be provided with an option or hook On Thu, 31 Dec 2015 08:51 John-David Dalton notifications@github.com
|
I like Guy's idea here. While we can try to statically analyze to determine
|
That's actually how early versions of Rollup worked – it wouldn't even load a module until one of its exports was referenced by code that was directly reachable from the entry point. Unfortunately that turns out to be problematic in cases like this – the
It's certainly an idea worth exploring. For a brief moment there was an |
The lodash-es case is probably as safe as you're going to get. In the meantime there's always babel-plugin-lodash to assist. |
hey @jdalton, what's the latest status of lodash-es and babel-plugin-lodash? What's the best practice at the moment for bundling lodash with rollup for clientside code (if you want to make sure only a minimal set of lodash functions make it into the bundle)? |
@callumlocke lodash-es and babel-plugin-lodash are trucking along. The FAQ covers es6 to es6 transforms but if you'd like to contribute an example working with rollup that would be great. |
Going to close this as it's digressed a long way from where it started – bundling CommonJS now works reasonably well in most cases and there are other issues relating to tricky tree-shaking cases. |
Hi Rich. This project looks very exciting. I'm sold.
I have a question about pre-ES6 modules... I tried importing lodash as an experiment (today's lodash), and got this error:
Is this a permanent design decision or is it just early days? Will you ever add functionality that can read an old-style
main
script and traverserequire()
calls...or do you recommend another strategy for people who want to mix ES6 and old school modules in one bundle (e.g. using Browserify as another build step after rollup)?The text was updated successfully, but these errors were encountered: