Skip to content

Commit 0e8b6df

Browse files
committed
Support for 'export default' with expressions
1 parent a0eff60 commit 0e8b6df

File tree

7 files changed

+48
-42
lines changed

7 files changed

+48
-42
lines changed

src/compiler/checker.ts

+14-18
Original file line numberDiff line numberDiff line change
@@ -693,9 +693,14 @@ module ts {
693693
// The two types of exports are mutually exclusive.
694694
error(node, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements);
695695
}
696-
if (node.exportName.text) {
697-
var meaning = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace;
698-
var exportSymbol = resolveName(node, node.exportName.text, meaning, Diagnostics.Cannot_find_name_0, node.exportName);
696+
if (node.expression.kind === SyntaxKind.Identifier && (<Identifier>node.expression).text) {
697+
var exportSymbol = resolveName(node, (<Identifier>node.expression).text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace,
698+
Diagnostics.Cannot_find_name_0, <Identifier>node.expression);
699+
}
700+
else {
701+
var exportSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "*default*");
702+
exportSymbol.parent = containerSymbol;
703+
(<TransientSymbol>exportSymbol).type = checkExpression(node.expression);
699704
}
700705
symbolLinks.exportAssignmentSymbol = exportSymbol || unknownSymbol;
701706
}
@@ -9537,19 +9542,6 @@ module ts {
95379542
if (!isInAmbientContext(node) && node.name.kind === SyntaxKind.StringLiteral) {
95389543
grammarErrorOnNode(node.name, Diagnostics.Only_ambient_modules_can_use_quoted_names);
95399544
}
9540-
else if (node.name.kind === SyntaxKind.Identifier && node.body.kind === SyntaxKind.ModuleBlock) {
9541-
var statements = (<ModuleBlock>node.body).statements;
9542-
for (var i = 0, n = statements.length; i < n; i++) {
9543-
var statement = statements[i];
9544-
9545-
// TODO: AndersH: No reason to do a separate pass over the statements for this check, we should
9546-
// just fold it into checkExportAssignment.
9547-
if (statement.kind === SyntaxKind.ExportAssignment) {
9548-
// Export assignments are not allowed in an internal module
9549-
grammarErrorOnNode(statement, Diagnostics.An_export_assignment_cannot_be_used_in_an_internal_module);
9550-
}
9551-
}
9552-
}
95539545
}
95549546

95559547
checkCollisionWithCapturedThisVariable(node, node.name);
@@ -9704,11 +9696,14 @@ module ts {
97049696
}
97059697

