diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5c371e7a34468..bc1a788236127 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -693,9 +693,14 @@ module ts { // 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.exportName.text) { - var meaning = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace; - var exportSymbol = resolveName(node, node.exportName.text, meaning, Diagnostics.Cannot_find_name_0, node.exportName); + 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; } @@ -9537,19 +9542,6 @@ module ts { if (!isInAmbientContext(node) && node.name.kind === SyntaxKind.StringLiteral) { grammarErrorOnNode(node.name, Diagnostics.Only_ambient_modules_can_use_quoted_names); } - else if (node.name.kind === SyntaxKind.Identifier && node.body.kind === SyntaxKind.ModuleBlock) { - var statements = (node.body).statements; - for (var i = 0, n = statements.length; i < n; i++) { - var statement = statements[i]; - - // TODO: AndersH: No reason to do a separate pass over the statements for this check, we should - // just fold it into checkExportAssignment. - if (statement.kind === SyntaxKind.ExportAssignment) { - // Export assignments are not allowed in an internal module - grammarErrorOnNode(statement, Diagnostics.An_export_assignment_cannot_be_used_in_an_internal_module); - } - } - } } checkCollisionWithCapturedThisVariable(node, node.name); @@ -9704,11 +9696,14 @@ module ts { } function checkExportAssignment(node: ExportAssignment) { + if (node.parent.kind === SyntaxKind.ModuleBlock && (node.parent.parent).name.kind === SyntaxKind.Identifier) { + error(node, Diagnostics.An_export_assignment_cannot_be_used_in_an_internal_module); + return; + } // Grammar checking 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 @@ -9906,6 +9901,7 @@ module ts { case SyntaxKind.ClassDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.EnumMember: + case SyntaxKind.ExportAssignment: case SyntaxKind.SourceFile: forEachChild(node, checkFunctionExpressionBodies); break; @@ -10165,7 +10161,7 @@ module ts { } if (nodeOnRightSide.parent.kind === SyntaxKind.ExportAssignment) { - return (nodeOnRightSide.parent).exportName === nodeOnRightSide && nodeOnRightSide.parent; + return (nodeOnRightSide.parent).expression === nodeOnRightSide && nodeOnRightSide.parent; } return undefined; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 3247b5ff1d117..5d90b2245e53c 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -700,8 +700,8 @@ module ts { } function emitExportAssignment(node: ExportAssignment) { - write("export = "); - writeTextOfNode(currentSourceFile, node.exportName); + write(node.isExportEquals ? "export = " : "export default "); + writeTextOfNode(currentSourceFile, node.expression); write(";"); writeLine(); } @@ -4759,15 +4759,14 @@ 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 "); - emitStart(exportAssignment.exportName); - write(exportName); - emitEnd(exportAssignment.exportName); + emit(exportAssignment.expression); write(";"); emitEnd(exportAssignment); } @@ -4780,15 +4779,14 @@ 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("module.exports = "); - emitStart(exportAssignment.exportName); - write(exportName); - emitEnd(exportAssignment.exportName); + emit(exportAssignment.expression); write(";"); emitEnd(exportAssignment); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9dc8a4755276f..abccfb96ffaa4 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -278,7 +278,7 @@ module ts { visitNode(cbNode, (node).name); case SyntaxKind.ExportAssignment: return visitNodes(cbNodes, node.modifiers) || - visitNode(cbNode, (node).exportName); + visitNode(cbNode, (node).expression); case SyntaxKind.TemplateExpression: return visitNode(cbNode, (node).head) || visitNodes(cbNodes, (node).templateSpans); case SyntaxKind.TemplateSpan: @@ -1500,6 +1500,10 @@ module ts { } if (token === SyntaxKind.ExportKeyword) { nextToken(); + if (token === SyntaxKind.DefaultKeyword) { + nextToken(); + return token === SyntaxKind.ClassKeyword || token === SyntaxKind.FunctionKeyword; + } return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.OpenBraceToken && canFollowModifier(); } nextToken(); @@ -4828,10 +4832,17 @@ module ts { return finishNode(node); } - function parseExportAssignmentTail(fullStart: number, modifiers: ModifiersArray): ExportAssignment { + function parseExportAssignment(fullStart: number, modifiers: ModifiersArray): ExportAssignment { var node = createNode(SyntaxKind.ExportAssignment, fullStart); setModifiers(node, modifiers); - node.exportName = parseIdentifier(); + if (parseOptional(SyntaxKind.EqualsToken)) { + node.isExportEquals = true; + } + else { + parseExpected(SyntaxKind.DefaultKeyword); + } + //node.exportName = parseIdentifier(); + node.expression = parseAssignmentExpressionOrHigher(); parseSemicolon(); return finishNode(node); } @@ -4898,7 +4909,7 @@ module ts { function nextTokenCanFollowExportKeyword() { nextToken(); return token === SyntaxKind.EqualsToken || token === SyntaxKind.AsteriskToken || - token === SyntaxKind.OpenBraceToken || isDeclarationStart(); + token === SyntaxKind.OpenBraceToken || token === SyntaxKind.DefaultKeyword || isDeclarationStart(); } function nextTokenIsDeclarationStart() { @@ -4915,8 +4926,8 @@ module ts { var modifiers = parseModifiers(); if (token === SyntaxKind.ExportKeyword) { nextToken(); - if (parseOptional(SyntaxKind.EqualsToken)) { - return parseExportAssignmentTail(fullStart, modifiers); + if (token === SyntaxKind.DefaultKeyword || token === SyntaxKind.EqualsToken) { + return parseExportAssignment(fullStart, modifiers); } if (token === SyntaxKind.AsteriskToken || token === SyntaxKind.OpenBraceToken) { return parseExportDeclaration(fullStart, modifiers); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5e5993fe4a239..bf022786f811c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -933,7 +933,8 @@ module ts { export type ExportSpecifier = ImportOrExportSpecifier; export interface ExportAssignment extends Statement, ModuleElement { - exportName: Identifier; + isExportEquals?: boolean; + expression: Expression; } export interface FileReference extends TextRange { diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index a4943705e72db..e3dd15a8f887e 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -174,7 +174,7 @@ module ts.BreakpointResolver { case SyntaxKind.ExportAssignment: // span on export = id - return textSpan(node, (node).exportName); + return textSpan(node, (node).expression); case SyntaxKind.ImportEqualsDeclaration: // import statement without including semicolon diff --git a/tests/cases/conformance/externalModules/exportAssignDottedName.ts b/tests/cases/conformance/externalModules/exportAssignDottedName.ts index 8499681a9a884..2d0783a636132 100644 --- a/tests/cases/conformance/externalModules/exportAssignDottedName.ts +++ b/tests/cases/conformance/externalModules/exportAssignDottedName.ts @@ -5,4 +5,4 @@ export function x(){ // @Filename: foo2.ts import foo1 = require('./foo1'); -export = foo1.x; // Error, export assignment must be identifier only +export = foo1.x; // Ok diff --git a/tests/cases/conformance/externalModules/exportAssignNonIdentifier.ts b/tests/cases/conformance/externalModules/exportAssignNonIdentifier.ts index 882fd16252fc8..c624cee840591 100644 --- a/tests/cases/conformance/externalModules/exportAssignNonIdentifier.ts +++ b/tests/cases/conformance/externalModules/exportAssignNonIdentifier.ts @@ -1,25 +1,25 @@ // @Filename: foo1.ts var x = 10; -export = typeof x; // Error +export = typeof x; // Ok // @Filename: foo2.ts -export = "sausages"; // Error +export = "sausages"; // Ok // @Filename: foo3.ts -export = class Foo3 {}; // Error +export = class Foo3 {}; // Error, not an expression // @Filename: foo4.ts -export = true; // Error +export = true; // Ok // @Filename: foo5.ts export = undefined; // Valid. undefined is an identifier in JavaScript/TypeScript // @Filename: foo6.ts -export = void; // Error +export = void; // Error, void operator requires an argument // @Filename: foo7.ts -export = Date || String; // Error +export = Date || String; // Ok // @Filename: foo8.ts -export = null; // Error +export = null; // Ok