-
-
Notifications
You must be signed in to change notification settings - Fork 15.3k
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
Add ES Modules build #1369
Add ES Modules build #1369
Conversation
cc @jdalton too because wondering what Lodash stance on this is |
Lodash does export ES modules. |
😮 I had no idea, thank you! |
There's a lot of duplication in your |
If we use
Haha, I assumed |
How much lodash do you use? Can you include the bits you do into the bundle, or do without entirely? |
Yes, definitely.
That’s how it used to be but I don’t want to maintain |
Wouldn't the right approach here be to use Realistically you're going to need both of those anyway if you want to be able to import e.g. React. |
Tree-shaking for lodash here is a red herring, since you need to do the deep imports anyway to get just the parts you need for the CJS build. |
Dropping
Can you expand? I’m not sure I get what you are referring to. I just want Sure, React doesn’t support |
I mean that the user needs to do e.g. https://github.com/rollup/rollup-plugin-commonjs#usage. Something like: plugins: [
npm({
jsnext: true,
main: true,
}),
commonjs({
include: 'node_modules/**',
}),
] |
I understand. However this is a workaround. It’s necessary for libraries like React, but we don’t have to also provide only CommonJS build just because this workaround exists. The code with Rollup CommonJS plugin will be ugly because it defeats the whole purpose of Rollup, which is to use ES modules until the very end. With that solution, what you’ll end up with is a build that contains both our CommonJS interop code (generated by Babel) and Rollup’s CommonJS interop code (generated by Rollup). There is no point in doing that if we can fix it. |
I think we might be talking past each other here. My understanding of what will happen in this case is that Rollup will use It will use the CJS build for lodash, but there wouldn't be any Babel interop helpers there. |
I had a look into my own question, as you say - you only use isPlainObject, and only twice.
I think it comes down to whether these checks are intended to catch common mistakes, or enforce some sort of rule. Personally I'd skew towards the former - and then you should be able to do without the strong How good is the rollup interop, can an ES native module still depend on a small commonjs module? would using https://www.npmjs.com/package/is-plain-object help at all, or is this equivalent to using the lodash direct import?
|
I would like to not debate depending on Lodash in this issue. It was discussed a couple of times before. I don’t want our experimental ES module bundling problems to dictate which third-party modules we use or not—this looks like a separate issue to me. (Please feel free to file an issue if you’d like to revisit any of these decisions, but they are irrelevant to providing ES modules bundle, IMO.) |
If you really wanted to, you could just add a Babel transform that rewrites the lodash imports, for use in the ES2015 module build. I don't think that's worthwhile – in practice, users are going to need to add the rollup CJS shims to work with anything else in the JS ecosystem, so you're not really saving anybody any effort. Unless I'm missing something, they'll be getting the full benefit of ES2015 modules for Redux anyway – they just won't transitively get it for lodash as well. |
Yep, lodash offers an es build. Rollup still can't natively tree shake it so you'd have to use the explicit module path to similar to what you're doing now. We have a babel plugin for converting es6 imports to node-style module paths (so you don't have to). |
Now that I look at it, @taion is right! export default {
plugins: [babel(), npm({ jsnext: true }), commonjs({ include: 'node_modules/**',})]
}; Rollup is able to pick up Redux source code (and only import the relevant file) but it falls back to CommonJS for Lodash in this case. |
You don't need an explicit |
Anyway, in principle it's not ideal to require the user use the CommonJS plugin, but in practice it won't matter for some time, since that CommonJS plugin is a de facto requirement to accomplish anything useful. This is how I have the ES2015 module builds set up for React Router and for history, and so far nobody's complained to me about it, though I also don't know if anybody's actually using said builds. |
Cool! You need anything else from me? If you use lodash-es I believe the path becomes: import isPlainObject from 'lodash-es/isPlainObject' |
In this case Redux is using |
We don’t @jdalton, we are good with using |
Right, but IMO it’s good to set example for other libraries. Lodash does offer an ES build, and I think so should we. |
I meant transitively for the lodash dependency. If you unconditionally use |
"build": "npm run build:lib && npm run build:umd && npm run build:umd:min && node ./prepublish", | ||
"prepublish": "npm run clean && npm run check:lib && npm run build", | ||
"build:commonjs": "cross-env BABEL_ENV=commonjs babel src --out-dir lib", | ||
"build:es": "cross-env BABEL_ENV=es babel src --out-dir es", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quick question @gaearon, what (if anything) are you defining under BABEL_ENV=es
? Or is it a placeholder for stuff to come or an indicator of the mode? Thanks :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just left it for consistency so I always specify either that, or commonjs
. Indeed it is not currently used explicitly anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good stuff :) thanks for the clarification!
On Fri 5 Feb 2016 at 23:51 Dan Abramov notifications@github.com wrote:
In package.json
#1369 (comment):"check:examples": "npm run build:examples && npm run test:examples",
- "build:lib": "babel src --out-dir lib",
- "build:umd": "cross-env NODE_ENV=development webpack src/index.js dist/redux.js",
- "build:umd:min": "cross-env NODE_ENV=production webpack src/index.js dist/redux.min.js",
- "build:examples": "babel-node examples/buildAll.js",
- "build": "npm run build:lib && npm run build:umd && npm run build:umd:min && node ./prepublish",
- "prepublish": "npm run clean && npm run check:lib && npm run build",
- "build:commonjs": "cross-env BABEL_ENV=commonjs babel src --out-dir lib",
- "build:es": "cross-env BABEL_ENV=es babel src --out-dir es",
I just left it for consistency so I always specify either that, or
commonjs. Indeed it is not currently used explicitly anywhere.—
Reply to this email directly or view it on GitHub
https://github.com/rackt/redux/pull/1369/files#r52088759.
Awesome! Thanks for making this happen @gaearon and everyone 🎉
There's probably no objectively correct answer. I've argued elsewhere that libraries should expose a bundled version rather than individual files, for various reasons (quicker builds for consumers, increased portability, greater resilience in the face of an evolving ecosystem), but it certainly depends on circumstance. As @jdalton and I have discovered, tree-shaking isn't a perfect science, so in some cases it makes sense to offer individual files (though I think Redux is very 'tree shakeable'). Either way, Rollup and Webpack 2 users can now create the leanest possible Redux apps with no headaches, and I'm excited about that. FWIW I do think rewriting <script src='https://cdn.jsdelivr.net/react/0.14.7/react.js'></script>
<script src='my-app-with-everything-except-react.js'></script> |
Easy to do so I did that in #1372, will release in 3.3.1. |
What were the issues with tree-shaking with lodash-es? Trying to do some due diligence here. Also, what approach are people using with webpack 2 support? Do you just point users to (Sorry to necro a dead thread.) |
If you do /** Used to detect if a method is native. */
var reIsNative = RegExp('^' +
funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
.replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
); ...which, while irrelevant to the bits that you imported, look as though (from a static analysis point of view) they might have global side-effects – unless we can somehow guarantee that the Importing from individual files ( |
Got it. That makes sense. It sounds like this can only come up in the context of exporting a bundle as the ES module build, then – i.e. if I just provide the source modules, and my main entry point only re-exports from other modules (like https://github.com/reactjs/redux/blob/master/src/index.js and https://github.com/reactjs/react-router/blob/master/modules/index.js), then this is not a concern? Or am I missing something there? |
If I understand correctly, yes. It's purely down to what |
I'm still missing something – it looks like |
Ah, sorry – missed the part about the main entry point. If you have a statement like this in the entry file... export { default as foo } from './foo'; ...then apart from it not creating a local binding in that file, it's essentially the same as this: import foo from './foo';
export { foo }; So if a consumer wanted to import |
Ahh! That makes sense, but it's quite unfortunate. I don't suppose there are any proposals to add anything like passthrough re-exports? babel-plugin-lodash is great, but I'd really like for users to be able to able to just do |
Rollup actually used to work that way, but it caused problems in some cases (admittedly edge cases). I think we'd be open to revisiting it under an option, though we briefly tried (with an |
Sorry, I don't mean that this is an issue in Rollup – the way you described the semantics of the spec make sense (though they're clearly not optimal for the "re-export a bunch of stuff in entry point" use case). I mean that I wish there were something like a export foo from './foo' as passthrough or whatever that would be specifically geared for this style of re-exports, which that importing |
@Rich-Harris BTW, do you have a blog post or other explanation of this phenomenon that can be used as a reference? |
@taion no, but I would like to! |
Can someone post a Webpack v2 config which deals correctly with these es6 modules? We somehow need to let a basic Babel run over these imported ES modules, right? Do you implement this with a dual Babel setup? a) local files with e.g. react plugins - b) dependent files with just es2015 modules translation. |
Webpack 2 understands |
Perfect. Thanks for letting me know. |
Addresses #1042, #1327.
This is intended to fix the Rollup issues by fixing
jsnext:main
to point to the special build in thees-modules
folder. That build is identical to the CommonJS build except that it doesn’t include thetransform-es2015-modules-commonjs
Babel plugin.However we have a problem here. We started using Lodash 4.x recently. Lodash modules are CommonJS builds and offer no ES6 equivalents. Therefore, even with this PR, if you try to use Redux in a Rollup project with
npm({ jsnext: true })
, you will see the following error:This would be a non-issue if Lodash offered an ES modules build of its modules. For example,
lodash/es-modules/isPlainObject
that would be identical tolodash/isPlainObject
but used ES Modulesimport
andexport
instead of CommonJS.Another possible fix is to give up on offering ES modules with Babel build, and instead build a separate ES6 single file bundle using either Webpack or Rollup itself.
I welcome comments and discussion around this because I really want to fix things for Rollup users on our end.
cc @Rich-Harris @dchambers @taion @flying-sheep