browserify / browserify

browser-side require() the node.js way
http://browserify.org/
MIT License
14.56k stars 1.19k forks source link

Add support for ES6 import syntax #1186

Open romeovs opened 9 years ago

romeovs commented 9 years ago

This might be a long shot but I'd like to see this in browserify.

The ES6 module syntax has a few nice features compared to commonJS require method, namely bindings and cyclic dependencies.

Since these things are in the ES6 standard it seems reasonable to work them into browserify too!

Note that just using a ES6 to ES5 transpiler wouldn't work in this case because these are cross module concerns. For instance, babelify is only able to transpile import foo from 'foo' to var foo = require('foo') at file-level, but this does not allow cyclic dependencies.

The Esperanto project has a nice way of handling things but lacks too many features to be a valid replacement for browserify.

Is there any possibility to see support for ES6 import syntax come to browserify?

ghost commented 9 years ago

I've been waiting for this feature to land in node first so browserify can match the semantics properly. I'm also concerned that if browserify implements a default es6 import mechanism while node doesn't have one, it will needlessly fragment browser and server ecosystems.

romeovs commented 9 years ago

Yes, my approach to writing modules is currently to write them in ES6 but only expose ES5-compliant code (transpiling everything on npm publish for example).

I get your concerns, and they are indeed important ones.