97069698
function checkExportAssignment(node: ExportAssignment) {
9699+
if (node.parent.kind === SyntaxKind.ModuleBlock && (<ModuleDeclaration>node.parent.parent).name.kind === SyntaxKind.Identifier) {
9700+
error(node, Diagnostics.An_export_assignment_cannot_be_used_in_an_internal_module);
9701+
return;
9702+
}
97079703
// Grammar checking
97089704
if (!checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) {
97099705
grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers);
97109706
}
9711-
97129707
var container = node.parent;
97139708
if (container.kind !== SyntaxKind.SourceFile) {
97149709
// In a module, the immediate parent will be a block, so climb up one more parent
@@ -9906,6 +9901,7 @@ module ts {
99069901
case SyntaxKind.ClassDeclaration:
99079902
case SyntaxKind.EnumDeclaration:
99089903
case SyntaxKind.EnumMember:
9904+
case SyntaxKind.ExportAssignment:
99099905
case SyntaxKind.SourceFile:
99109906
forEachChild(node, checkFunctionExpressionBodies);
99119907
break;
@@ -10165,7 +10161,7 @@ module ts {
1016510161
}
1016610162

1016710163
if (nodeOnRightSide.parent.kind === SyntaxKind.ExportAssignment) {
10168-
return (<ExportAssignment>nodeOnRightSide.parent).exportName === nodeOnRightSide && <ExportAssignment>nodeOnRightSide.parent;
10164+
return (<ExportAssignment>nodeOnRightSide.parent).expression === <Node>nodeOnRightSide && <ExportAssignment>nodeOnRightSide.parent;
1016910165
}
1017010166

1017110167
return undefined;

src/compiler/emitter.ts

+6-8
Original file line numberDiff line numberDiff line change
@@ -700,8 +700,8 @@ module ts {
700700
}
701701

702702
function emitExportAssignment(node: ExportAssignment) {
703-
write("export = ");
704-
writeTextOfNode(currentSourceFile, node.exportName);
703+
write(node.isExportEquals ? "export = " : "export default ");
704+
writeTextOfNode(currentSourceFile, node.expression);
705705
write(";");
706706
writeLine();
707707
}
@@ -4759,15 +4759,14 @@ module ts {
47594759
emitCaptureThisForNodeIfNecessary(node);
47604760
emitLinesStartingAt(node.statements, startIndex);
47614761
emitTempDeclarations(/*newLine*/ true);
4762+
// TODO: Handle export default expressions
47624763
var exportName = resolver.getExportAssignmentName(node);
47634764
if (exportName) {
47644765
writeLine();
47654766
var exportAssignment = getFirstExportAssignment(node);
47664767
emitStart(exportAssignment);
47674768
write("return ");
4768-
emitStart(exportAssignment.exportName);
4769-
write(exportName);
4770-
emitEnd(exportAssignment.exportName);
4769+
emit(exportAssignment.expression);
47714770
write(";");
47724771
emitEnd(exportAssignment);
47734772
}
@@ -4780,15 +4779,14 @@ module ts {
47804779
emitCaptureThisForNodeIfNecessary(node);
47814780
emitLinesStartingAt(node.statements, startIndex);
47824781
emitTempDeclarations(/*newLine*/ true);
4782+
// TODO: Handle export default expressions
47834783
var exportName = resolver.getExportAssignmentName(node);
47844784
if (exportName) {
47854785
writeLine();
47864786
var exportAssignment = getFirstExportAssignment(node);
47874787
emitStart(exportAssignment);
47884788
write("module.exports = ");
4789-
emitStart(exportAssignment.exportName);
4790-
write(exportName);
4791-
emitEnd(exportAssignment.exportName);
4789+
emit(exportAssignment.expression);
47924790
write(";");
47934791
emitEnd(exportAssignment);
47944792
}

src/compiler/parser.ts

+17-6
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ module ts {
278278
visitNode(cbNode, (<ImportOrExportSpecifier>node).name);
279279
case SyntaxKind.ExportAssignment:
280280
return visitNodes(cbNodes, node.modifiers) ||
281-
visitNode(cbNode, (<ExportAssignment>node).exportName);
281+
visitNode(cbNode, (<ExportAssignment>node).expression);
282282
case SyntaxKind.TemplateExpression:
283283
return visitNode(cbNode, (<TemplateExpression>node).head) || visitNodes(cbNodes, (<TemplateExpression>node).templateSpans);
284284
case SyntaxKind.TemplateSpan:
@@ -1500,6 +1500,10 @@ module ts {
15001500
}
15011501
if (token === SyntaxKind.ExportKeyword) {
15021502
nextToken();
1503+
if (token === SyntaxKind.DefaultKeyword) {
1504+
nextToken();
1505+
return token === SyntaxKind.ClassKeyword || token === SyntaxKind.FunctionKeyword;
1506+
}
15031507
return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.OpenBraceToken && canFollowModifier();
15041508
}
15051509
nextToken();
@@ -4828,10 +4832,17 @@ module ts {
48284832
return finishNode(node);
48294833
}
48304834

4831-
function parseExportAssignmentTail(fullStart: number, modifiers: ModifiersArray): ExportAssignment {
4835+
function parseExportAssignment(fullStart: number, modifiers: ModifiersArray): ExportAssignment {
48324836
var node = <ExportAssignment>createNode(SyntaxKind.ExportAssignment, fullStart);
48334837
setModifiers(node, modifiers);
4834-
node.exportName = parseIdentifier();
4838+
if (parseOptional(SyntaxKind.EqualsToken)) {
4839+
node.isExportEquals = true;
4840+
}
4841+
else {
4842+
parseExpected(SyntaxKind.DefaultKeyword);
4843+
}
4844+
//node.exportName = parseIdentifier();
4845+
node.expression = parseAssignmentExpressionOrHigher();
48354846
parseSemicolon();
48364847
return finishNode(node);
48374848
}
@@ -4898,7 +4909,7 @@ module ts {
48984909
function nextTokenCanFollowExportKeyword() {
48994910
nextToken();
49004911
return token === SyntaxKind.EqualsToken || token === SyntaxKind.AsteriskToken ||
4901-
token === SyntaxKind.OpenBraceToken || isDeclarationStart();
4912+
token === SyntaxKind.OpenBraceToken || token === SyntaxKind.DefaultKeyword || isDeclarationStart();
49024913
}
49034914

49044915
function nextTokenIsDeclarationStart() {
@@ -4915,8 +4926,8 @@ module ts {
49154926
var modifiers = parseModifiers();
49164927
if (token === SyntaxKind.ExportKeyword) {
49174928
nextToken();
4918-
if (parseOptional(SyntaxKind.EqualsToken)) {
4919-
return parseExportAssignmentTail(fullStart, modifiers);
4929+
if (token === SyntaxKind.DefaultKeyword || token === SyntaxKind.EqualsToken) {
4930+
return parseExportAssignment(fullStart, modifiers);
49204931
}
49214932
if (token === SyntaxKind.AsteriskToken || token === SyntaxKind.OpenBraceToken) {
49224933
return parseExportDeclaration(fullStart, modifiers);

src/compiler/types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,8 @@ module ts {
933933
export type ExportSpecifier = ImportOrExportSpecifier;
934934

935935
export interface ExportAssignment extends Statement, ModuleElement {
936-
exportName: Identifier;
936+
isExportEquals?: boolean;
937+
expression: Expression;
937938
}
938939

939940
export interface FileReference extends TextRange {

src/services/breakpoints.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ module ts.BreakpointResolver {
174174

175175
case SyntaxKind.ExportAssignment:
176176
// span on export = id
177-
return textSpan(node, (<ExportAssignment>node).exportName);
177+
return textSpan(node, (<ExportAssignment>node).expression);
178178

179179
case SyntaxKind.ImportEqualsDeclaration:
180180
// import statement without including semicolon

tests/cases/conformance/externalModules/exportAssignDottedName.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ export function x(){
55

66
// @Filename: foo2.ts
77
import foo1 = require('./foo1');
8-
export = foo1.x; // Error, export assignment must be identifier only
8+
export = foo1.x; // Ok
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
// @Filename: foo1.ts
22
var x = 10;
3-
export = typeof x; // Error
3+
export = typeof x; // Ok
44

55
// @Filename: foo2.ts
6-
export = "sausages"; // Error
6+
export = "sausages"; // Ok
77

88
// @Filename: foo3.ts
9-
export = class Foo3 {}; // Error
9+
export = class Foo3 {}; // Error, not an expression
1010

1111
// @Filename: foo4.ts
12-
export = true; // Error
12+
export = true; // Ok
1313

1414
// @Filename: foo5.ts
1515
export = undefined; // Valid. undefined is an identifier in JavaScript/TypeScript
1616

1717
// @Filename: foo6.ts
18-
export = void; // Error
18+
export = void; // Error, void operator requires an argument
1919

2020
// @Filename: foo7.ts
21-
export = Date || String; // Error
21+
export = Date || String; // Ok
2222

2323
// @Filename: foo8.ts
24-
export = null; // Error
24+
export = null; // Ok
2525

0 commit comments

Comments
 (0)