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

jsnext:main should point to a distributable ES6 bundle #1042

Closed
dchambers opened this issue Nov 16, 2015 · 22 comments
Closed

jsnext:main should point to a distributable ES6 bundle #1042

dchambers opened this issue Nov 16, 2015 · 22 comments

Comments

@dchambers
Copy link

It's not currently spelt out in the wiki page, but jsnext:main is apparently meant to point to a distributable ES6 bundle rather than to the source code. For example, see:

Unfortunately, Redux's jsnext:main points to its source code, not a distributable ES6 bundle. Because of that, the build process for this repo needs to transpile Redux's source code. Redux uses Babel 5, this repo uses Babel 6, causing the build to fail. These kinds of incompatibilities are why jsnext:main should not be used to point to source code.

taken from this comment.

@gaearon
Copy link
Contributor

gaearon commented Nov 16, 2015

Hmm. What is a “distributable ES6 bundle”? Something that doesn’t have import and export statements?

@ghost
Copy link

ghost commented Nov 16, 2015

@dchambers So basically what we need is to run babel on src with most of the transforms, but exclude the module transforms so that import and export remain in the transpiled src directory?

@dchambers
Copy link
Author

Hmm. What is a “distributable ES6 bundle”? Something that doesn’t have import and export statements?

Good question! I did wonder myself, but assumed it was only me that didn't quite get it :-). @Rich-Harris, can you clarify please?

@ghost
Copy link

ghost commented Nov 16, 2015

Other than that possibility, it could either mean that it needs to be bundled in to a single file (which I think defeats the purpose of Rollup, so I imagine it is not that case) or that the es7 features used in the codebase are the blocker. There isn't extensive usage of es7 but the object spread operation would probably be enough to make it "not es6 distributable"

I'm sure Rich-Harris can provide a solid answer, though.

@Rich-Harris
Copy link

Sure, I'll try and clarify – but with the caveat that this is my interpretation! Unfortunately as a community we haven't quite figured out a consensus on how to transition to ES6 modules – there's lots of room for different views on how exactly to go about it. There's a thread over here though if you'd like to participate --> jsforum/jsforum#5