How feasible would you say it is to expose this as a plugin for browserify (I'm currently looking into writing that).

romeovs commented 9 years ago

I've created a small gist with a proof of concept for a plugin. I'll start working on it!

nmn commented 9 years ago

@romeovs You can already use a transform like babelify to get ES6 imports. With both Node and Browserify. You can turn off the other features if you don't want them.

romeovs commented 9 years ago

@nmn but do these imports with babelify have the complete ES6 semantics? As far as I can see they just transform the import statements into require statements, but require and import have different semantics with respect to bindings and cyclic dependencies.

The problem is really the way browserify concatenates the imported files. esperanto does this correctly, but lacks other features that make browserify a much better choice.

romeovs commented 9 years ago

@nmn after some testing I can see that it does!

fregante commented 8 years ago

I think @substack is right in waiting for proper node support. browserify brings node to the browser; if node doesn't support import, browserify shouldn't either.

If babelify doesn't follow the right semantics, raise the issue with them or ask for an esperantify plugin.

flyingrobots commented 8 years ago

How about now?

yoshuawuyts commented 8 years ago

It appears a good deal of fragmentation will start occurring because of d3@4.0.0 relying on es6 imports. It's not seen an official release yet, but given the buzz around its components I think it's already becoming somewhat of an issue.

I know that Node doesn't support ES6 imports quite yet, but arguably browserify is in a somewhat different spot since it doesn't support dynamic imports (or at least I don't think it does out of the box).

The reason why I think we should consider options is because if other loaders become popular because of only a single feature, it might signal there's a need for it.

I'm not quite sure what the correct resolution is; for the package I'm writing that consumes d3 I might include a compiled build (breaking npm reliance) or include babelify (making the package very heavy) - though neither is ideal. Perhaps an importify transform needs to be written, or perhaps this should be resolved at the browserify level - I'm not sure.

For now I'm posting this here to signal that changes are happening, and we'll probably need to think of a solution for this 😕

edit July 2016: nope nope, we shouldn't move ahead on import unless Node does so (which it frankly might not, so hey)

terinjokes commented 8 years ago

@yoshuawuyts Implying that d3@4.0.0 only works with loaders that support ES6 modules is a bit disingenuous. The main field in the root's package.json and the individual modules all point at Node.js compatible files (and I'm successfully using several d3 modules with Browserify just fine)

dantman commented 8 years ago

Browserify's greatest feature is the transform pipeline (heck the parts that actually break require into a dependency tree and combine that tree into a bundle aren't even in this repo, they're the module-deps and browser-pack packages) and the collection of transforms we have that make . IMHO rollup's greatest feature is not ES6 imports, but the static analysis and tree shaking it does. So even if browserify rushes to hastily implement non-babel ES6 import support before any JS engine natively supports it, I doubt that'll stop may people from using rollup.

There are already a pile of other packages using ES6 import; but they all do it alongside the use of other ES2015 features. So I expect packages like d3 that only use ES6 import is going to be a rarity. And the moment you use any other feature, you need Babel, whether you're using rollup or browserify. So I doubt "rollup lets me use packages that only use ES6 import without needing Babel" will be the reason people use rollup.

I think the problem browserify is going to have is rollup's plugins. That functionality is what turns rollup from just a tool to pack up ES6 exports/imports into something that can pass as a standalone loader.

Rather than trying to hack ES6 import in with require schematics, just as a way to bypass babel in a few cases (because if you use Babel this is already entirely possible). I think it would be better to accept the area that rollup is better at (tree shaking ES6 imports) and find a way to break up rollup and create a rollupify plugin that will integrate rollup's tree-shaking and also act as an optional ES6 import handler for those that don't want to use babelify.


It's a different topic. But I think browserify's other undoing may be how easy it I've seen it is to make a simple transform but hard it is to make one that correctly preserves source maps. I don't see any easy tools for source-map aware string manipulation. And while I'd love to transform using an AST, that's not viable when each transform has to source -> parse to AST -> transform -> AST to source -> source instead of sharing an AST. At this point, in my own code, I can't tell if my source maps work or not anymore and I have no clue which piece might be at fault for messing with them.

jokeyrhyme commented 8 years ago

FYI: there's an enhancement proposal for Node.js being discussed here regarding ES2015 modules:

gregtatum commented 7 years ago

Coming in to say I'm actively getting issues because d3's latest version is only using ES6 syntax. I can't use my browserify tooling now. The time seems ripe for this change as the ecosystem has already begun the move, otherwise I think browserify will have lost its relevance in this increasingly competitive bundling space.

jmm commented 7 years ago

Hello @TatumCreative. D3's README says their releases support CommonJS. Is that not the case? Even so, there are multiple tools for compiling ES6 module syntax to CJS -- can you not use one of those?

gregtatum commented 7 years ago

Hmm... Yeah my bad. It looks like user error on my part. Sorry about that. My opinion still stands though. Three.js just landed a big patch to go the ES6 route as well. Personally I'm in a weird position as I tend to primarily target one browser and I want what Browserify does with bundling without the side-effect of mangling my ES6 code and turning it into ES5 code.

I can probably come up with a solution if I do more research, but regardless I still feel it is important to support ES6 imports sooner rather than later, as the community is already adopting it in projects. I feel like fragmentation is happening as people have moved on to other bundling solutions, which I would really not have to do, as I really love the browserify tooling ecosystem. All that said, I probably don't have the time to contribute to this project to make it happen, and I appreciate the work everyone does here!

terinjokes commented 7 years ago

@TatumCreative I'm glad that you were able to correct this issue. The contributors to Browserify, including myself, understand your frustration.

Browserify has always been a way to use Node.js-compatible modules in the browser. As a result, Browserify has always looked to Node.js for how to implement the module resolution and loader. Right now Node.js doesn't support ES6 imports; there are blockers in other parts of the stack. We don't yet know the final algorithms or behaviors.

While slower than we would all like, these efforts are moving ahead. I'm excited for the day! Once Node.js has implemented ES6 imports and exports, we can add support to Browserify.

You may wish to opt-in to a possible future, and Browserify's plugin system allows you to do so. But it's not the place for us to make that decision for everyone.


As an aside, there's probably still a few improvements to Browserify to make using standardized ES6 a little more comfortable, even without imports and exports. I'm happy to take a look at those issues. 💖

jmm commented 7 years ago

@TatumCreative Ok, no problem, thanks for the feedback.

Personally I'm in a weird position as I tend to primarily target one browser and I want what Browserify does with bundling without the side-effect of mangling my ES6 code and turning it into ES5 code.

I understand. That's not that weird of a position, as people increasingly want to selectively transpile parts of ES6. For example, only transpile the parts not natively supported by recent versions of Node. ES6 has no native module bundling story anyway, so even if a browser supported ES6 100% you might still want to bundle them via transpiling just module syntax to ES5.

I think there's probably a solution for you. If I'm not mistaken some people run Rollup to convert module syntax before carrying on with bundling with Browserify, or you could run Babel (Babelify) with just the module transform enabled. With Rollup you could get the benefit of tree shaking as well.

but regardless I still feel it is important to support ES6 imports sooner rather than later, as the community is already adopting it in projects.

Like other people have mentioned, the community is adopting the syntax and speculating about what behavior will be standardized. So it's not necessarily all smooth sailing to implement it now. For example, from version 5 to 6 the core Babel CommonJS module transform made a big change in its output. If you look at the issue linked in the comment before yours up above you'll see that the discussion about how to implement ES modules in Node is epic and very complex.

I feel like fragmentation is happening as people have moved on to other bundling solutions

I think that has more to do with other factors than this.

I appreciate the work everyone does here!

Thanks!

gregtatum commented 7 years ago

Thanks @jmm and @terinjokes! I think that is very well reasoned, and I agree with where you all are coming from. I'll probably end up adding another tool to help me deal with this until all of this gets sorted out on the node end. I can deal with a little bit more tooling complexity on my end :) I'm almost afraid to read that node module discussion, lol.

Keep on rocking it! 😀

gregtatum commented 7 years ago

For anyone coming into this thread, the browserify transform rollupify will take all of your ES6 import statements and hoist them all into a single module scope.

gkatsanos commented 7 years ago

I am assuming I can't use ES6 style imports with browserify and babelify still?

yoshuawuyts commented 7 years ago

You can use babelify and things will work - it's still not recommended though, given that Node has no support for it

On Wed, 4 Jan 2017, 15:33 George Katsanos, notifications@github.com wrote:

I am assuming I can't use ES6 style imports with browserify and babelify still?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/substack/node-browserify/issues/1186#issuecomment-270383970, or mute the thread https://github.com/notifications/unsubscribe-auth/ACWleq1joqUYnVh2Zr91XLw-dGrSngm4ks5rO62pgaJpZM4D4Vtf .

gkatsanos commented 7 years ago

I use browserify in Gulp.. does this change things?:)

