esnext / es6-module-transpiler

Tomorrow’s JavaScript module syntax today
http://esnext.github.io/es6-module-transpiler/
Other
1.21k stars 85 forks source link

Support ES6-specified whole-module import syntax. #119

Closed nathanhammond closed 9 years ago

nathanhammond commented 10 years ago

Currently fails parsing. My current workaround requires in everything named and then re-exports it as default:

import { __fixtures__, defineFixture, lookupFixture, raw, request } from 'ic-ajax';

var ajax = {
   __fixtures__: __fixtures__,
   defineFixture: defineFixture,
   lookupFixture: lookupFixture,
   raw: raw,
   request: request
};

export default ajax;
Aintaer commented 10 years ago

This seems like it can be solved with #86 The compiled output (AMD) would have to be something like:

define(
  ['foo', 'exports'], 
  function(__dependency1__, __exports__) {
    var foo = __dependency1__.__esModule ? 
      __dependency1__["default"] :
      __dependency1__;
caridy commented 10 years ago

import "foo" as bar; is not a valid syntax as today, the consideration at this point is import * as bar from "foo";

eventualbuddha commented 10 years ago

I had support for module foo from "foo" for a bit, but as that syntax is in flux I've removed it. Once TC39 decides what to do about it vs import * as foo from "foo" and esprima supports it I'll add support.

caridy commented 10 years ago

@eventualbuddha I'm working on the esprima branch for that. One thing to notice though, the branch of esprima used by 0.5.x is very old (git://github.com/thomasboyt/esprima#4be906f1abcbb), do you have any plan to align it with the esprima#harmony branch?

eventualbuddha commented 10 years ago

@caridy yes, as soon as the export default problem is fixed (ariya/esprima#252).

matthewrobb commented 10 years ago

It looks like import * as foo from "foo" is the new way to import all of a modules named exports. However I would wonder how feasible it might be to add support for module foo from "foo" and log out a warning about it. Basically interpret it the same was as the new syntax. The reason I ask this is as I look to use existing es6 module projects (ones using 0.4.x of the transpiler) with newer ones I am running into weird inconsistencies. It appears that the upgrade path to 0.5.x+ is rather rough and there seems to be very little momentum behind it.

I am trying to us a build workflow that brings in the raw es6 source files of my dependencies and then runs the transpiler over the entire tree of files. This is basically working save for the fact that many projects on the old version of transpiler are using slightly different syntax and in some cases broken syntax that once silently failed.

From Handlebars src:

// Sourced from lodash
// https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
var isFunction = function(value) {
  return typeof value === 'function';
};
// fallback for older versions of Chrome and Safari
if (isFunction(/x/)) {
  isFunction = function(value) {
    return typeof value === 'function' && toString.call(value) === '[object Function]';
  };
}
export var isFunction;

This is a static error in es5 code considering it's a double declare of isFunction but this didn't fail in the old transpiler it's just broken in a strange way (that handlebars seems to not be affected by).

It should instead be written:

// Sourced from lodash
// https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
var isFunction = function(value) {
  return typeof value === 'function';
};
// fallback for older versions of Chrome and Safari
if (isFunction(/x/)) {
  isFunction = function(value) {
    return typeof value === 'function' && toString.call(value) === '[object Function]';
  };
}
export { isFunction }
domenic commented 10 years ago

Strongly against adding legacy features. If you want legacy features, use legacy versions. And then make sure all your code is compatible with the new version. No compromises.

matthewrobb commented 10 years ago

@domenic After thinking about it more I can see why you think that and honestly I think I agree. ES6 modules are entering one of the most important (and currently difficult) phases of their existence. There has been some adoption and tooling built up but there has also been some recent spec adjustments and the tools are starting to get smarter and stricter.

My worry is that if module authors are forced to adjust their usage of the syntax too often it will quickly become more of a pain than a gain.

What else can be done to encourage conversion to the new syntax and transpiler? Should there be a release of the 0.4.x transpiler that either implements the new syntax changes or at the very least spits out warnings about changes? Are the authors of the grunt/gulp/broccoli/etc plugins actively working on upgrading to the new transpiler version?

The state of much of this leaves me feeling uneasy. I'll continue trying to do what I can do, limited as it may be.

caridy commented 10 years ago

@matthewrobb @domenic, the beauty of this is that module foo from "foo" and import * as foo from "foo" will probably produce the same AST since their semantic is identical. Based on that assumption, and the fact that the current esprima implementation has a lot of code and tests to support module foo from "foo", I think we should start by adding support for the new syntax to esprima, producing the exact same AST, update the transpiler to transform that AST (which it doesn't support today), and then eventually clean up esprima from the old syntax.

I'm willing to take this one, but I will need some help! :)

matthewrobb commented 10 years ago

@caridy @domenic For some "legacy" es6 features that may have seen decently wide use it would be best, when possible, to keep esprima's support in place. It may not be a feature used by the transpiler but it could be very useful for a validator or a linter.

domenic commented 10 years ago

I really don't think nonstandard features have any place in Esprima. It doesn't parse JSX or VBScript, and it shouldn't parse module x from "y".

caridy commented 10 years ago

@matthewrobb I agree with @domenic, there is no room for such things. Updating scripts using that syntax is a one time operation.

nathanhammond commented 10 years ago

My goal with filing this bug was simply to have the transpiler support whole-module import syntax, which I believe has changed since I filed this bug.

I'm editing the title for this bug report from Supportimport "foo" as bar;whole-module import syntax.`` toSupport ES6-specified whole-module import syntax.`

I haven't tested to make sure that it doesn't work, so if whole-module import has been created already you may safely close this issue by my opinion.

matthewrobb commented 10 years ago

@domenic @caridy Is there any reason you couldn't do

import { * as foo } from "foo";

It seems like it actually makes more sense but beside that it would be nice to be able to import some named imports and then import the rest to a single identifier.

import {
  * as lodash,
  bind
} from "lodash";
matthewrobb commented 10 years ago

I am trying to determine how this would tokenize in esprima. Currently module crypto from "crypto" is a ModuleDeclaration. The AST generated from import * couldn't possibly be compatible with the current whole-module import AST.

In the case that it cannot appear within curlies then it must be a new special "kind". Options for that would be: "*", "star", "all", "whole", "module". Or something along those lines.

Having it able to appear in curlies complicates things some despite my opinion that it's useful.

matthewrobb commented 10 years ago

I'm looking at adding the following test:

'import * as crypto from "crypto"': {
    type: 'ImportDeclaration',
    specifiers: [{
        type: 'ImportBatchSpecifier',
        id: {
            type: 'Identifier',
            name: 'crypto',
            range: [12, 18],
            loc: {
                start: { line: 1, column: 12 },
                end: { line: 1, column: 18 }
            }
        },
        name: null,
        range: [12, 18],
        loc: {
            start: { line: 1, column: 12 },
            end: { line: 1, column: 18 }
        }
    }],
    kind: 'batch',
    source: {
        type: 'Literal',
        value: 'crypto',
        raw: '"crypto"',
        range: [24, 32],
        loc: {
            start: { line: 1, column: 24 },
            end: { line: 1, column: 32 }
        }
    },
    range: [0, 32],
    loc: {
        start: { line: 1, column: 0 },
        end: { line: 1, column: 32 }
    }
}

I borrowed the batch terminology from export *. Still not sure if it should be a specifier or there should be special-case parsing similar to default imports. Currently no specifier other than export * contains a non-identifier.

domenic commented 10 years ago

Because that's not what's in the spec


From: Matthew Robbmailto:notifications@github.com Sent: ý2014-ý08-ý23 11:40 To: esnext/es6-module-transpilermailto:es6-module-transpiler@noreply.github.com Cc: Domenic Denicolamailto:domenic@domenicdenicola.com Subject: Re: [es6-module-transpiler] Support ES6-specified whole-module import syntax. (#119)

@domenichttps://github.com/domenic @caridyhttps://github.com/caridy Is there any reason you couldn't do

import { * as foo } from "foo";

It seems like it actually makes more sense but beside that it would be nice to be able to import some named imports and then import the rest to a single identifier.

import {

— Reply to this email directly or view it on GitHubhttps://github.com/esnext/es6-module-transpiler/issues/119#issuecomment-53156354.

caridy commented 10 years ago

recommendations:

eventualbuddha commented 9 years ago

This has landed in v0.9.0. See the editor for a demo.