From 234358e6c6da54084ed78f27182f4946016f3bcb Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 2 Mar 2015 12:17:05 -0800 Subject: [PATCH] Unifying ES6 and TypeScript external modules Export assignments are now equivalent to export of member named "default" Export assignments and exports defaults collected by binder Export * declarations collected by binder Simplified logic for marking import symbols as referenced Removed "location" parameter from resolveEntityName Improved error position reporting in resolveEntityName --- src/compiler/binder.ts | 34 ++-- src/compiler/checker.ts | 347 +++++++++++++++++++--------------------- src/compiler/emitter.ts | 45 +++--- src/compiler/types.ts | 26 ++- 4 files changed, 221 insertions(+), 231 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index c26d3369f3fad..8541c3b36b38e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -112,6 +112,10 @@ module ts { return "__new"; case SyntaxKind.IndexSignature: return "__index"; + case SyntaxKind.ExportAssignment: + return "default"; + case SyntaxKind.ExportDeclaration: + return "__export"; } } @@ -137,9 +141,9 @@ module ts { : Diagnostics.Duplicate_identifier_0; forEach(symbol.declarations, declaration => { - file.bindDiagnostics.push(createDiagnosticForNode(declaration.name, message, getDisplayName(declaration))); + file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration))); }); - file.bindDiagnostics.push(createDiagnosticForNode(node.name, message, getDisplayName(node))); + file.bindDiagnostics.push(createDiagnosticForNode(node.name || node, message, getDisplayName(node))); symbol = createSymbol(0, name); } @@ -310,13 +314,6 @@ module ts { } } - function bindExportDeclaration(node: ExportDeclaration) { - if (!node.exportClause) { - ((container).exportStars || ((container).exportStars = [])).push(node); - } - bindChildren(node, 0, /*isBlockScopeContainer*/ false); - } - function bindFunctionOrConstructorType(node: SignatureDeclaration) { // For a given function symbol "<...>(...) => T" we want to generate a symbol identical // to the one we would get for: { <...>(...): T } @@ -478,9 +475,6 @@ module ts { case SyntaxKind.ExportSpecifier: bindDeclaration(node, SymbolFlags.Import, SymbolFlags.ImportExcludes, /*isBlockScopeContainer*/ false); break; - case SyntaxKind.ExportDeclaration: - bindExportDeclaration(node); - break; case SyntaxKind.ImportClause: if ((node).name) { bindDeclaration(node, SymbolFlags.Import, SymbolFlags.ImportExcludes, /*isBlockScopeContainer*/ false); @@ -489,6 +483,22 @@ module ts { bindChildren(node, 0, /*isBlockScopeContainer*/ false); } break; + case SyntaxKind.ExportDeclaration: + if (!(node).exportClause) { + // All export * declarations are collected in an __export symbol + declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, 0); + } + bindChildren(node, 0, /*isBlockScopeContainer*/ false); + break; + case SyntaxKind.ExportAssignment: + if ((node).expression.kind === SyntaxKind.Identifier) { + declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Import, SymbolFlags.ImportExcludes); + } + else { + declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes); + } + bindChildren(node, 0, /*isBlockScopeContainer*/ false); + break; case SyntaxKind.SourceFile: if (isExternalModule(node)) { bindAnonymousDeclaration(node, SymbolFlags.ValueModule, '"' + removeFileExtension((node).fileName) + '"', /*isBlockScopeContainer*/ true); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bc1a788236127..7699f333d54f6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -465,7 +465,8 @@ module ts { node.kind === SyntaxKind.ImportClause && !!(node).name || node.kind === SyntaxKind.NamespaceImport || node.kind === SyntaxKind.ImportSpecifier || - node.kind === SyntaxKind.ExportSpecifier; + node.kind === SyntaxKind.ExportSpecifier || + node.kind === SyntaxKind.ExportAssignment; } function getDeclarationOfImportSymbol(symbol: Symbol): Declaration { @@ -518,7 +519,11 @@ module ts { function getTargetOfExportSpecifier(node: ExportSpecifier): Symbol { return (node.parent.parent).moduleSpecifier ? getExternalModuleMember(node.parent.parent, node) : - resolveEntityName(node, node.propertyName || node.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace); + resolveEntityName(node.propertyName || node.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace); + } + + function getTargetOfExportAssignment(node: ExportAssignment): Symbol { + return resolveEntityName(node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace); } function getTargetOfImportDeclaration(node: Declaration): Symbol { @@ -533,6 +538,8 @@ module ts { return getTargetOfImportSpecifier(node); case SyntaxKind.ExportSpecifier: return getTargetOfExportSpecifier(node); + case SyntaxKind.ExportAssignment: + return getTargetOfExportAssignment(node); } } @@ -556,6 +563,31 @@ module ts { return links.target; } + function markExportAsReferenced(node: ImportEqualsDeclaration | ExportAssignment | ExportSpecifier) { + var symbol = getSymbolOfNode(node); + var target = resolveImport(symbol); + if (target && target !== unknownSymbol && target.flags & SymbolFlags.Value && !isConstEnumOrConstEnumOnlyModule(target)) { + markImportSymbolAsReferenced(symbol); + } + } + + function markImportSymbolAsReferenced(symbol: Symbol) { + var links = getSymbolLinks(symbol); + if (!links.referenced) { + links.referenced = true; + var node = getDeclarationOfImportSymbol(symbol); + if (node.kind === SyntaxKind.ExportAssignment) { + checkExpressionCached((node).expression); + } + else if (node.kind === SyntaxKind.ExportSpecifier) { + checkExpressionCached((node).propertyName || (node).name); + } + else if (isInternalModuleImportEqualsDeclaration(node)) { + checkExpressionCached((node).moduleReference); + } + } + } + // This function is only for imports with entity names function getSymbolOfPartOfRightHandSideOfImportEquals(entityName: EntityName, importDeclaration?: ImportEqualsDeclaration): Symbol { if (!importDeclaration) { @@ -573,13 +605,13 @@ module ts { } // Check for case 1 and 3 in the above example if (entityName.kind === SyntaxKind.Identifier || entityName.parent.kind === SyntaxKind.QualifiedName) { - return resolveEntityName(importDeclaration, entityName, SymbolFlags.Namespace); + return resolveEntityName(entityName, SymbolFlags.Namespace); } else { // Case 2 in above example // entityName.kind could be a QualifiedName or a Missing identifier Debug.assert(entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration); - return resolveEntityName(importDeclaration, entityName, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace); + return resolveEntityName(entityName, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace); } } @@ -588,28 +620,28 @@ module ts { } // Resolves a qualified name and any involved import aliases - function resolveEntityName(location: Node, name: EntityName, meaning: SymbolFlags): Symbol { + function resolveEntityName(name: EntityName, meaning: SymbolFlags): Symbol { if (getFullWidth(name) === 0) { return undefined; } - if (name.kind === SyntaxKind.Identifier) { - var symbol = resolveName(location,(name).text, meaning, Diagnostics.Cannot_find_name_0, name); + var symbol = resolveName(name, (name).text, meaning, Diagnostics.Cannot_find_name_0, name); if (!symbol) { - return; + return undefined; } } else if (name.kind === SyntaxKind.QualifiedName) { - var namespace = resolveEntityName(location,(name).left, SymbolFlags.Namespace); - if (!namespace || namespace === unknownSymbol || getFullWidth((name).right) === 0) return; - var symbol = getSymbol(getExportsOfSymbol(namespace), (name).right.text, meaning); + var namespace = resolveEntityName((name).left, SymbolFlags.Namespace); + if (!namespace || namespace === unknownSymbol || getFullWidth((name).right) === 0) { + return undefined; + } + var right = (name).right; + var symbol = getSymbol(getExportsOfSymbol(namespace), right.text, meaning); if (!symbol) { - error(location, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace), - declarationNameToString((name).right)); - return; + error(right, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace), declarationNameToString(right)); + return undefined; } } - Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); return symbol.flags & meaning ? symbol : resolveImport(symbol); } @@ -658,6 +690,10 @@ module ts { error(moduleReferenceLiteral, Diagnostics.Cannot_find_external_module_0, moduleName); } + function getExportAssignmentSymbol(moduleSymbol: Symbol): Symbol { + return moduleSymbol.exports["default"]; + } + function getResolvedExportAssignmentSymbol(moduleSymbol: Symbol): Symbol { var symbol = getExportAssignmentSymbol(moduleSymbol); if (symbol) { @@ -670,64 +706,6 @@ module ts { } } - function getExportAssignmentSymbol(symbol: Symbol): Symbol { - checkTypeOfExportAssignmentSymbol(symbol); - return getSymbolLinks(symbol).exportAssignmentSymbol; - } - - function checkTypeOfExportAssignmentSymbol(containerSymbol: Symbol): void { - var symbolLinks = getSymbolLinks(containerSymbol); - if (!symbolLinks.exportAssignmentChecked) { - var exportInformation = collectExportInformationForSourceFileOrModule(containerSymbol); - if (exportInformation.exportAssignments.length) { - if (exportInformation.exportAssignments.length > 1) { - // TypeScript 1.0 spec (April 2014): 11.2.4 - // It is an error for an external module to contain more than one export assignment. - forEach(exportInformation.exportAssignments, node => error(node, Diagnostics.A_module_cannot_have_more_than_one_export_assignment)); - } - var node = exportInformation.exportAssignments[0]; - if (exportInformation.hasExportedMember) { - // TypeScript 1.0 spec (April 2014): 11.2.3 - // If an external module contains an export assignment it is an error - // for the external module to also contain export declarations. - // The two types of exports are mutually exclusive. - error(node, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements); - } - if (node.expression.kind === SyntaxKind.Identifier && (node.expression).text) { - var exportSymbol = resolveName(node, (node.expression).text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, - Diagnostics.Cannot_find_name_0, node.expression); - } - else { - var exportSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "*default*"); - exportSymbol.parent = containerSymbol; - (exportSymbol).type = checkExpression(node.expression); - } - symbolLinks.exportAssignmentSymbol = exportSymbol || unknownSymbol; - } - symbolLinks.exportAssignmentChecked = true; - } - } - - function collectExportInformationForSourceFileOrModule(symbol: Symbol) { - var seenExportedMember = false; - var result: ExportAssignment[] = []; - forEach(symbol.declarations, declaration => { - var block = (declaration.kind === SyntaxKind.SourceFile ? declaration : (declaration).body); - forEach(block.statements, node => { - if (node.kind === SyntaxKind.ExportAssignment) { - result.push(node); - } - else { - seenExportedMember = seenExportedMember || (node.flags & NodeFlags.Export) !== 0; - } - }); - }); - return { - hasExportedMember: seenExportedMember, - exportAssignments: result - }; - } - function getExportsOfSymbol(symbol: Symbol): SymbolTable { return symbol.flags & SymbolFlags.Module ? getExportsOfModule(symbol) : symbol.exports; } @@ -738,6 +716,14 @@ module ts { } function getExportsForModule(moduleSymbol: Symbol): SymbolTable { + if (compilerOptions.target < ScriptTarget.ES6) { + var defaultSymbol = getExportAssignmentSymbol(moduleSymbol); + if (defaultSymbol) { + return { + "default": defaultSymbol + }; + } + } var result: SymbolTable; var visitedSymbols: Symbol[] = []; visit(moduleSymbol); @@ -752,13 +738,12 @@ module ts { } extendSymbolTable(result, symbol.exports); } - forEach(symbol.declarations, node => { - if (node.kind === SyntaxKind.SourceFile || node.kind === SyntaxKind.ModuleDeclaration) { - forEach((node).exportStars, exportStar => { - visit(resolveExternalModuleName(exportStar, exportStar.moduleSpecifier)); - }); - } - }); + var exportStars = symbol.exports["__export"]; + if (exportStars) { + forEach(exportStars.declarations, node => { + visit(resolveExternalModuleName(node, (node).moduleSpecifier)); + }); + } } } } @@ -2001,6 +1986,10 @@ module ts { if (declaration.kind === SyntaxKind.CatchClause) { return links.type = anyType; } + // Handle export default expressions + if (declaration.kind === SyntaxKind.ExportAssignment) { + return links.type = checkExpression((declaration).expression); + } // Handle variable, parameter or property links.type = resolvingType; var type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true); @@ -3055,7 +3044,7 @@ module ts { function getTypeFromTypeReferenceNode(node: TypeReferenceNode): Type { var links = getNodeLinks(node); if (!links.resolvedType) { - var symbol = resolveEntityName(node, node.typeName, SymbolFlags.Type); + var symbol = resolveEntityName(node.typeName, SymbolFlags.Type); if (symbol) { var type: Type; if ((symbol.flags & SymbolFlags.TypeParameter) && isTypeParameterReferenceIllegalInConstraint(node, symbol)) { @@ -5001,22 +4990,6 @@ module ts { } } - /*Transitively mark all linked imports as referenced*/ - function markLinkedImportsAsReferenced(node: ImportEqualsDeclaration): void { - if (node) { - var nodeLinks = getNodeLinks(node); - while (nodeLinks.importOnRightSide) { - var rightSide = nodeLinks.importOnRightSide; - nodeLinks.importOnRightSide = undefined; - - getSymbolLinks(rightSide).referenced = true; - Debug.assert((rightSide.flags & SymbolFlags.Import) !== 0); - - nodeLinks = getNodeLinks(getDeclarationOfKind(rightSide, SyntaxKind.ImportEqualsDeclaration)) - } - } - } - function checkIdentifier(node: Identifier): Type { var symbol = getResolvedSymbol(node); @@ -5030,44 +5003,8 @@ module ts { 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); - //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); - - // 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(getDeclarationOfKind(symbol, SyntaxKind.ImportEqualsDeclaration)); - } + if (symbol.flags & SymbolFlags.Import && !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveImport(symbol))) { + markImportSymbolAsReferenced(symbol); } checkCollisionWithCapturedSuperVariable(node, node); @@ -9081,7 +9018,7 @@ module ts { var staticBaseType = getTypeOfSymbol(baseType.symbol); checkTypeAssignableTo(staticType, getTypeWithoutConstructors(staticBaseType), node.name, Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1); - if (baseType.symbol !== resolveEntityName(node, baseTypeNode.typeName, SymbolFlags.Value)) { + if (baseType.symbol !== resolveEntityName(baseTypeNode.typeName, SymbolFlags.Value)) { error(baseTypeNode, Diagnostics.Type_name_0_in_extends_clause_does_not_reference_constructor_function_for_0, typeToString(baseType)); } @@ -9656,30 +9593,25 @@ module ts { function checkImportEqualsDeclaration(node: ImportEqualsDeclaration) { checkGrammarModifiers(node); - if (isInternalModuleImportEqualsDeclaration(node)) { + if (isInternalModuleImportEqualsDeclaration(node) || checkExternalImportOrExportDeclaration(node)) { checkImportBinding(node); - var symbol = getSymbolOfNode(node); - var target = resolveImport(symbol); - if (target !== unknownSymbol) { - if (target.flags & SymbolFlags.Value) { - // Target is a value symbol, check that it is not hidden by a local declaration with the same name and - // ensure it can be evaluated as an expression - var moduleName = getFirstIdentifier(node.moduleReference); - if (resolveEntityName(node, moduleName, SymbolFlags.Value | SymbolFlags.Namespace).flags & SymbolFlags.Namespace) { - checkExpressionOrQualifiedName(node.moduleReference); + if (node.flags & NodeFlags.Export) { + markExportAsReferenced(node); + } + if (isInternalModuleImportEqualsDeclaration(node)) { + var target = resolveImport(getSymbolOfNode(node)); + if (target !== unknownSymbol) { + if (target.flags & SymbolFlags.Value) { + // Target is a value symbol, check that it is not hidden by a local declaration with the same name + var moduleName = getFirstIdentifier(node.moduleReference); + if (!(resolveEntityName(moduleName, SymbolFlags.Value | SymbolFlags.Namespace).flags & SymbolFlags.Namespace)) { + error(moduleName, Diagnostics.Module_0_is_hidden_by_a_local_declaration_with_the_same_name, declarationNameToString(moduleName)); + } } - else { - error(moduleName, Diagnostics.Module_0_is_hidden_by_a_local_declaration_with_the_same_name, declarationNameToString(moduleName)); + if (target.flags & SymbolFlags.Type) { + checkTypeNameIsReserved(node.name, Diagnostics.Import_name_cannot_be_0); } } - if (target.flags & SymbolFlags.Type) { - checkTypeNameIsReserved(node.name, Diagnostics.Import_name_cannot_be_0); - } - } - } - else { - if (checkExternalImportOrExportDeclaration(node)) { - checkImportBinding(node); } } } @@ -9690,13 +9622,21 @@ module ts { } if (!node.moduleSpecifier || checkExternalImportOrExportDeclaration(node)) { if (node.exportClause) { - forEach(node.exportClause.elements, checkImportSymbol); + forEach(node.exportClause.elements, checkExportSpecifier); } } } + function checkExportSpecifier(node: ExportSpecifier) { + checkImportSymbol(node); + if (!(node.parent.parent).moduleSpecifier) { + markExportAsReferenced(node); + } + } + function checkExportAssignment(node: ExportAssignment) { - if (node.parent.kind === SyntaxKind.ModuleBlock && (node.parent.parent).name.kind === SyntaxKind.Identifier) { + var container = node.parent.kind === SyntaxKind.SourceFile ? node.parent : node.parent.parent; + if (container.kind === SyntaxKind.ModuleDeclaration && (container).name.kind === SyntaxKind.Identifier) { error(node, Diagnostics.An_export_assignment_cannot_be_used_in_an_internal_module); return; } @@ -9704,12 +9644,64 @@ module ts { if (!checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) { grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers); } - var container = node.parent; - if (container.kind !== SyntaxKind.SourceFile) { - // In a module, the immediate parent will be a block, so climb up one more parent - container = container.parent; + if (node.expression.kind === SyntaxKind.Identifier) { + markExportAsReferenced(node); + } + else { + checkExpressionCached(node.expression); + } + checkExternalModuleExports(container); + } + + function getModuleStatements(node: Declaration): ModuleElement[] { + if (node.kind === SyntaxKind.SourceFile) { + return (node).statements; + } + if (node.kind === SyntaxKind.ModuleDeclaration && (node).body.kind === SyntaxKind.ModuleBlock) { + return ((node).body).statements; + } + return emptyArray; + } + + function hasExportedMembers(moduleSymbol: Symbol) { + var declarations = moduleSymbol.declarations; + for (var i = 0; i < declarations.length; i++) { + var statements = getModuleStatements(declarations[i]); + for (var j = 0; j < statements.length; j++) { + var node = statements[j]; + if (node.kind === SyntaxKind.ExportDeclaration) { + var exportClause = (node).exportClause; + if (!exportClause) { + return true; + } + var specifiers = exportClause.elements; + for (var k = 0; k < specifiers.length; k++) { + var specifier = specifiers[k]; + if (!(specifier.propertyName && specifier.name && specifier.name.text === "default")) { + return true; + } + } + } + else if (node.kind !== SyntaxKind.ExportAssignment && node.flags & NodeFlags.Export) { + return true; + } + } + } + } + + function checkExternalModuleExports(node: SourceFile | ModuleDeclaration) { + var moduleSymbol = getSymbolOfNode(node); + var links = getSymbolLinks(moduleSymbol); + if (!links.exportsChecked) { + var defaultSymbol = getExportAssignmentSymbol(moduleSymbol); + if (defaultSymbol) { + if (hasExportedMembers(moduleSymbol)) { + var declaration = getDeclarationOfImportSymbol(defaultSymbol); + error(declaration, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements); + } + } + links.exportsChecked = true; } - checkTypeOfExportAssignmentSymbol(getSymbolOfNode(container)); } function checkSourceElement(node: Node): void { @@ -9928,13 +9920,7 @@ module ts { checkFunctionExpressionBodies(node); if (isExternalModule(node)) { - var symbol = getExportAssignmentSymbol(node.symbol); - if (symbol && symbol.flags & SymbolFlags.Import) { - // Mark the import as referenced so that we emit it in the final .js file. - getSymbolLinks(symbol).referenced = true; - // mark any import declarations that depend upon this import as referenced - markLinkedImportsAsReferenced(getDeclarationOfKind(symbol, SyntaxKind.ImportEqualsDeclaration)) - } + checkExternalModuleExports(node); } if (potentialThisCollisions.length) { @@ -10182,7 +10168,7 @@ module ts { } if (entityName.parent.kind === SyntaxKind.ExportAssignment) { - return resolveEntityName(/*location*/ entityName.parent.parent, entityName, + return resolveEntityName(entityName, /*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Import); } @@ -10207,7 +10193,7 @@ module ts { // Include Import in the meaning, this ensures that we do not follow aliases to where they point and instead // return the alias symbol. var meaning: SymbolFlags = SymbolFlags.Value | SymbolFlags.Import; - return resolveEntityName(entityName, entityName, meaning); + return resolveEntityName(entityName, meaning); } else if (entityName.kind === SyntaxKind.PropertyAccessExpression) { var symbol = getNodeLinks(entityName).resolvedSymbol; @@ -10229,7 +10215,7 @@ module ts { // Include Import in the meaning, this ensures that we do not follow aliases to where they point and instead // return the alias symbol. meaning |= SymbolFlags.Import; - return resolveEntityName(entityName, entityName, meaning); + return resolveEntityName(entityName, meaning); } // Do we want to return undefined here? @@ -10301,7 +10287,7 @@ module ts { // This is necessary as an identifier in short-hand property assignment can contains two meaning: // property name and property value. if (location && location.kind === SyntaxKind.ShorthandPropertyAssignment) { - return resolveEntityName(location, (location).name, SymbolFlags.Value); + return resolveEntityName((location).name, SymbolFlags.Value); } return undefined; } @@ -10567,9 +10553,9 @@ module ts { } } - function getExportAssignmentName(node: SourceFile): string { - var symbol = getExportAssignmentSymbol(getSymbolOfNode(node)); - return symbol && symbol !== unknownSymbol && symbolIsValue(symbol) && !isConstEnumSymbol(symbol) ? symbolToString(symbol): undefined; + function hasExportDefaultValue(node: SourceFile): boolean { + var symbol = getResolvedExportAssignmentSymbol(getSymbolOfNode(node)); + return symbol && symbol !== unknownSymbol && symbolIsValue(symbol) && !isConstEnumSymbol(symbol); } function isTopLevelValueImportEqualsWithEntityName(node: ImportEqualsDeclaration): boolean { @@ -10596,11 +10582,6 @@ module ts { 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; - } } return forEachChild(node, isReferencedImportDeclaration); } @@ -10676,7 +10657,7 @@ module ts { return { getGeneratedNameForNode, getExpressionNameSubstitution, - getExportAssignmentName, + hasExportDefaultValue, isReferencedImportDeclaration, getNodeCheckFlags, isTopLevelValueImportEqualsWithEntityName, diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 5d90b2245e53c..8551d8301e789 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1580,6 +1580,7 @@ module ts { var tempParameters: Identifier[]; var externalImports: ExternalImportInfo[]; var exportSpecifiers: Map; + var exportDefault: ExportAssignment | ExportSpecifier; /** write emitted output to disk*/ var writeEmittedFiles = writeJavaScriptFile; @@ -3446,7 +3447,7 @@ module ts { } function emitExportMemberAssignments(name: Identifier) { - if (exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { + if (!exportDefault && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { forEach(exportSpecifiers[name.text], specifier => { writeLine(); emitStart(specifier.name); @@ -4665,13 +4666,20 @@ module ts { function createExternalModuleInfo(sourceFile: SourceFile) { externalImports = []; exportSpecifiers = {}; + exportDefault = undefined; forEach(sourceFile.statements, node => { if (node.kind === SyntaxKind.ExportDeclaration && !(node).moduleSpecifier) { forEach((node).exportClause.elements, specifier => { + if (specifier.name.text === "default") { + exportDefault = exportDefault || specifier; + } var name = (specifier.propertyName || specifier.name).text; (exportSpecifiers[name] || (exportSpecifiers[name] = [])).push(specifier); }); } + else if (node.kind === SyntaxKind.ExportAssignment) { + exportDefault = exportDefault || node; + } else { var info = createExternalImportInfo(node); if (info) { @@ -4759,17 +4767,7 @@ module ts { emitCaptureThisForNodeIfNecessary(node); emitLinesStartingAt(node.statements, startIndex); emitTempDeclarations(/*newLine*/ true); - // TODO: Handle export default expressions - var exportName = resolver.getExportAssignmentName(node); - if (exportName) { - writeLine(); - var exportAssignment = getFirstExportAssignment(node); - emitStart(exportAssignment); - write("return "); - emit(exportAssignment.expression); - write(";"); - emitEnd(exportAssignment); - } + emitExportDefault(node, /*emitAsReturn*/ true); decreaseIndent(); writeLine(); write("});"); @@ -4779,16 +4777,22 @@ module ts { emitCaptureThisForNodeIfNecessary(node); emitLinesStartingAt(node.statements, startIndex); emitTempDeclarations(/*newLine*/ true); - // TODO: Handle export default expressions - var exportName = resolver.getExportAssignmentName(node); - if (exportName) { + emitExportDefault(node, /*emitAsReturn*/ false); + } + + function emitExportDefault(sourceFile: SourceFile, emitAsReturn: boolean) { + if (exportDefault && resolver.hasExportDefaultValue(sourceFile)) { writeLine(); - var exportAssignment = getFirstExportAssignment(node); - emitStart(exportAssignment); - write("module.exports = "); - emit(exportAssignment.expression); + emitStart(exportDefault); + write(emitAsReturn ? "return " : "module.exports = "); + if (exportDefault.kind === SyntaxKind.ExportAssignment) { + emit((exportDefault).expression); + } + else { + emit((exportDefault).propertyName); + } write(";"); - emitEnd(exportAssignment); + emitEnd(exportDefault); } } @@ -4845,6 +4849,7 @@ module ts { else { externalImports = undefined; exportSpecifiers = undefined; + exportDefault = undefined; emitCaptureThisForNodeIfNecessary(node); emitLinesStartingAt(node.statements, startIndex); emitTempDeclarations(/*newLine*/ true); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index bf022786f811c..5115fd0d25cd0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -863,11 +863,7 @@ module ts { members: NodeArray; } - export interface ExportContainer { - exportStars?: ExportDeclaration[]; // List of 'export *' statements (initialized by binding) - } - - export interface ModuleDeclaration extends Declaration, ModuleElement, ExportContainer { + export interface ModuleDeclaration extends Declaration, ModuleElement { name: Identifier | LiteralExpression; body: ModuleBlock | ModuleDeclaration; } @@ -912,7 +908,7 @@ module ts { name: Identifier; } - export interface ExportDeclaration extends Statement, ModuleElement { + export interface ExportDeclaration extends Declaration, ModuleElement { exportClause?: NamedExports; moduleSpecifier?: Expression; } @@ -932,7 +928,7 @@ module ts { export type ImportSpecifier = ImportOrExportSpecifier; export type ExportSpecifier = ImportOrExportSpecifier; - export interface ExportAssignment extends Statement, ModuleElement { + export interface ExportAssignment extends Declaration, ModuleElement { isExportEquals?: boolean; expression: Expression; } @@ -946,7 +942,7 @@ module ts { } // Source files are declarations when they are external modules. - export interface SourceFile extends Declaration, ExportContainer { + export interface SourceFile extends Declaration { statements: NodeArray; endOfFileToken: Node; @@ -1191,7 +1187,7 @@ module ts { export interface EmitResolver { getGeneratedNameForNode(node: ModuleDeclaration | EnumDeclaration | ImportDeclaration | ExportDeclaration): string; getExpressionNameSubstitution(node: Identifier): string; - getExportAssignmentName(node: SourceFile): string; + hasExportDefaultValue(node: SourceFile): boolean; isReferencedImportDeclaration(node: Node): boolean; isTopLevelValueImportEqualsWithEntityName(node: ImportEqualsDeclaration): boolean; getNodeCheckFlags(node: Node): NodeCheckFlags; @@ -1227,11 +1223,9 @@ module ts { Signature = 0x00020000, // Call, construct, or index signature TypeParameter = 0x00040000, // Type parameter TypeAlias = 0x00080000, // Type alias - - // Export markers (see comment in declareModuleMember in binder) - ExportValue = 0x00100000, // Exported value marker - ExportType = 0x00200000, // Exported type marker - ExportNamespace = 0x00400000, // Exported namespace marker + ExportValue = 0x00100000, // Exported value marker (see comment in declareModuleMember in binder) + ExportType = 0x00200000, // Exported type marker (see comment in declareModuleMember in binder) + ExportNamespace = 0x00400000, // Exported namespace marker (see comment in declareModuleMember in binder) Import = 0x00800000, // Import Instantiated = 0x01000000, // Instantiated symbol Merged = 0x02000000, // Merged symbol (created during program binding) @@ -1239,6 +1233,7 @@ module ts { Prototype = 0x08000000, // Prototype property (no source representation) UnionProperty = 0x10000000, // Property in union type Optional = 0x20000000, // Optional property + ExportStar = 0x40000000, // Export * declaration Enum = RegularEnum | ConstEnum, Variable = FunctionScopedVariable | BlockScopedVariable, @@ -1306,10 +1301,9 @@ module ts { declaredType?: Type; // Type of class, interface, enum, or type parameter mapper?: TypeMapper; // Type mapper for instantiation alias referenced?: boolean; // True if alias symbol has been referenced as a value - exportAssignmentChecked?: boolean; // True if export assignment was checked - exportAssignmentSymbol?: Symbol; // Symbol exported from external module unionType?: UnionType; // Containing union type for union property resolvedExports?: SymbolTable; // Resolved exports of module + exportsChecked?: boolean; // True if exports of external module have been checked } export interface TransientSymbol extends Symbol, SymbolLinks { }