yoshuawuyts commented 7 years ago

Nope

On Wed, 4 Jan 2017, 15:45 George Katsanos, notifications@github.com wrote:

I use browserify in Gulp.. does this change things?:)

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/substack/node-browserify/issues/1186#issuecomment-270386851, or mute the thread https://github.com/notifications/unsubscribe-auth/ACWlem6m1eKTQn1jgG2Br7bqWwhgBbCFks5rO7BtgaJpZM4D4Vtf .

gkatsanos commented 7 years ago

What does "not recommended" mean? The end result seems to work? Should I just drop gulp/browserify and use webpack instead?

yoshuawuyts commented 7 years ago

I mean that Node doesn't have support for import and last I checked parts of the spec are still not done and might change even. If you want things to work everywhere, today and in the future, require is a more stable choice. I don't want to debate anyone on this, I'm sure other people have other views on this.

On Wed, 4 Jan 2017, 16:01 George Katsanos, notifications@github.com wrote:

What does "not recommended" mean? The end result seems to work? Should I just drop gulp/browserify and use webpack instead?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/substack/node-browserify/issues/1186#issuecomment-270391071, or mute the thread https://github.com/notifications/unsubscribe-auth/ACWlelQGOr3-AA2Jzh9voscMTuuBjzK8ks5rO7RagaJpZM4D4Vtf .

gkatsanos commented 7 years ago

Alright. From reading around it seems to me that's the basic syntax is pretty finalized and is the future but I could be wrong of course.

terinjokes commented 7 years ago

@gkatsanos the syntax is stable, but the mechanics of how to do imports (and specifically, how Node.js will do imports) are still being figured out.

Browserify is a set of tools that together bring Node.js's module resolution and loading to the browser. Since Node.js doesn't support importing ES6 modules, we have nothing to implement.

gkatsanos commented 7 years ago

@terinjokes alright, it's clear. How does browserify compare to webpack in that respect?

terinjokes commented 7 years ago

@gkatsanos Being module bundlers, Webpack and Browserify are similar. They take input, resolve a tree of dependencies, and generate a bundle (or bundles) as output.

As described above, Browserify supports only require at the moment. Webpack supports the illusion of other module types, including AMD and ES6, but the resolution algorithm is the same. If you used AMD modules expecting dependencies resolution according to AMD's rules, you'd be surprised to learn that it instead uses the Node.js require rules!

Neither project knows how Node.js will handle ES6 resolution and loading. There have been discussions of using separate file extensions or looking up a key in the module's package.json. We'll have to wait and see gets implemented.

Webpack's plugin system would likely allow them to pivot and allow developers to configure which behavior they want. Browserify prefers waiting for Node.js to decide and not have the need for configuration or the fears of breaking user applications.

TheLarkInn commented 7 years ago

