CommonJS emit for ES6 import declarations

This commit is contained in:
Anders Hejlsberg 2015-02-08 08:03:15 -08:00
parent 7e187ef75f
commit 69bd05946a
7 changed files with 246 additions and 101 deletions

View File

@ -20,7 +20,7 @@ module ts {
return ModuleInstanceState.ConstEnumOnly;
}
// 3. non - exported import declarations
else if (node.kind === SyntaxKind.ImportEqualsDeclaration && !(node.flags & NodeFlags.Export)) {
else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && !(node.flags & NodeFlags.Export)) {
return ModuleInstanceState.NonInstantiated;
}
// 4. other uninstantiated module declarations.
@ -207,7 +207,8 @@ module ts {
exportKind |= SymbolFlags.ExportNamespace;
}
if (getCombinedNodeFlags(node) & NodeFlags.Export || (node.kind !== SyntaxKind.ImportEqualsDeclaration && isAmbientContext(container))) {
if (getCombinedNodeFlags(node) & NodeFlags.Export ||
(node.kind !== SyntaxKind.ImportDeclaration && node.kind !== SyntaxKind.ImportEqualsDeclaration && isAmbientContext(container))) {
if (exportKind) {
var local = declareSymbol(container.locals, undefined, node, exportKind, symbolExcludes);
local.exportSymbol = declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes);

View File

@ -442,12 +442,15 @@ module ts {
return result;
}
function isImportSymbolDeclaration(node: Node): boolean {
return node.kind === SyntaxKind.ImportEqualsDeclaration ||
node.kind === SyntaxKind.ImportClause && !!(<ImportClause>node).name ||
node.kind === SyntaxKind.NamespaceImport ||
node.kind === SyntaxKind.ImportSpecifier;
}
function getDeclarationOfImportSymbol(symbol: Symbol): Declaration {
return forEach(symbol.declarations, d =>
d.kind === SyntaxKind.ImportEqualsDeclaration ||
d.kind === SyntaxKind.ImportClause ||
d.kind === SyntaxKind.NamespaceImport ||
d.kind === SyntaxKind.ImportSpecifier ? d : undefined);
return forEach(symbol.declarations, d => isImportSymbolDeclaration(d) ? d : undefined);
}
function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration): Symbol {
@ -4915,42 +4918,47 @@ module ts {
// To avoid that we will give an error to users if they use arguments objects in arrow function so that they
// can explicitly bound arguments objects
if (symbol === argumentsSymbol && getContainingFunction(node).kind === SyntaxKind.ArrowFunction) {
error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_arrow_function_Consider_using_a_standard_function_expression);
error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_arrow_function_Consider_using_a_standard_function_expression);
}
if (symbol.flags & SymbolFlags.Import) {
var symbolLinks = getSymbolLinks(symbol);
symbolLinks.referenced = !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveImport(symbol));
//var symbolLinks = getSymbolLinks(symbol);
//if (!symbolLinks.referenced) {
// if (!isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveImport(symbol))) {
// symbolLinks.referenced = true;
// }
//}
// TODO: AndersH: This needs to be simplified. In an import of the form "import x = a.b.c;" we only need
// to resolve "a" and mark it as referenced. If "b" and/or "c" are aliases, we would be able to access them
// unless they're exported, and in that case they're already implicitly referenced.
//var symbolLinks = getSymbolLinks(symbol);
//if (!symbolLinks.referenced) {
// var importOrExportAssignment = getLeftSideOfImportEqualsOrExportAssignment(node);
var symbolLinks = getSymbolLinks(symbol);
if (!symbolLinks.referenced) {
var importOrExportAssignment = getLeftSideOfImportEqualsOrExportAssignment(node);
// // decision about whether import is referenced can be made now if
// // - import that are used anywhere except right side of import declarations
// // - imports that are used on the right side of exported import declarations
// // for other cases defer decision until the check of left side
// if (!importOrExportAssignment ||
// (importOrExportAssignment.flags & NodeFlags.Export) ||
// (importOrExportAssignment.kind === SyntaxKind.ExportAssignment)) {
// // Mark the import as referenced so that we emit it in the final .js file.
// // exception: identifiers that appear in type queries, const enums, modules that contain only const enums
// symbolLinks.referenced = !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveImport(symbol));
// }
// else {
// var nodeLinks = getNodeLinks(importOrExportAssignment);
// Debug.assert(!nodeLinks.importOnRightSide);
// nodeLinks.importOnRightSide = symbol;
// }
//}
// decision about whether import is referenced can be made now if
// - import that are used anywhere except right side of import declarations
// - imports that are used on the right side of exported import declarations
// for other cases defer decision until the check of left side
if (!importOrExportAssignment ||
(importOrExportAssignment.flags & NodeFlags.Export) ||
(importOrExportAssignment.kind === SyntaxKind.ExportAssignment)) {
// Mark the import as referenced so that we emit it in the final .js file.
// exception: identifiers that appear in type queries, const enums, modules that contain only const enums
symbolLinks.referenced = !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveImport(symbol));
}
else {
var nodeLinks = getNodeLinks(importOrExportAssignment);
Debug.assert(!nodeLinks.importOnRightSide);
nodeLinks.importOnRightSide = symbol;
}
}
//if (symbolLinks.referenced) {
// markLinkedImportsAsReferenced(<ImportEqualsDeclaration>getDeclarationOfKind(symbol, SyntaxKind.ImportEqualsDeclaration));
//}
if (symbolLinks.referenced) {
markLinkedImportsAsReferenced(<ImportEqualsDeclaration>getDeclarationOfKind(symbol, SyntaxKind.ImportEqualsDeclaration));
}
}
checkCollisionWithCapturedSuperVariable(node, node);
@ -10108,7 +10116,7 @@ module ts {
// Make sure the name in question does not collide with an import.
if (symbolWithRelevantName.flags & SymbolFlags.Import) {
var importEqualsDeclarationWithRelevantName = <ImportEqualsDeclaration>getDeclarationOfKind(symbolWithRelevantName, SyntaxKind.ImportEqualsDeclaration);
if (isReferencedImportEqualsDeclaration(importEqualsDeclarationWithRelevantName)) {
if (isReferencedImportDeclaration(importEqualsDeclarationWithRelevantName)) {
return false;
}
}
@ -10182,17 +10190,19 @@ module ts {
return isConstEnumSymbol(s) || s.constEnumOnlyModule;
}
function isReferencedImportEqualsDeclaration(node: ImportEqualsDeclaration): boolean {
var symbol = getSymbolOfNode(node);
if (getSymbolLinks(symbol).referenced) {
return true;
function isReferencedImportDeclaration(node: Node): boolean {
if (isImportSymbolDeclaration(node)) {
var symbol = getSymbolOfNode(node);
if (getSymbolLinks(symbol).referenced) {
return true;
}
// logic below will answer 'true' for exported import declaration in a nested module that itself is not exported.
// As a consequence this might cause emitting extra.
if (node.kind === SyntaxKind.ImportEqualsDeclaration && node.flags & NodeFlags.Export && isImportResolvedToValue(symbol)) {
return true;
}
}
// logic below will answer 'true' for exported import declaration in a nested module that itself is not exported.
// As a consequence this might cause emitting extra.
if (node.flags & NodeFlags.Export) {
return isImportResolvedToValue(symbol);
}
return false;
return forEachChild(node, isReferencedImportDeclaration);
}
function isImplementationOfOverload(node: FunctionLikeDeclaration) {
@ -10266,7 +10276,7 @@ module ts {
getLocalNameOfContainer,
getExpressionNamePrefix,
getExportAssignmentName,
isReferencedImportEqualsDeclaration,
isReferencedImportDeclaration,
getNodeCheckFlags,
isTopLevelValueImportEqualsWithEntityName,
isDeclarationVisible,
@ -10440,7 +10450,7 @@ module ts {
return grammarErrorOnNode(lastPrivate, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "private");
}
}
else if (node.kind === SyntaxKind.ImportEqualsDeclaration && flags & NodeFlags.Ambient) {
else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && flags & NodeFlags.Ambient) {
return grammarErrorOnNode(lastDeclare, Diagnostics.A_declare_modifier_cannot_be_used_with_an_import_declaration, "declare");
}
else if (node.kind === SyntaxKind.InterfaceDeclaration && flags & NodeFlags.Ambient) {

View File

@ -16,6 +16,11 @@ module ts {
getIndent(): number;
}
interface ExternalImportInfo {
declaration: ImportDeclaration | ImportEqualsDeclaration;
paramName: Identifier; // Callback parameter name for AMD module
}
interface SymbolAccessibilityDiagnostic {
errorNode: Node;
diagnosticMessage: DiagnosticMessage;
@ -1561,6 +1566,7 @@ module ts {
var tempCount = 0;
var tempVariables: Identifier[];
var tempParameters: Identifier[];
var externalImports: ExternalImportInfo[];
/** write emitted output to disk*/
var writeEmittedFiles = writeJavaScriptFile;
@ -3889,8 +3895,103 @@ module ts {
emitEnd(node);
}
function emitRequire(moduleName: Expression) {
if (moduleName) {
write("require(");
emitStart(moduleName);
emitLiteral(<LiteralExpression>moduleName);
emitEnd(moduleName);
emitToken(SyntaxKind.CloseParenToken, moduleName.end);
write(";");
}
else {
write("require();");
}
}
function emitImportAssignment(node: Declaration, moduleName: Expression) {
if (!(node.flags & NodeFlags.Export)) write("var ");
emitModuleMemberName(<Declaration>node);
write(" = ");
emitRequire(moduleName);
}
function emitNamedImportAssignments(namedImports: NamedImports, moduleReference: Identifier) {
var elements = namedImports.elements;
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (resolver.isReferencedImportDeclaration(element)) {
writeLine();
if (!(element.flags & NodeFlags.Export)) write("var ");
emitModuleMemberName(element);
write(" = ");
emit(moduleReference);
write(".");
emit(element.propertyName || element.name);
write(";");
}
}
}
function emitExportedImportAssignment(node: Declaration) {
if (node.flags & NodeFlags.Export) {
emitModuleMemberName(node);
write(" = ");
emit(node.name);
write(";");
}
}
function emitImportDeclarationAMD(node: ImportDeclaration) {
var importClause = node.importClause;
if (importClause) {
if (importClause.name) {
emitExportedImportAssignment(importClause);
}
else if (importClause.namedBindings) {
if (node.importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
emitExportedImportAssignment(<NamespaceImport>importClause.namedBindings);
}
else {
}
}
}
}
function emitImportDeclaration(node: ImportDeclaration) {
var importClause = node.importClause;
if (!importClause || resolver.isReferencedImportDeclaration(node)) {
emitLeadingComments(node);
emitStart(node);
var moduleName = getImportedModuleName(node);
if (importClause) {
if (importClause.name) {
emitImportAssignment(importClause, moduleName);
}
else if (importClause.namedBindings) {
if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
emitImportAssignment(<NamespaceImport>importClause.namedBindings, moduleName);
}
else {
var temp = createTempVariable(node);
write("var ");
emit(temp);
write(" = ");
emitRequire(moduleName);
emitNamedImportAssignments(<NamedImports>importClause.namedBindings, temp);
}
}
}
else {
emitRequire(moduleName);
}
emitEnd(node);
emitTrailingComments(node);
}
}
function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) {
var emitImportDeclaration = resolver.isReferencedImportEqualsDeclaration(node);
var emitImportDeclaration = resolver.isReferencedImportDeclaration(node);
if (!emitImportDeclaration) {
// preserve old compiler's behavior: emit 'var' for import declaration (even if we do not consider them referenced) when
@ -3917,21 +4018,16 @@ module ts {
writeLine();
emitLeadingComments(node);
emitStart(node);
if (!(node.flags & NodeFlags.Export)) write("var ");
emitModuleMemberName(node);
write(" = ");
if (isInternalModuleImportEqualsDeclaration(node)) {
if (!(node.flags & NodeFlags.Export)) write("var ");
emitModuleMemberName(node);
write(" = ");
emit(node.moduleReference);
write(";");
}
else {
var literal = <LiteralExpression>getExternalModuleImportEqualsDeclarationExpression(node);
write("require(");
emitStart(literal);
emitLiteral(literal);
emitEnd(literal);
emitToken(SyntaxKind.CloseParenToken, literal.end);
emitImportAssignment(node, getImportedModuleName(node));
}
write(";");
emitEnd(node);
emitTrailingComments(node);
}
@ -3941,13 +4037,48 @@ module ts {
function getExternalImportEqualsDeclarations(node: SourceFile): ImportEqualsDeclaration[] {
var result: ImportEqualsDeclaration[] = [];
forEach(node.statements, statement => {
if (isExternalModuleImportEqualsDeclaration(statement) && resolver.isReferencedImportEqualsDeclaration(<ImportEqualsDeclaration>statement)) {
if (isExternalModuleImportEqualsDeclaration(statement) && resolver.isReferencedImportDeclaration(<ImportEqualsDeclaration>statement)) {
result.push(<ImportEqualsDeclaration>statement);
}
});
return result;
}
function getExternalImportInfo(node: ImportDeclaration | ImportEqualsDeclaration): ExternalImportInfo {
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
var name = (<ImportEqualsDeclaration>node).name;
}
else {
var importClause = (<ImportDeclaration>node).importClause;
if (importClause) {
if (importClause.name) {
var name = importClause.name;
}
else if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
var name = (<NamespaceImport>importClause.namedBindings).name;
}
else {
var name = createTempVariable(node);
}
}
}
return {
declaration: node,
paramName: name
};
}
function getExternalImports(sourceFile: SourceFile): ExternalImportInfo[] {
var result: ExternalImportInfo[] = [];
forEach(sourceFile.statements, node => {
if ((node.kind === SyntaxKind.ImportDeclaration || isExternalModuleImportEqualsDeclaration(node)) &&
resolver.isReferencedImportDeclaration(node)) {
result.push(getExternalImportInfo(<ImportDeclaration | ImportEqualsDeclaration>node));
}
});
return result;
}
function getFirstExportAssignment(sourceFile: SourceFile) {
return forEach(sourceFile.statements, node => {
if (node.kind === SyntaxKind.ExportAssignment) {
@ -3957,16 +4088,22 @@ module ts {
}
function emitAMDModule(node: SourceFile, startIndex: number) {
var imports = getExternalImportEqualsDeclarations(node);
externalImports = getExternalImports(node);
writeLine();
write("define(");
if (node.amdModuleName) {
write("\"" + node.amdModuleName + "\", ");
}
write("[\"require\", \"exports\"");
forEach(imports, imp => {
forEach(externalImports, imp => {
write(", ");
emitLiteral(<LiteralExpression>getExternalModuleImportEqualsDeclarationExpression(imp));
var moduleName = getImportedModuleName(imp.declaration);
if (moduleName) {
emitLiteral(moduleName);
}
else {
write("\"\"");
}
});
forEach(node.amdDependencies, amdDependency => {
var text = "\"" + amdDependency + "\"";
@ -3974,9 +4111,9 @@ module ts {
write(text);
});
write("], function (require, exports");
forEach(imports, imp => {
forEach(externalImports, imp => {
write(", ");
emit(imp.name);
emit(imp.paramName);
});
write(") {");
increaseIndent();
@ -4104,6 +4241,7 @@ module ts {
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.ExportAssignment:
return false;
@ -4265,6 +4403,8 @@ module ts {
return emitEnumMember(<EnumMember>node);
case SyntaxKind.ModuleDeclaration:
return emitModuleDeclaration(<ModuleDeclaration>node);
case SyntaxKind.ImportDeclaration:
return emitImportDeclaration(<ImportDeclaration>node);
case SyntaxKind.ImportEqualsDeclaration:
return emitImportEqualsDeclaration(<ImportEqualsDeclaration>node);
case SyntaxKind.SourceFile:

View File

@ -4600,32 +4600,22 @@ module ts {
var node = <ExternalModuleReference>createNode(SyntaxKind.ExternalModuleReference);
parseExpected(SyntaxKind.RequireKeyword);
parseExpected(SyntaxKind.OpenParenToken);
// We allow arbitrary expressions here, even though the grammar only allows string
// literals. We check to ensure that it is only a string literal later in the grammar
// walker.
node.expression = parseExpression();
// Ensure the string being required is in our 'identifier' table. This will ensure
// that features like 'find refs' will look inside this file when search for its name.
if (node.expression.kind === SyntaxKind.StringLiteral) {
internIdentifier((<LiteralExpression>node.expression).text);
}
node.expression = parseModuleSpecifier();
parseExpected(SyntaxKind.CloseParenToken);
return finishNode(node);
}
function parseModuleSpecifier(): StringLiteralExpression {
// ModuleSpecifier:
// StringLiteral
if (token === SyntaxKind.StringLiteral) {
// Ensure the string being required is in our 'identifier' table. This will ensure
// that features like 'find refs' will look inside this file when search for its name.
return <StringLiteralExpression>parseLiteralNode(/*internName*/ true);
function parseModuleSpecifier(): Expression {
// We allow arbitrary expressions here, even though the grammar only allows string
// literals. We check to ensure that it is only a string literal later in the grammar
// walker.
var result = parseExpression();
// Ensure the string being required is in our 'identifier' table. This will ensure
// that features like 'find refs' will look inside this file when search for its name.
if (result.kind === SyntaxKind.StringLiteral) {
internIdentifier((<LiteralExpression>result).text);
}
parseErrorAtCurrentToken(Diagnostics.String_literal_expected);
return result;
}
function parseNamespaceImport(): NamespaceImport {

View File

@ -349,21 +349,6 @@ module ts {
});
}
function getImportedModuleName(node: Node): StringLiteralExpression {
if (node.kind === SyntaxKind.ImportDeclaration) {
return (<ImportDeclaration>node).moduleSpecifier;
}
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
var reference = (<ImportEqualsDeclaration>node).moduleReference;
if (reference.kind === SyntaxKind.ExternalModuleReference) {
var expr = (<ExternalModuleReference>reference).expression;
if (expr && expr.kind === SyntaxKind.StringLiteral) {
return <StringLiteralExpression>expr;
}
}
}
}
function processImportedModules(file: SourceFile, basePath: string) {
forEach(file.statements, node => {
if (node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) {

View File

@ -880,7 +880,7 @@ module ts {
// ImportClause information is shown at its declaration below.
export interface ImportDeclaration extends Statement, ModuleElement {
importClause?: ImportClause;
moduleSpecifier: StringLiteralExpression;
moduleSpecifier: Expression;
}
// In case of:
@ -1166,7 +1166,7 @@ module ts {
getLocalNameOfContainer(container: ModuleDeclaration | EnumDeclaration): string;
getExpressionNamePrefix(node: Identifier): string;
getExportAssignmentName(node: SourceFile): string;
isReferencedImportEqualsDeclaration(node: ImportEqualsDeclaration): boolean;
isReferencedImportDeclaration(node: Node): boolean;
isTopLevelValueImportEqualsWithEntityName(node: ImportEqualsDeclaration): boolean;
getNodeCheckFlags(node: Node): NodeCheckFlags;
isDeclarationVisible(node: Declaration): boolean;

View File

@ -597,6 +597,22 @@ module ts {
return node.kind === SyntaxKind.ImportEqualsDeclaration && (<ImportEqualsDeclaration>node).moduleReference.kind !== SyntaxKind.ExternalModuleReference;
}
function extractStringLiteral(node: Expression): StringLiteralExpression {
return node && node.kind === SyntaxKind.StringLiteral ? <StringLiteralExpression>node : undefined;
}
export function getImportedModuleName(node: Node): StringLiteralExpression {
if (node.kind === SyntaxKind.ImportDeclaration) {
return extractStringLiteral((<ImportDeclaration>node).moduleSpecifier);
}
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
var reference = (<ImportEqualsDeclaration>node).moduleReference;
if (reference.kind === SyntaxKind.ExternalModuleReference) {
return extractStringLiteral((<ExternalModuleReference>reference).expression);
}
}
}
export function hasDotDotDotToken(node: Node) {
return node && node.kind === SyntaxKind.Parameter && (<ParameterDeclaration>node).dotDotDotToken !== undefined;
}
@ -674,6 +690,9 @@ module ts {
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportClause:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.NamespaceImport:
return true;
}
return false;