'Distributable ES6 bundle' is a poorly-chosen phrase but I meant code that is runnable except for the import/export statements. I've found that packages exposing untranspiled source code causes all sorts of problems, because any consuming code needs to take care of that package's transpilation (for example, if I'm consuming Redux via its jsnext:main, I need to transpile stage 0 features). Apart from the extra work this entails (in the form of longer builds for the consuming package, etc) this causes all sorts of headaches when (for example) the consuming package is running a different version of Babel from the consumed package.

You might reasonably ask what's so special about import and export – aren't they just ES6 language features like any other? Personally I think it makes sense to draw a distinction, because import and export are about how code gets into your app or library, whereas everything else is about what it does once it's there (another way of looking at it: you can only transpile arrow functions one way, but you can transpile import and export to AMD, CommonJS, or whatever else depending on the target environment).

In an ideal world we wouldn't need jsnext:main because we'd be able to have a UMD block that encompassed all the different formats – AMD, CommonJS, browser globals, ES6 – but unfortunately import and export can't coexist with the other forms. Because of that, the only way we'll ever be able to transition JavaScript away from legacy module formats is if we have a transitional measure that allows package authors to distribute UMD and ES6 simultaneously, and jsnext:main is the best candidate I've come across yet. But if it's used to mean 'any upcoming JavaScript syntax' then I believe it creates more problems than it solves.

Apologies for the long answer, I didn't have time to write a shorter one 😉

@ghost
Copy link

ghost commented Nov 16, 2015

So it sounds like there are few options:

  1. Don't add jsnext:main
  2. In the npm publish process, compile the src directory with all but the module transforms and add jsnext:main pointing to this compiled src dir.
  3. Expose the src directory as is(es6/es7) and add jsnext:main pointing to it( which puts the burden on the consumer)

Did I miss any options?

@ghost
Copy link

ghost commented Nov 16, 2015

Also, how does this relate to #964? It seems like that PR wouldn't work without this issue being handled, right?

edit: I see now that Rollup is indeed bundling the compiled source.
Rich-Harris@78d98e8#diff-b9cfc7f2cdf78a7f4b91a753d10865a2R19

@Rich-Harris
Copy link

Did I miss any options?

The fourth option – let's call it 2b – is to create a single file containing the whole of Redux, but as an ES6 module. It would look like this: https://gist.github.com/Rich-Harris/d3269558c4cd30a3f41b This is basically the same as the current UMD build, but, well, without the UMD. There isn't really any advantage in this case to exposing a directory that mirrored the structure of src but with transpiled-except-modules JavaScript – bundling is better.

It's orthogonal to #964, which is purely about a faster/leaner UMD build. That PR is ready to go – it's entirely up to @gaearon and the other contributors whether they want to use it or not.

@taion
Copy link
Contributor

taion commented Nov 17, 2015

BTW, this is how we're considering setting things up for React Router: remix-run/react-router#2530.

By my understanding, jsnext:main should point to just another transpiled build, but one targeting ES6 modules rather than CJS modules.

@ghost
Copy link

ghost commented Nov 17, 2015

@taion Yea, that looks to be about what I was thinking would work too. To be candid about this though: I probably wouldn't have suggested adding jsnext:main if I had known/understood about this aspect of it.

@taion
Copy link
Contributor

taion commented Nov 17, 2015

I'm coming at this from the sidelines a bit - I think for a package like React Router where most users will use most of the exported things, the practical benefit of adding an ES6 build is pretty minimal. My vague understanding of Redux is that it's in the same boat.

Packages that would help their users by distributing a build in this format would be more like history or React-Bootstrap, where any given user will only use a small subset of what is exported - it's much cleaner to just do a named export from the top-level package directly, rather than e.g. reaching into history/lib just to get around the impossibility of tree-shaking when using transpiled CJS modules.

@taion
Copy link
Contributor

taion commented Nov 17, 2015

@danmartinez101

I'm curious why you'd recommend against adding that jsnext:main, though. When using Babel 5, it's easy enough to just blacklist the es6.modules transform for a separate build. It seems like an easy enough win to me.

@ghost
Copy link

ghost commented Nov 17, 2015

I don't mean that I would recommend against it; I just mean that I wouldn't have initially suggested it in reduxjs/react-redux#186 (which I now realize isn't even in this project). Basically, I have no strong opinion for or against it.

@taion
Copy link
Contributor

taion commented Nov 17, 2015

Hah. Okay. Yes, we turned down a PR on React Router to the same effect: remix-run/react-router#2523

I'd assert that reduxjs/react-redux#186 is incorrect - but there is an easy solution, one that I think as many libraries as possible should try to take.

@dchambers
Copy link
Author

I'm coming at this from the sidelines a bit - I think for a package like React Router where most users will use most of the exported things, the practical benefit of adding an ES6 build is pretty minimal. My vague understanding of Redux is that it's in the same boat.

It's not just about whether the user only uses parts of your library, it's also about whether you only use parts of the libraries you use, etc, because this benefit will be transitive.

@taion
Copy link
Contributor

taion commented Nov 20, 2015

@gaearon Do you want this set up per remix-run/react-router#2530? I can put together a PR. About to update both React Router and history to do this shortly.

@gaearon
Copy link
Contributor

gaearon commented Dec 13, 2015

So any consensus here?

@taion
Copy link
Contributor

taion commented Dec 14, 2015

IMO the best/easiest build setup available is something like what we have on the React Router and history repos. It's a little harder with Babel 6, because you can't just blacklist the module transform and instead have to make an "es2015-without-module-transform" preset, but you don't need rollup to build an ES6 module build.

@gaearon
Copy link
Contributor

gaearon commented Dec 14, 2015

Who does this benefit?

@taion
Copy link
Contributor

taion commented Dec 14, 2015

Almost nobody - in theory rollup and webpack 2 users, but you can't really bundle an application with rollup, and the webpack 2 beta isn't quite usable yet.

More importantly, the tree-shaking benefit is almost non-existent here, too, given how small Redux is.

Especially if you're looking at moving to Babel 6 soon, IMO just drop the jsnext:main and we all stop worrying about this for now :p

@gaearon
Copy link
Contributor

gaearon commented Feb 5, 2016

Please give your comments on #1369.

@gaearon
Copy link
Contributor

gaearon commented Feb 6, 2016

Fixed in 3.3.0.

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

No branches or pull requests

4 participants