@terinjokes is right, we (at webpack) leverage nodes module resolution but allow allow plugins to define other types of dependencies and resolution patterns. If one becomes obsolete (like System.import did) and drop out the plugin from core. Below is a link to our resolver if you are curious.

https://github.com/webpack/enhanced-resolve

gkatsanos commented 7 years ago

Thank you both, many things learned today.

gkatsanos commented 7 years ago

@terinjokes one follow up: today I was trying to solve the infamous 'import' and 'export' may appear only with 'sourceType: module' error, and discovered that the Bootstrap v4 JS plugins (Carousel and others) use the ES6 syntax. Is there a way around this to keep using Browserify and using ES6 -based JS modules living in node_modules? (babelify doesn't transpile node_modules)

terinjokes commented 7 years ago

@gkatsanos I'm not sure how you're importing the bootstrap modules, but both node_modules/bootstrap/dist/bootstrap.js (the "main" import, if you had just done require('bootstrap');) and node_modules/bootstrap/js/dist/carousel.js are ES5 modules.

gkatsanos commented 7 years ago

@terinjokes (referring to Bootstrap v4) : https://github.com/twbs/bootstrap/blob/v4-dev/js/src/carousel.js ... import Util from './util' export default Carousel

terinjokes commented 7 years ago

@gkatsanos If you installed bootstrap@4.0.0-alpha.5 as noted in their Quick Start you'll see that they distribute ES5 code (by default) for this reason.

gkatsanos commented 7 years ago

@terinjokes This is the code from alpha.5 : https://github.com/twbs/bootstrap/blob/v4.0.0-alpha.5/js/src/carousel.js and I believe this is ES6 modules?

terinjokes commented 7 years ago

@gkatsanos In the npm releases they distribute ES5 code. That is, they compile the ES6 to ES5 before publishing the package.

gkatsanos commented 7 years ago

@terinjokes sorry, not following. The compiled version isn't related to what we're trying to do here which is use only 1 out of the 10 Bootstrap plugins :) (a bit like how we can import lodash.debounce without loading the entire lodash library.) https://github.com/twbs/bootstrap/commit/ce2e944aa6957528f23f1f7e680ac0cb4a75dcac

terinjokes commented 7 years ago

var Carousel = require('bootstrap/js/dist/carousel');

gkatsanos commented 7 years ago

Nah, doesn't work because the Carousel module is requiring another library (with ES6 imports) - I read on it and it seems Bootstrap v4 hasn't yet made it possible for people to use plugins independently.

AurelioDeRosa commented 6 years ago

@gkatsanos it's now possible to import plugins individually but I'm not clear how to do that with browserify (the doc only mention a Webpack solution).

mattdesl commented 6 years ago

I'm curious what the current feel is on this, now that ES6 modules are in browsers (at least in some form). I've also noticed more modules on npm being published with code that works fine in Webpack/Rollup/Parcel (and other ES6-able bundlers) but does not work in Browserify.

I personally don't like import/export but if the rest of the community is using it, and browsers are adopting it, I feel Browserify should definitely follow suit, even if Node.js hasn't fully adopted it yet.

ljharb commented 6 years ago

I don't agree; until node supports it unflagged, browserify shouldn't support it.

Browsers only currently support URLs with import, and node doesn't support URLs with import.

gkatsanos commented 6 years ago

It doesn't really matter. No-one is using browserify any more. It's hanging out with its old buddies Grunt and Gulp somewhere.

mattdesl commented 6 years ago

@ljharb Makes sense.

Another option: what about allowing import syntax and just ignoring it? Or perhaps providing a warning that it was skipped? I'm enjoying the new dynamic import() support in browsers (which isn't possible to fully re-create with a bundler), but I can't use that syntax currently in browserify.

In the mean time I am considering writing a small transform to handle this transparently so users don't need to deal with babel/config/etc.

EDIT: I just realized browserify actually supports dynamic import() already, it only fails on static import/export. So maybe no need to change browserify yet.

ljharb commented 6 years ago

(dynamic import() is also available in Scripts, import is only available in Modules)

ljharb commented 6 years ago

It wouldn't be safe to treat a Module as if it was a Script - modules are in auto-strict mode, for examples, and there's a number of things that could behave differently.

mattdesl commented 6 years ago

For the mean time, I've created a browserify plugin that:

See here: https://github.com/mattdesl/esmify

mukururo commented 5 years ago

I am happy and excited with the upgrade

mukururo commented 5 years ago

Let's work together to meet my obligations to work