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

Support ES6 import and export declarations #1983

Merged
merged 49 commits into from
Feb 23, 2015
Merged

Support ES6 import and export declarations #1983

merged 49 commits into from
Feb 23, 2015

Conversation

ahejlsberg
Copy link
Member

This PR adds support for ES6 import and export declarations. Supported import forms include:

import defaultBinding from "foo";  // Import foo's default export
import * as foo from "foo";        // Make a single binding for the entire namespace
import { a, b as x } from "foo";   // Make local bindings for individually imported members

Supported export forms include:

export { a, b as x };             // Export local entities
export { a, b as x } from "foo";  // Re-export individual members of foo without local bindings
export * from "foo";              // Re-export all members of foo without local bindings

The following export forms are not yet implemented:

export default class {...}
export default function(...) {...}
export default someExpression;

ES6 modules are very similar to TypeScript's external modules. ES6 modules permit individual entities to be exported using an export modifier and additionally support an optional export default in each module. The export default corresponds to TypeScript's export =, except that in ES6 a module can have both individual exports and a default export.

Given the following external module utils.ts:

export function foo(s: string) {}
export function bar(s: string) {}
export default function(x: number) {}

here are some ways it might be imported:

import * as utils from "utils";  // Namespace import
utils.foo("hello");
utils.bar("world");
import { foo, bar as baz } from "utils";  // Individual named imports
foo("hello");
baz("world");
import func from "utils";  // Import of default export
func(42);
import func, { foo, bar } from "utils";  // Combination
func(42);
foo("hello");
bar("world");

When ES6 modules are compiled down-level for ES3 or ES5 (which is currently the only supported way), a module cannot have both individual exports and a default export.

sheetalkamat and others added 30 commits January 27, 2015 14:42
NamedImports :
{ }
{ ImportsList }
{ ImportsList , }

ImportsList :
ImportSpecifier
ImportsList , ImportSpecifier

ImportSpecifier :
ImportedBinding
IdentifierName as ImportedBinding

Conflicts:
	src/compiler/parser.ts
…void creating missing symbols

Missing symbols are defined when the declaration doesnt have name,
so if we created node for missing identifier it would end up binding symbol with name (Missing)
…claration

This helps binder to use it directly to bind the default binding
Conflicts:
	src/compiler/checker.ts
@RyanCavanaugh
Copy link
Member

Notes from the design meeting

If we rewrite references to an imported function func to _ref.func, then rewritten calls to func() will have a this of _ref instead of the default. Potential fix is to rewrite invocations as (0, _ref.func)(args) instead of _ref.func(args) (note that (_ref.func)(args) is not a sufficient fix).

Brian notes that this will be undefined in any ES6 module, so we may not need to really care.

@ahejlsberg
Copy link
Member Author

@sheetalkamat That should be an error. We currently put all external symbols of a module into scope, but we should exclude those that came from an export declaration. I will fix.

@ahejlsberg
Copy link
Member Author

@RyanCavanaugh Regarding effects of rewriting on this, the same thing happens today with the rewrite we do when referencing exported members of another declaration of the same internal module. It happens everywhere in the compiler, for example. Doesn't appear to be a problem, at least I haven't seen anyone report an issue for it.

@ahejlsberg
Copy link
Member Author

Latest commit adds support for export * declarations, including binding, checking, and down-level emit for CommonJS and AMD. @mhegazy @sheetalkamat The commit does not include .d.ts emit support.

An example:

// a.ts
export var x = 1;
export var y = 2;
export function foo() {
    console.log("Hello from a");
}
// b.ts
export function foo() {
    console.log("Hello from b");
}
export { foo as bar };
export * from "./a";
// t.ts
import * as a from "./a";
import * as b from "./b";
a.foo();  // Hello from a
b.foo();  // Hello from b
b.bar();  // Hello from b
console.log(a.x);  // 1
console.log(b.x);  // 1

This generates the following when compiled with -m commonjs:

// a.js
exports.x = 1;
exports.y = 2;
function foo() {
    console.log("Hello from a");
}
exports.foo = foo;
// b.js
function foo() {
    console.log("Hello from b");
}
exports.foo = foo;
exports.bar = foo;
var _a = require("./a");
for (var _b in _a) if (!exports.hasOwnProperty(_b)) exports[_b] = _a[_b];
// t.js
var a = require("./a");
var b = require("./b");
a.foo();
b.foo();
b.bar();
console.log(a.x);
console.log(b.x);

return links.localModuleName;

function isExistingName(name: string) {
return hasProperty(sourceFile.identifiers, name) || hasProperty(generatedNames, name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to have a situation when name does not appear in current file but exists on the top level in some another file (like window or name)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we should probably check globals as well. Will fix.

return links.localModuleName;

function isExistingName(name: string) {
return hasProperty(globals, name) || hasProperty(sourceFile.identifiers, name) || hasProperty(generatedNames, name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

identifiers can be incomplete after incremental parsing so 'compile on save' will be broken. that is why we use a separate table nameTable in part of SourceFile that belongs to services layer. We can pull initializeNameTable function that builds nameTable on demand down to the base compiler.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we should do that.

@@ -1820,7 +1847,7 @@ module ts {
function isReusableModuleElement(node: Node) {
if (node) {
switch (node.kind) {
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the new import and export node types should be in this list.

Conflicts:
	src/compiler/diagnosticInformationMap.generated.ts
	src/compiler/diagnosticMessages.json
	src/compiler/emitter.ts
	tests/baselines/reference/APISample_compile.js
	tests/baselines/reference/APISample_compile.types
	tests/baselines/reference/APISample_linter.js
	tests/baselines/reference/APISample_linter.types
	tests/baselines/reference/APISample_transform.js
	tests/baselines/reference/APISample_transform.types
	tests/baselines/reference/APISample_watcher.js
	tests/baselines/reference/APISample_watcher.types
	tests/baselines/reference/recursiveClassReferenceTest.js.map
	tests/baselines/reference/recursiveClassReferenceTest.sourcemap.txt
@Vadorequest
Copy link

Super nice. Thanks!

@daslicht
Copy link

how to import templates like this with the es6 syntax, please:
var template = require("./file.handlebars");
https://github.com/pcardune/handlebars-loader#your-js-making-use-of-the-templates

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Nov 12, 2016

@daslicht First, you need a loader that interpolates them into a named module export, usually this is the export named default.
Then you need an ambient wildcard module declaration so that TypeScript understand that the paths are legal module import targets. That part looks like

declare module '*.handlebars' {
  // export matching the shape produced by the loader.
}

In your case, it looks like using ES2015 module syntax is not supported, and you are stuck with export = and import = as I noticed you commented on pcardune/handlebars-loader#115

@daslicht
Copy link

daslicht commented Nov 17, 2016

First, you need a loader that interpolates them into a named module export, usually this is the export named default.

Any example on that ? please?

I am trying to use handlebars in an TypeScript setup

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Nov 17, 2016

@daslicht say I were using SystemJS to load and bundle my application. In that case I would have a setup like this (simplified)

systemjs.config.js

SystemJS.config({
  meta: {
    '*.handlebars': {
      loader: 'text-loader-plugin'
    }
  }
});

If I were using webpack it would look like this (simplified)

webpack.config.js

module.exports = {
  loaders: [
    {
      test: /\.handlebars$/,
      loader: 'text-loader-plugin'
    }
  ]
};

The plugin would sort of look like this (changed and simplified for exposition)

text-loader-plugin.js

export default function (path) {
  const content = load(path); // XHR or fs.readFile
  return { 
    'default': content 
  };
}

Then in my TypeScript project I would have a file like globals.d.ts with the following declaration

globals.d.ts

declare module '*.handlebars' {
  export default '';
}

Then I would use it like this

app/home.ts

import template from 'app/home.handlebars';

@daslicht
Copy link

daslicht commented Nov 17, 2016

OK, thank you !, I keep trying, my approach was using the handlebars-loader:
https://github.com/pcardune/handlebars-loader

@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.