Comments (19)
Interesting. So it looks like a module is "… a prototype-less object that provides read-only access to the exports of the module. All of the exports are provided as getters without setters". What is supposed to happen when there is a default export? When would I use module foo from "foo";
vs import foo from "foo";
?
from es6-module-transpiler.
To clarify, I think it would be unexpected for import fs from "fs";
to transpile to var fs = require("fs").__default__;
since you'd end up with undefined
. My understanding of why export default
was added was to address exactly this use case where someone might reasonably export a single object, like a function. I could easily be misinterpreting the spec, though.
from es6-module-transpiler.
You should be using module fs from "fs"
for the fs
case. import foo from "foo"
should only be used for modules that have default exports defined via export default [AssignmentExpression]
.
In other words:
export default [AssignmentExpression]
andimport [Identifier] from
work together;- all other
export
forms and all otherimport
forms work together; - but you cannot mix these two sets and expect it to work, e.g.
export default [AssignmentExpression]
andimport [ImportSpecifierSet] from
/module [Identifier] from
do not work together.
from es6-module-transpiler.
Concrete example: in ES6 code you will have the following.
// foo.js
export var foo = 5;
export default 10;
// bar.js
import { foo } from "foo"; // foo == 5
import bar from "foo"; // bar == 10
module baz from "foo"; // baz deepEquals { foo: 5 }
// x.js
export var a = 10;
export var b = 20;
// y.js
import x from "x"; // compilation error: x.js did not have an `export default` statement
// a.js
export default 10;
// b.js
module a from "a"; // a deepEquals {}
from es6-module-transpiler.
@domenic in your first example, you have
export qux;
this should be
export { qux };
right? the syntax doesn't seem to support exporting identifiers without the curly braces.
(not trying to be pedantic about your example, mind you, just making sure I understand the syntax correctly)
from es6-module-transpiler.
@thomasboyt good catch, thanks; will edit OP to fix.
from es6-module-transpiler.
This __default__
bit just threw me for a loop, just as @domenic suggested. I can't say that I quite agree with the spec on this one as I think quite a few people are going to be confused by this.
Regardless of my opinions of the spec on this one, this implementation has a very real impact on code that is trying to interop with non-transpiled code. Since there is no way to use arbitrary objects as modules, i.e. node's module.exports =
and AMD's function return value, a fair amount of code is going to have a difficult time being migrated.
In particular this is having an impact here: https://github.com/wycats/handlebars.js/blob/es6-modules/lib/handlebars.js#L35-38
For this pattern non-transpiled code is going to have to do something like:
var Handlebars = require('handlebars').__default__;
or
define(['handlebars'], function(Handlebars) {
Handlebars = Handlebars.default;
Both of which feel a bit painful, not to mention will break existing code. Right now the only solution that comes to mind is a non-transpiled shim that will effectively do the above redirects but this feels like it's a hack as well.
/cc @wycats
from es6-module-transpiler.
easy solution: we could add a flag to the transpiler (and grunt task), i.e. exportDefaultBare: true
(tho something more descriptive would be better), that exports the default as the object for those modules. for example, if you were using grunt-es6-module-transpiler for handlebars, your config would become:
"transpile": {
"handlebars": {
type: 'amd',
files: [{
expand: true,
cwd: 'tmp/transpiled/handlebars/',
src: '**/*.js',
dest: 'tmp/transpiled/handlebars/'
}
},
"handlebars_root": {
type: 'amd',
src: 'tmp/transpiled/handlebars.js' // root module
dest: 'tmp/transpiled/handlebars.js',
exportDefaultBare: true
}
}
sound good?
from es6-module-transpiler.
This seems like a nice stop gap for sure. I think the module vs. default confusion will still be there but that would allow the "legacy" interop without yet another buld step.
I already have a fork so I can look into making a PR here while I'm working out the config in handlebars land.
from es6-module-transpiler.
Just be aware you are not writing ES6 if you do this, and your modules will start failing to work together if you start using native module support.
from es6-module-transpiler.
My preferred strategy is to write ES6 modules as ES6 modules and translate "single imports" from AMD into default
on the way in by wrapping.
This should work reliably because we're only talking about having code written as ES6 importing code written as AMD, and not the other way around.
from es6-module-transpiler.
@domenic the original source would still work properly under a compliant engine would it not? If you have module support you would load those files, if you do not you would load the output of the transpiler. This really is an issue of trying to compile to the existing module definitions that are not quite a perfect match with how es6 handles modules.
I totally agree that things would likely blow up if a legacy loaded attempted to load the native es6 module but that is a concern for the require.js and browserify, etc projects to figure out and any solution there would need to be aware of the formal definition of default exports in es6.
@wycats the exportDefaultBare
would be on the way out. I.e. so vanilla AMD implementations that are using handlebars don't have to know to look at default
rather than the root object. It's a relatively minor thing given there are presumably few people using the amd module as it was never formally released but still an annoyance none the less.
from es6-module-transpiler.
the original source would still work properly under a compliant engine would it not?
Yes, but it would not work in the way you'd expect, causing compat problems as your entire module graph starts breaking upon upgrade. It would be syntactically valid, but semantically mean completely different things, causing errors in the places you forced incorrect semantics via this exportDefaultBare
option.
from es6-module-transpiler.
@domenic the only place that exportDefaultBare
is used is to create an AMD/CJS import-friendly version of the "index" (public-facing export) module. everything else has the exact same semantics as expected. nothing will break when
var Handlebars = require("handlebars");
becomes
import Handlebars from "handlebars";
assuming that the handlebars
module exports a single default.
from es6-module-transpiler.
@thomasboyt that's just not true. A lot of code will break; in fact, any code that uses a property of Handlebars
in your example will break, because Handlebars
does not have properties in ES6 semantics. Instead the "handlebars"
module has non-default exports.
More generally, adding exportDefaultBare
creates a body of code that does not distinguish correctly between import x from "y"
and module x from "y"
. With exportDefaultBare
's wrong semantics, you can use them interchangeably; as you showed above, people will do exactly that. Whereas with real ES6, you must use the correct one, or in some cases both. Code written with ES6 syntax (but wrong semantics), then transpiled, will break when upgraded to ES6 semantics and used without transpilation.
from es6-module-transpiler.
@domenic as @thomasboyt mentioned the whole tenant here is that you are explicitly creating "non-es6" code with this system.
I hear you on the confusion possible for users who are attempting to load the transpiled es5 module using the es6 loader. The same concern occurs with a hand-written es5 module and the es6 loader. @wycats solution seems like a reasonable one to this problem. Ideally they would switch to using the es6 source files directly but should they not do this for whatever reason then the legacy failover logic will prevent them from blowing up.
What the flag allows for is for libraries to start moving forward without fear of leaving their users behind or adding overhead to wrap the transpiled code, which puts you in the same situation as above.
from es6-module-transpiler.
as @thomasboyt mentioned the whole tenant here is that you are explicitly creating "non-es6" code with this system.
In that case I would suggest using a different syntax than the ES6 module syntax. To be perfectly clear, what will happen is:
- People will wrote code in ES6 syntax
- People will use this transpiler
- Browsers will get native support, so they will stop using this transpiler and try loading their original code directly
- Everything will break
If you are not creating ES6 code, you should not be using ES6 syntax. You are just creating a third module syntax, after AMD and CommonJS. Worse, it's one that looks confusingly like ES6 syntax, and gives users a false sense of future proofing, but is not ES6.
I hear you on the confusion possible for users who are attempting to load the transpiled es5 module using the es6 loader.
Sentences like this make me not clear you are getting my point. It's not the transpiled code that will break. It's the original source that will break. Here is an example.
// fs.js
export function readFile() { };
// myCode.js
import fs from "./fs";
fs.readFile();
This code will work when transpiled with exportDefaultBare
, becoming
// fs.js
exports.readFile = function () { };
// myCode.js
var fs = require("./fs");
fs.readFile();
but will break in ES6. In fact it will break on the first line, import fs from "./fs"
, because no default export is declared for "./fs"
.
What the flag allows for is for libraries to start moving forward without fear of leaving their users behind or adding overhead to wrap the transpiled code
If ES5 consumers want to consume ES6 modules, they should do one of two things:
- Fix their ES5 module loader to recognize exports named "default" and treat them the same way they currently treat default exports, just like ES6 does.
- Just write
var glob = require("glob").default
when they want to get the default export.
from es6-module-transpiler.
A second example of breakage:
// glob.js
export function default() { };
export function sync() { };
// myCode.js
module glob from "./glob";
glob(); // invalid in ES6; glob is not a function, it's a module instance object
Transpiles (with exportDefaultBare
) to:
// glob.js
module.exports = function default() { };
module.exports.sync = function () { };
// myCode.js
var glob = require("./glob");
glob(); // this works with exportDefaultBare, even though it doesn't in ES6
from es6-module-transpiler.
In that case I would suggest using a different syntax than the ES6 module syntax.
So you'd rather have people not use ES6 than to transition to it? That combined with libraries wanting to avoid breaking changes is a wonderful way to kill off ES6 module adaptation.
The intended users of exportDefaultBare
output would NOT be anyone using any form ES6 already. It's a migration path that maintains the API compatibility with our existing users, 100% of which are running ES5 right now. Ideally libraries would create a build for these users and then another build for users of the transpiler that sticks to the ES6 implementation. (Or the transpiler users would build in the original sources directly, I'm not clear on the packaging best practices under the transpiler world). In the case of handlebars this might look something like handlebars-amd.js
, handlebars-es6.js
, etc (This splats out to a lot of different files once you add minimization, etc but such is life)
While not ideal as there is an admitted footgun for users attempting to link to the ES5 module from code that is loading using native module support, I think that the maintainer of libraries should be given the option to create a build that continues to support their existing clients with an eye on the future.
from es6-module-transpiler.
Related Issues (20)
- esprima:harmony updates for ES6 modules HOT 13
- Add option to not generate source map and append sourceMappingURL HOT 8
- Performance hit from 0.4 -> 0.5+ HOT 13
- add support for `ImportNamespaceSpecifier` HOT 1
- Unnecessary semicolons left around in compiled files HOT 3
- support for classes HOT 1
- Import chains do not preserve bindings across multiple files in CommonJS
- some module syntax fails to parse. HOT 1
- ES6 module transpiler breaks CSP 'unsafe-eval', can't develop for FFOS HOT 3
- RequireJS transpiler HOT 1
- export { foo } from "./bar" does not work
- Module bindings are not propagated across multiple modules when using `import * as` syntax
- Using es6-module-transpiler in conjunction with a classes transpiler HOT 2
- CommonJS formatter, Browserify, ES3, and reserved-word export names HOT 2
- Include another (more up to date) broccoli module
- "missing module import" error message HOT 1
- Break on deconstruction of arrays in function arguments HOT 1
- Multiple exports do not work HOT 1
- Include module in exceptions, for easier debugging. HOT 1
- readFileSync doesn't like reading from directories HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from es6-module-transpiler.