From 79be0a7d26bcedc2d59b121f5f3cbc47923f5854 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 12 Feb 2015 18:05:02 -0800 Subject: [PATCH] Support for ES6 export declarations (except export default and export *) --- src/compiler/binder.ts | 58 +++++++------- src/compiler/checker.ts | 47 ++++++++--- src/compiler/emitter.ts | 122 +++++++++++++++++++++++------ src/compiler/parser.ts | 161 ++++++++++++++++++++++++-------------- src/compiler/program.ts | 4 +- src/compiler/types.ts | 28 +++++-- src/compiler/utilities.ts | 6 +- 7 files changed, 293 insertions(+), 133 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5a4a983537a..4bd562291d5 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -15,11 +15,11 @@ module ts { if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.TypeAliasDeclaration) { return ModuleInstanceState.NonInstantiated; } - // 2. const enum declarations don't make module instantiated + // 2. const enum declarations else if (isConstEnumDeclaration(node)) { return ModuleInstanceState.ConstEnumOnly; } - // 3. non - exported import declarations + // 3. non-exported import declarations else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && !(node.flags & NodeFlags.Export)) { return ModuleInstanceState.NonInstantiated; } @@ -185,42 +185,39 @@ module ts { } function declareModuleMember(node: Declaration, symbolKind: SymbolFlags, symbolExcludes: SymbolFlags) { - // Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue, - // ExportType, or ExportContainer flag, and an associated export symbol with all the correct flags set - // on it. There are 2 main reasons: - // - // 1. We treat locals and exports of the same name as mutually exclusive within a container. - // That means the binder will issue a Duplicate Identifier error if you mix locals and exports - // with the same name in the same container. - // TODO: Make this a more specific error and decouple it from the exclusion logic. - // 2. When we checkIdentifier in the checker, we set its resolved symbol to the local symbol, - // but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way - // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope. - var exportKind = 0; - if (symbolKind & SymbolFlags.Value) { - exportKind |= SymbolFlags.ExportValue; + var hasExportModifier = getCombinedNodeFlags(node) & NodeFlags.Export; + if (symbolKind & SymbolFlags.Import) { + if (node.kind === SyntaxKind.ExportSpecifier || (node.kind === SyntaxKind.ImportEqualsDeclaration && hasExportModifier)) { + declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes); + } + else { + declareSymbol(container.locals, undefined, node, symbolKind, symbolExcludes); + } } - if (symbolKind & SymbolFlags.Type) { - exportKind |= SymbolFlags.ExportType; - } - if (symbolKind & SymbolFlags.Namespace) { - exportKind |= SymbolFlags.ExportNamespace; - } - - if (getCombinedNodeFlags(node) & NodeFlags.Export || - (node.kind !== SyntaxKind.ImportDeclaration && node.kind !== SyntaxKind.ImportEqualsDeclaration && isAmbientContext(container))) { - if (exportKind) { + else { + // Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue, + // ExportType, or ExportContainer flag, and an associated export symbol with all the correct flags set + // on it. There are 2 main reasons: + // + // 1. We treat locals and exports of the same name as mutually exclusive within a container. + // That means the binder will issue a Duplicate Identifier error if you mix locals and exports + // with the same name in the same container. + // TODO: Make this a more specific error and decouple it from the exclusion logic. + // 2. When we checkIdentifier in the checker, we set its resolved symbol to the local symbol, + // but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way + // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope. + if (hasExportModifier || isAmbientContext(container)) { + var exportKind = (symbolKind & SymbolFlags.Value ? SymbolFlags.ExportValue : 0) | + (symbolKind & SymbolFlags.Type ? SymbolFlags.ExportType : 0) | + (symbolKind & SymbolFlags.Namespace ? SymbolFlags.ExportNamespace : 0); var local = declareSymbol(container.locals, undefined, node, exportKind, symbolExcludes); local.exportSymbol = declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes); node.localSymbol = local; } else { - declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes); + declareSymbol(container.locals, undefined, node, symbolKind, symbolExcludes); } } - else { - declareSymbol(container.locals, undefined, node, symbolKind, symbolExcludes); - } } // All container nodes are kept on a linked list in declaration order. This list is used by the getLocalNameOfContainer function @@ -477,6 +474,7 @@ module ts { case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.NamespaceImport: case SyntaxKind.ImportSpecifier: + case SyntaxKind.ExportSpecifier: bindDeclaration(node, SymbolFlags.Import, SymbolFlags.ImportExcludes, /*isBlockScopeContainer*/ false); break; case SyntaxKind.ImportClause: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 11b9de61b26..743a39b48fc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -446,7 +446,8 @@ module ts { return node.kind === SyntaxKind.ImportEqualsDeclaration || node.kind === SyntaxKind.ImportClause && !!(node).name || node.kind === SyntaxKind.NamespaceImport || - node.kind === SyntaxKind.ImportSpecifier; + node.kind === SyntaxKind.ImportSpecifier || + node.kind === SyntaxKind.ExportSpecifier; } function getDeclarationOfImportSymbol(symbol: Symbol): Declaration { @@ -477,10 +478,10 @@ module ts { return resolveExternalModuleName(node, (node.parent.parent).moduleSpecifier); } - function getTargetOfImportSpecifier(node: ImportSpecifier): Symbol { - var moduleSymbol = resolveExternalModuleName(node, (node.parent.parent.parent).moduleSpecifier); + function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration, specifier: ImportOrExportSpecifier): Symbol { + var moduleSymbol = resolveExternalModuleName(node, node.moduleSpecifier); if (moduleSymbol) { - var name = node.propertyName || node.name; + var name = specifier.propertyName || specifier.name; if (name.text) { var symbol = getSymbol(moduleSymbol.exports, name.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace); if (!symbol) { @@ -492,6 +493,16 @@ module ts { } } + function getTargetOfImportSpecifier(node: ImportSpecifier): Symbol { + return getExternalModuleMember(node.parent.parent.parent, node); + } + + 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); + } + function getTargetOfImportDeclaration(node: Declaration): Symbol { switch (node.kind) { case SyntaxKind.ImportEqualsDeclaration: @@ -502,6 +513,8 @@ module ts { return getTargetOfNamespaceImport(node); case SyntaxKind.ImportSpecifier: return getTargetOfImportSpecifier(node); + case SyntaxKind.ExportSpecifier: + return getTargetOfExportSpecifier(node); } } @@ -9346,7 +9359,7 @@ module ts { } function checkExternalImportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration): boolean { - var moduleName = getImportedModuleName(node); + var moduleName = getExternalModuleName(node); if (getFullWidth(moduleName) !== 0 && moduleName.kind !== SyntaxKind.StringLiteral) { error(moduleName, Diagnostics.String_literal_expected); return false; @@ -10174,6 +10187,9 @@ module ts { case SyntaxKind.ImportDeclaration: generateNameForImportDeclaration(node); break; + case SyntaxKind.ExportDeclaration: + generateNameForExportDeclaration(node); + break; case SyntaxKind.SourceFile: case SyntaxKind.ModuleBlock: forEach((node).statements, generateNames); @@ -10219,17 +10235,27 @@ module ts { } } + function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) { + var expr = getExternalModuleName(node); + var baseName = expr.kind === SyntaxKind.StringLiteral ? + escapeIdentifier(makeIdentifierFromModuleName((expr).text)) : "module"; + assignGeneratedName(node, makeUniqueName(baseName)); + } + function generateNameForImportDeclaration(node: ImportDeclaration) { if (node.importClause && node.importClause.namedBindings && node.importClause.namedBindings.kind === SyntaxKind.NamedImports) { - var expr = getImportedModuleName(node); - var baseName = expr.kind === SyntaxKind.StringLiteral ? - escapeIdentifier(makeIdentifierFromModuleName((expr).text)) : "module"; - assignGeneratedName(node, makeUniqueName(baseName)); + generateNameForImportOrExportDeclaration(node); + } + } + + function generateNameForExportDeclaration(node: ExportDeclaration) { + if (node.moduleSpecifier) { + generateNameForImportOrExportDeclaration(node); } } } - function getGeneratedNameForNode(node: ModuleDeclaration | EnumDeclaration | ImportDeclaration) { + function getGeneratedNameForNode(node: ModuleDeclaration | EnumDeclaration | ImportDeclaration | ExportDeclaration) { var links = getNodeLinks(node); if (!links.generatedName) { getGeneratedNamesForSourceFile(getSourceFile(node)); @@ -11322,6 +11348,7 @@ module ts { if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration || + node.kind === SyntaxKind.ExportDeclaration || node.kind === SyntaxKind.ExportAssignment || (node.flags & NodeFlags.Ambient)) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index b05dd62a86f..552c7201279 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -17,7 +17,7 @@ module ts { } interface ExternalImportInfo { - importNode: ImportDeclaration | ImportEqualsDeclaration; + rootNode: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration; declarationNode?: ImportEqualsDeclaration | ImportClause | NamespaceImport; namedImports?: NamedImports; } @@ -1568,6 +1568,7 @@ module ts { var tempVariables: Identifier[]; var tempParameters: Identifier[]; var externalImports: ExternalImportInfo[]; + var exportSpecifiers: Map; /** write emitted output to disk*/ var writeEmittedFiles = writeJavaScriptFile; @@ -3067,6 +3068,20 @@ module ts { emitEnd(node.name); } + function emitExportMemberAssignment(name: Identifier) { + if (exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { + var exportName = exportSpecifiers[name.text].name; + writeLine(); + emitStart(exportName); + write("exports."); + emitNode(exportName); + emitEnd(exportName); + write(" = "); + emitNode(name); + write(";"); + } + } + function emitDestructuring(root: BinaryExpression | VariableDeclaration | ParameterDeclaration, value?: Expression) { var emitCount = 0; // An exported declaration is actually emitted as an assignment (to a property on the module object), so @@ -3299,6 +3314,16 @@ module ts { } } + function emitExportVariableAssignments(node: VariableDeclaration | BindingElement) { + var name = (node).name; + if (name.kind === SyntaxKind.Identifier) { + emitExportMemberAssignment(name); + } + else if (isBindingPattern(name)) { + forEach((name).elements, emitExportVariableAssignments); + } + } + function emitVariableStatement(node: VariableStatement) { if (!(node.flags & NodeFlags.Export)) { if (isLet(node.declarationList)) { @@ -3313,6 +3338,9 @@ module ts { } emitCommaList(node.declarationList.declarations); write(";"); + if (languageVersion < ScriptTarget.ES6 && node.parent === currentSourceFile) { + forEach(node.declarationList.declarations, emitExportVariableAssignments); + } } function emitParameter(node: ParameterDeclaration) { @@ -3437,6 +3465,9 @@ module ts { emit(node.name); } emitSignatureAndBody(node); + if (languageVersion < ScriptTarget.ES6 && node.kind === SyntaxKind.FunctionDeclaration && node.parent === currentSourceFile) { + emitExportMemberAssignment((node).name); + } if (node.kind !== SyntaxKind.MethodDeclaration && node.kind !== SyntaxKind.MethodSignature) { emitTrailingComments(node); } @@ -3773,6 +3804,9 @@ module ts { emitEnd(node); write(";"); } + if (languageVersion < ScriptTarget.ES6 && node.parent === currentSourceFile) { + emitExportMemberAssignment(node.name); + } function emitConstructorOfClass() { var saveTempCount = tempCount; @@ -3899,6 +3933,9 @@ module ts { emitEnd(node); write(";"); } + if (languageVersion < ScriptTarget.ES6 && node.parent === currentSourceFile) { + emitExportMemberAssignment(node.name); + } } function emitEnumMember(node: EnumMember) { @@ -3997,6 +4034,9 @@ module ts { emitModuleMemberName(node); write(" = {}));"); emitEnd(node); + if (languageVersion < ScriptTarget.ES6 && node.name.kind === SyntaxKind.Identifier && node.parent === currentSourceFile) { + emitExportMemberAssignment(node.name); + } } function emitRequire(moduleName: Expression) { @@ -4013,13 +4053,6 @@ module ts { } } - function emitImportAssignment(node: Declaration, moduleName: Expression) { - if (!(node.flags & NodeFlags.Export)) write("var "); - emitModuleMemberName(node); - write(" = "); - emitRequire(moduleName); - } - function emitImportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration) { var info = getExternalImportInfo(node); if (info) { @@ -4028,7 +4061,7 @@ module ts { if (compilerOptions.module !== ModuleKind.AMD) { emitLeadingComments(node); emitStart(node); - var moduleName = getImportedModuleName(node); + var moduleName = getExternalModuleName(node); if (declarationNode) { if (!(declarationNode.flags & NodeFlags.Export)) write("var "); emitModuleMemberName(declarationNode); @@ -4082,11 +4115,35 @@ module ts { } } + function emitExportDeclaration(node: ExportDeclaration) { + if (node.exportClause && node.moduleSpecifier) { + var generatedName = resolver.getGeneratedNameForNode(node); + emitStart(node); + write("var "); + write(generatedName); + write(" = "); + emitRequire(getExternalModuleName(node)); + forEach(node.exportClause.elements, specifier => { + writeLine(); + emitStart(specifier); + write("exports."); + emitNode(specifier.name); + write(" = "); + write(generatedName); + write("."); + emitNode(specifier.propertyName || specifier.name); + write(";"); + emitEnd(specifier); + }); + emitEnd(node); + } + } + function createExternalImportInfo(node: Node): ExternalImportInfo { if (node.kind === SyntaxKind.ImportEqualsDeclaration) { if ((node).moduleReference.kind === SyntaxKind.ExternalModuleReference) { return { - importNode: node, + rootNode: node, declarationNode: node }; } @@ -4096,35 +4153,50 @@ module ts { if (importClause) { if (importClause.name) { return { - importNode: node, + rootNode: node, declarationNode: importClause }; } if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { return { - importNode: node, + rootNode: node, declarationNode: importClause.namedBindings }; } return { - importNode: node, + rootNode: node, namedImports: importClause.namedBindings, localName: resolver.getGeneratedNameForNode(node) }; } return { - importNode: node + rootNode: node + } + } + else if (node.kind === SyntaxKind.ExportDeclaration) { + if ((node).moduleSpecifier) { + return { + rootNode: node, + }; } } } - function createExternalImports(sourceFile: SourceFile) { + function createExternalModuleInfo(sourceFile: SourceFile) { externalImports = []; + exportSpecifiers = {}; forEach(sourceFile.statements, node => { - var info = createExternalImportInfo(node); - if (info) { - if ((!info.declarationNode && !info.namedImports) || resolver.isReferencedImportDeclaration(node)) { - externalImports.push(info); + if (node.kind === SyntaxKind.ExportDeclaration && !(node).moduleSpecifier) { + forEach((node).exportClause.elements, e => { + exportSpecifiers[(e.propertyName || e.name).text] = e; + }); + } + else { + var info = createExternalImportInfo(node); + if (info) { + if ((!info.declarationNode && !info.namedImports) || resolver.isReferencedImportDeclaration(node)) { + externalImports.push(info); + } } } }); @@ -4134,7 +4206,7 @@ module ts { if (externalImports) { for (var i = 0; i < externalImports.length; i++) { var info = externalImports[i]; - if (info.importNode === node) { + if (info.rootNode === node) { return info; } } @@ -4158,7 +4230,7 @@ module ts { write("[\"require\", \"exports\""); forEach(externalImports, info => { write(", "); - var moduleName = getImportedModuleName(info.importNode); + var moduleName = getExternalModuleName(info.rootNode); if (moduleName.kind === SyntaxKind.StringLiteral) { emitLiteral(moduleName); } @@ -4178,7 +4250,7 @@ module ts { emit(info.declarationNode.name); } else { - write(resolver.getGeneratedNameForNode(info.importNode)); + write(resolver.getGeneratedNameForNode(info.rootNode)); } }); write(") {"); @@ -4263,7 +4335,7 @@ module ts { extendsEmitted = true; } if (isExternalModule(node)) { - createExternalImports(node); + createExternalModuleInfo(node); if (compilerOptions.module === ModuleKind.AMD) { emitAMDModule(node, startIndex); } @@ -4272,6 +4344,8 @@ module ts { } } else { + externalImports = undefined; + exportSpecifiers = undefined; emitCaptureThisForNodeIfNecessary(node); emitLinesStartingAt(node.statements, startIndex); emitTempDeclarations(/*newLine*/ true); @@ -4474,6 +4548,8 @@ module ts { return emitImportDeclaration(node); case SyntaxKind.ImportEqualsDeclaration: return emitImportEqualsDeclaration(node); + case SyntaxKind.ExportDeclaration: + return emitExportDeclaration(node); case SyntaxKind.SourceFile: return emitSourceFile(node); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 76b1f2cb32b..a9e2a98bab8 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -261,10 +261,16 @@ module ts { case SyntaxKind.NamespaceImport: return visitNode(cbNode, (node).name); case SyntaxKind.NamedImports: - return visitNodes(cbNodes, (node).elements); + case SyntaxKind.NamedExports: + return visitNodes(cbNodes, (node).elements); + case SyntaxKind.ExportDeclaration: + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).exportClause) || + visitNode(cbNode, (node).moduleSpecifier); case SyntaxKind.ImportSpecifier: - return visitNode(cbNode, (node).propertyName) || - visitNode(cbNode, (node).name); + case SyntaxKind.ExportSpecifier: + return visitNode(cbNode, (node).propertyName) || + visitNode(cbNode, (node).name); case SyntaxKind.ExportAssignment: return visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).exportName); @@ -282,28 +288,28 @@ module ts { } const enum ParsingContext { - SourceElements, // Elements in source file - ModuleElements, // Elements in module declaration - BlockStatements, // Statements in block - SwitchClauses, // Clauses in switch statement - SwitchClauseStatements, // Statements in switch clause - TypeMembers, // Members in interface or type literal - ClassMembers, // Members in class declaration - EnumMembers, // Members in enum declaration - TypeReferences, // Type references in extends or implements clause - VariableDeclarations, // Variable declarations in variable statement - ObjectBindingElements, // Binding elements in object binding list - ArrayBindingElements, // Binding elements in array binding list - ArgumentExpressions, // Expressions in argument list - ObjectLiteralMembers, // Members in object literal - ArrayLiteralMembers, // Members in array literal - Parameters, // Parameters in parameter list - TypeParameters, // Type parameters in type parameter list - TypeArguments, // Type arguments in type argument list - TupleElementTypes, // Element types in tuple element type list - HeritageClauses, // Heritage clauses for a class or interface declaration. - ImportSpecifiers, // Named import clause's import specifier list - Count // Number of parsing contexts + SourceElements, // Elements in source file + ModuleElements, // Elements in module declaration + BlockStatements, // Statements in block + SwitchClauses, // Clauses in switch statement + SwitchClauseStatements, // Statements in switch clause + TypeMembers, // Members in interface or type literal + ClassMembers, // Members in class declaration + EnumMembers, // Members in enum declaration + TypeReferences, // Type references in extends or implements clause + VariableDeclarations, // Variable declarations in variable statement + ObjectBindingElements, // Binding elements in object binding list + ArrayBindingElements, // Binding elements in array binding list + ArgumentExpressions, // Expressions in argument list + ObjectLiteralMembers, // Members in object literal + ArrayLiteralMembers, // Members in array literal + Parameters, // Parameters in parameter list + TypeParameters, // Type parameters in type parameter list + TypeArguments, // Type arguments in type argument list + TupleElementTypes, // Element types in tuple element type list + HeritageClauses, // Heritage clauses for a class or interface declaration. + ImportOrExportSpecifiers, // Named import clause's import specifier list + Count // Number of parsing contexts } const enum Tristate { @@ -314,27 +320,27 @@ module ts { function parsingContextErrors(context: ParsingContext): DiagnosticMessage { switch (context) { - case ParsingContext.SourceElements: return Diagnostics.Declaration_or_statement_expected; - case ParsingContext.ModuleElements: return Diagnostics.Declaration_or_statement_expected; - case ParsingContext.BlockStatements: return Diagnostics.Statement_expected; - case ParsingContext.SwitchClauses: return Diagnostics.case_or_default_expected; - case ParsingContext.SwitchClauseStatements: return Diagnostics.Statement_expected; - case ParsingContext.TypeMembers: return Diagnostics.Property_or_signature_expected; - case ParsingContext.ClassMembers: return Diagnostics.Unexpected_token_A_constructor_method_accessor_or_property_was_expected; - case ParsingContext.EnumMembers: return Diagnostics.Enum_member_expected; - case ParsingContext.TypeReferences: return Diagnostics.Type_reference_expected; - case ParsingContext.VariableDeclarations: return Diagnostics.Variable_declaration_expected; - case ParsingContext.ObjectBindingElements: return Diagnostics.Property_destructuring_pattern_expected; - case ParsingContext.ArrayBindingElements: return Diagnostics.Array_element_destructuring_pattern_expected; - case ParsingContext.ArgumentExpressions: return Diagnostics.Argument_expression_expected; - case ParsingContext.ObjectLiteralMembers: return Diagnostics.Property_assignment_expected; - case ParsingContext.ArrayLiteralMembers: return Diagnostics.Expression_or_comma_expected; - case ParsingContext.Parameters: return Diagnostics.Parameter_declaration_expected; - case ParsingContext.TypeParameters: return Diagnostics.Type_parameter_declaration_expected; - case ParsingContext.TypeArguments: return Diagnostics.Type_argument_expected; - case ParsingContext.TupleElementTypes: return Diagnostics.Type_expected; - case ParsingContext.HeritageClauses: return Diagnostics.Unexpected_token_expected; - case ParsingContext.ImportSpecifiers: return Diagnostics.Identifier_expected; + case ParsingContext.SourceElements: return Diagnostics.Declaration_or_statement_expected; + case ParsingContext.ModuleElements: return Diagnostics.Declaration_or_statement_expected; + case ParsingContext.BlockStatements: return Diagnostics.Statement_expected; + case ParsingContext.SwitchClauses: return Diagnostics.case_or_default_expected; + case ParsingContext.SwitchClauseStatements: return Diagnostics.Statement_expected; + case ParsingContext.TypeMembers: return Diagnostics.Property_or_signature_expected; + case ParsingContext.ClassMembers: return Diagnostics.Unexpected_token_A_constructor_method_accessor_or_property_was_expected; + case ParsingContext.EnumMembers: return Diagnostics.Enum_member_expected; + case ParsingContext.TypeReferences: return Diagnostics.Type_reference_expected; + case ParsingContext.VariableDeclarations: return Diagnostics.Variable_declaration_expected; + case ParsingContext.ObjectBindingElements: return Diagnostics.Property_destructuring_pattern_expected; + case ParsingContext.ArrayBindingElements: return Diagnostics.Array_element_destructuring_pattern_expected; + case ParsingContext.ArgumentExpressions: return Diagnostics.Argument_expression_expected; + case ParsingContext.ObjectLiteralMembers: return Diagnostics.Property_assignment_expected; + case ParsingContext.ArrayLiteralMembers: return Diagnostics.Expression_or_comma_expected; + case ParsingContext.Parameters: return Diagnostics.Parameter_declaration_expected; + case ParsingContext.TypeParameters: return Diagnostics.Type_parameter_declaration_expected; + case ParsingContext.TypeArguments: return Diagnostics.Type_argument_expected; + case ParsingContext.TupleElementTypes: return Diagnostics.Type_expected; + case ParsingContext.HeritageClauses: return Diagnostics.Unexpected_token_expected; + case ParsingContext.ImportOrExportSpecifiers: return Diagnostics.Identifier_expected; } }; @@ -1481,7 +1487,10 @@ module ts { // 'const' is only a modifier if followed by 'enum'. return nextToken() === SyntaxKind.EnumKeyword; } - + if (token === SyntaxKind.ExportKeyword) { + nextToken(); + return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.OpenBraceToken && canFollowModifier(); + } nextToken(); return canFollowModifier(); } @@ -1541,7 +1550,7 @@ module ts { return token === SyntaxKind.CommaToken || isStartOfType(); case ParsingContext.HeritageClauses: return isHeritageClause(); - case ParsingContext.ImportSpecifiers: + case ParsingContext.ImportOrExportSpecifiers: return isIdentifierOrKeyword(); } @@ -1579,7 +1588,7 @@ module ts { case ParsingContext.EnumMembers: case ParsingContext.ObjectLiteralMembers: case ParsingContext.ObjectBindingElements: - case ParsingContext.ImportSpecifiers: + case ParsingContext.ImportOrExportSpecifiers: return token === SyntaxKind.CloseBraceToken; case ParsingContext.SwitchClauseStatements: return token === SyntaxKind.CloseBraceToken || token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword; @@ -4671,7 +4680,7 @@ module ts { // parse namespace or named imports if (!importClause.name || parseOptional(SyntaxKind.CommaToken)) { - importClause.namedBindings = token === SyntaxKind.AsteriskToken ? parseNamespaceImport() : parseNamedImports(); + importClause.namedBindings = token === SyntaxKind.AsteriskToken ? parseNamespaceImport() : parseNamedImportsOrExports(SyntaxKind.NamedImports); } return finishNode(importClause); @@ -4715,8 +4724,8 @@ module ts { return finishNode(namespaceImport); } - function parseNamedImports(): NamedImports { - var namedImports = createNode(SyntaxKind.NamedImports); + function parseNamedImportsOrExports(kind: SyntaxKind): NamedImportsOrExports { + var node = createNode(kind); // NamedImports: // { } @@ -4726,12 +4735,22 @@ module ts { // ImportsList: // ImportSpecifier // ImportsList, ImportSpecifier - namedImports.elements = parseBracketedList(ParsingContext.ImportSpecifiers, parseImportSpecifier, SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken); - return finishNode(namedImports); + node.elements = parseBracketedList(ParsingContext.ImportOrExportSpecifiers, + kind === SyntaxKind.NamedImports ? parseImportSpecifier : parseExportSpecifier, + SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken); + return finishNode(node); } - function parseImportSpecifier(): ImportSpecifier { - var node = createNode(SyntaxKind.ImportSpecifier); + function parseExportSpecifier() { + return parseImportOrExportSpecifier(SyntaxKind.ExportSpecifier); + } + + function parseImportSpecifier() { + return parseImportOrExportSpecifier(SyntaxKind.ImportSpecifier); + } + + function parseImportOrExportSpecifier(kind: SyntaxKind): ImportOrExportSpecifier { + var node = createNode(kind); // ImportSpecifier: // ImportedBinding // IdentifierName as ImportedBinding @@ -4759,6 +4778,23 @@ module ts { return finishNode(node); } + function parseExportDeclaration(fullStart: number, modifiers: ModifiersArray): ExportDeclaration { + var node = createNode(SyntaxKind.ExportDeclaration, fullStart); + setModifiers(node, modifiers); + if (parseOptional(SyntaxKind.AsteriskToken)) { + parseExpected(SyntaxKind.FromKeyword); + node.moduleSpecifier = parseModuleSpecifier(); + } + else { + node.exportClause = parseNamedImportsOrExports(SyntaxKind.NamedExports); + if (parseOptional(SyntaxKind.FromKeyword)) { + node.moduleSpecifier = parseModuleSpecifier(); + } + } + parseSemicolon(); + return finishNode(node); + } + function parseExportAssignmentTail(fullStart: number, modifiers: ModifiersArray): ExportAssignment { var node = createNode(SyntaxKind.ExportAssignment, fullStart); setModifiers(node, modifiers); @@ -4795,7 +4831,7 @@ module ts { return lookAhead(nextTokenIsIdentifierOrKeywordOrStringLiteral); case SyntaxKind.ExportKeyword: // Check for export assignment or modifier on source element - return lookAhead(nextTokenIsEqualsTokenOrDeclarationStart); + return lookAhead(nextTokenCanFollowExportKeyword); case SyntaxKind.DeclareKeyword: case SyntaxKind.PublicKeyword: case SyntaxKind.PrivateKeyword: @@ -4826,9 +4862,10 @@ module ts { token === SyntaxKind.AsteriskToken || token === SyntaxKind.OpenBraceToken; } - function nextTokenIsEqualsTokenOrDeclarationStart() { + function nextTokenCanFollowExportKeyword() { nextToken(); - return token === SyntaxKind.EqualsToken || isDeclarationStart(); + return token === SyntaxKind.EqualsToken || token === SyntaxKind.AsteriskToken || + token === SyntaxKind.OpenBraceToken || isDeclarationStart(); } function nextTokenIsDeclarationStart() { @@ -4848,6 +4885,9 @@ module ts { if (parseOptional(SyntaxKind.EqualsToken)) { return parseExportAssignmentTail(fullStart, modifiers); } + if (token === SyntaxKind.AsteriskToken || token === SyntaxKind.OpenBraceToken) { + return parseExportDeclaration(fullStart, modifiers); + } } switch (token) { @@ -4952,8 +4992,9 @@ module ts { sourceFile.externalModuleIndicator = forEach(sourceFile.statements, node => node.flags & NodeFlags.Export || node.kind === SyntaxKind.ImportEqualsDeclaration && (node).moduleReference.kind === SyntaxKind.ExternalModuleReference - || node.kind === SyntaxKind.ExportAssignment || node.kind === SyntaxKind.ImportDeclaration + || node.kind === SyntaxKind.ExportAssignment + || node.kind === SyntaxKind.ExportDeclaration ? node : undefined); } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index ade6141952a..b74ef039fd6 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -351,8 +351,8 @@ module ts { function processImportedModules(file: SourceFile, basePath: string) { forEach(file.statements, node => { - if (node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) { - var moduleNameExpr = getImportedModuleName(node); + if (node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration || node.kind === SyntaxKind.ExportDeclaration) { + var moduleNameExpr = getExternalModuleName(node); if (moduleNameExpr && moduleNameExpr.kind === SyntaxKind.StringLiteral) { var moduleNameText = (moduleNameExpr).text; if (moduleNameText) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 83cd99392c0..53aa08dae77 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -231,12 +231,15 @@ module ts { ModuleDeclaration, ModuleBlock, ImportEqualsDeclaration, - ExportAssignment, ImportDeclaration, ImportClause, NamespaceImport, NamedImports, ImportSpecifier, + ExportAssignment, + ExportDeclaration, + NamedExports, + ExportSpecifier, // Module references ExternalModuleReference, @@ -898,15 +901,26 @@ module ts { name: Identifier; } - export interface NamedImports extends Node { - elements: NodeArray; + export interface ExportDeclaration extends Statement, ModuleElement { + exportClause?: NamedExports; + moduleSpecifier?: Expression; } - export interface ImportSpecifier extends Declaration { - propertyName?: Identifier; // Property name to be imported from module - name: Identifier; // element name to be imported in the scope + export interface NamedImportsOrExports extends Node { + elements: NodeArray; } + export type NamedImports = NamedImportsOrExports; + export type NamedExports = NamedImportsOrExports; + + export interface ImportOrExportSpecifier extends Declaration { + propertyName?: Identifier; // Name preceding "as" keyword (or undefined when "as" is absent) + name: Identifier; // Declared name + } + + export type ImportSpecifier = ImportOrExportSpecifier; + export type ExportSpecifier = ImportOrExportSpecifier; + export interface ExportAssignment extends Statement, ModuleElement { exportName: Identifier; } @@ -1163,7 +1177,7 @@ module ts { } export interface EmitResolver { - getGeneratedNameForNode(node: ModuleDeclaration | EnumDeclaration | ImportDeclaration): string; + getGeneratedNameForNode(node: ModuleDeclaration | EnumDeclaration | ImportDeclaration | ExportDeclaration): string; getExpressionNameSubstitution(node: Identifier): string; getExportAssignmentName(node: SourceFile): string; isReferencedImportDeclaration(node: Node): boolean; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 9283064d7db..7c6b4d0be96 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -603,7 +603,7 @@ module ts { return node.kind === SyntaxKind.ImportEqualsDeclaration && (node).moduleReference.kind !== SyntaxKind.ExternalModuleReference; } - export function getImportedModuleName(node: Node): Expression { + export function getExternalModuleName(node: Node): Expression { if (node.kind === SyntaxKind.ImportDeclaration) { return (node).moduleSpecifier; } @@ -613,6 +613,9 @@ module ts { return (reference).expression; } } + if (node.kind === SyntaxKind.ExportDeclaration) { + return (node).moduleSpecifier; + } } export function hasDotDotDotToken(node: Node) { @@ -695,6 +698,7 @@ module ts { case SyntaxKind.ImportClause: case SyntaxKind.ImportSpecifier: case SyntaxKind.NamespaceImport: + case SyntaxKind.ExportSpecifier: return true; } return false;