Support for 'export default' with expressions

This commit is contained in:
Anders Hejlsberg
2015-02-25 16:03:03 -08:00
parent a0eff6033e
commit 0e8b6dfdf4
7 changed files with 48 additions and 42 deletions
+14 -18
View File
@@ -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 && (<Identifier>node.expression).text) {
var exportSymbol = resolveName(node, (<Identifier>node.expression).text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace,
Diagnostics.Cannot_find_name_0, <Identifier>node.expression);
}
else {
var exportSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "*default*");
exportSymbol.parent = containerSymbol;
(<TransientSymbol>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 = (<ModuleBlock>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 && (<ModuleDeclaration>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 (<ExportAssignment>nodeOnRightSide.parent).exportName === nodeOnRightSide && <ExportAssignment>nodeOnRightSide.parent;
return (<ExportAssignment>nodeOnRightSide.parent).expression === <Node>nodeOnRightSide && <ExportAssignment>nodeOnRightSide.parent;
}
return undefined;
+6 -8
View File
@@ -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);
}
+17 -6
View File
@@ -278,7 +278,7 @@ module ts {
visitNode(cbNode, (<ImportOrExportSpecifier>node).name);
case SyntaxKind.ExportAssignment:
return visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (<ExportAssignment>node).exportName);
visitNode(cbNode, (<ExportAssignment>node).expression);
case SyntaxKind.TemplateExpression:
return visitNode(cbNode, (<TemplateExpression>node).head) || visitNodes(cbNodes, (<TemplateExpression>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 = <ExportAssignment>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);
+2 -1
View File
@@ -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 {
+1 -1
View File
@@ -174,7 +174,7 @@ module ts.BreakpointResolver {
case SyntaxKind.ExportAssignment:
// span on export = id
return textSpan(node, (<ExportAssignment>node).exportName);
return textSpan(node, (<ExportAssignment>node).expression);
case SyntaxKind.ImportEqualsDeclaration:
// import statement without including semicolon
@@ -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
@@ -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