@@ -1103,39 +1103,81 @@ namespace ts {
11031103 return links.resolvedExports || (links.resolvedExports = getExportsForModule(moduleSymbol));
11041104 }
11051105
1106- function extendExportSymbols(target: SymbolTable, source: SymbolTable) {
1106+ interface ExportCollisionTracker {
1107+ specifierText: string;
1108+ exportsWithDuplicate: ExportDeclaration[];
1109+ }
1110+
1111+ /**
1112+ * Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument
1113+ * Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables
1114+ */
1115+ function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: Map<ExportCollisionTracker>, exportNode?: ExportDeclaration) {
11071116 for (const id in source) {
11081117 if (id !== "default" && !hasProperty(target, id)) {
11091118 target[id] = source[id];
1119+ if (lookupTable && exportNode) {
1120+ lookupTable[id] = {
1121+ specifierText: getTextOfNode(exportNode.moduleSpecifier)
1122+ } as ExportCollisionTracker;
1123+ }
1124+ }
1125+ else if (lookupTable && exportNode && id !== "default" && hasProperty(target, id) && resolveSymbol(target[id]) !== resolveSymbol(source[id])) {
1126+ if (!lookupTable[id].exportsWithDuplicate) {
1127+ lookupTable[id].exportsWithDuplicate = [exportNode];
1128+ }
1129+ else {
1130+ lookupTable[id].exportsWithDuplicate.push(exportNode);
1131+ }
11101132 }
11111133 }
11121134 }
11131135
11141136 function getExportsForModule(moduleSymbol: Symbol): SymbolTable {
1115- let result: SymbolTable;
11161137 const visitedSymbols: Symbol[] = [];
1117- visit(moduleSymbol);
1118- return result || moduleSymbol.exports;
1138+ return visit(moduleSymbol) || moduleSymbol.exports;
11191139
11201140 // The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example,
11211141 // module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error.
1122- function visit(symbol: Symbol) {
1123- if (symbol && symbol.flags & SymbolFlags.HasExports && !contains(visitedSymbols, symbol)) {
1124- visitedSymbols.push(symbol);
1125- if (symbol !== moduleSymbol) {
1126- if (!result) {
1127- result = cloneSymbolTable(moduleSymbol.exports);
1142+ function visit(symbol: Symbol): SymbolTable {
1143+ if (!(symbol && symbol.flags & SymbolFlags.HasExports && !contains(visitedSymbols, symbol))) {
1144+ return;
1145+ }
1146+ visitedSymbols.push(symbol);
1147+ const symbols = cloneSymbolTable(symbol.exports);
1148+ // All export * declarations are collected in an __export symbol by the binder
1149+ const exportStars = symbol.exports["__export"];
1150+ if (exportStars) {
1151+ const nestedSymbols: SymbolTable = {};
1152+ const lookupTable: Map<ExportCollisionTracker> = {};
1153+ for (const node of exportStars.declarations) {
1154+ const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier);
1155+ const exportedSymbols = visit(resolvedModule);
1156+ extendExportSymbols(
1157+ nestedSymbols,
1158+ exportedSymbols,
1159+ lookupTable,
1160+ node as ExportDeclaration
1161+ );
1162+ }
1163+ for (const id in lookupTable) {
1164+ const { exportsWithDuplicate } = lookupTable[id];
1165+ // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself
1166+ if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || hasProperty(symbols, id)) {
1167+ continue;
11281168 }
1129- extendExportSymbols(result, symbol.exports);
1130- }
1131- // All export * declarations are collected in an __export symbol by the binder
1132- const exportStars = symbol.exports["__export"];
1133- if (exportStars) {
1134- for (const node of exportStars.declarations) {
1135- visit(resolveExternalModuleName(node, (<ExportDeclaration>node).moduleSpecifier ));
1169+ for (const node of exportsWithDuplicate) {
1170+ diagnostics.add(createDiagnosticForNode(
1171+ node,
1172+ Diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity,
1173+ lookupTable[id].specifierText,
1174+ id
1175+ ));
11361176 }
11371177 }
1178+ extendExportSymbols(symbols, nestedSymbols);
11381179 }
1180+ return symbols;
11391181 }
11401182 }
11411183
@@ -14132,8 +14174,29 @@ namespace ts {
1413214174 const declaration = getDeclarationOfAliasSymbol(exportEqualsSymbol) || exportEqualsSymbol.valueDeclaration;
1413314175 error(declaration, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements);
1413414176 }
14177+ // Checks for export * conflicts
14178+ const exports = getExportsOfModule(moduleSymbol);
14179+ for (const id in exports) {
14180+ if (id === "__export") {
14181+ continue;
14182+ }
14183+ const { declarations, flags } = exports[id];
14184+ // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. (TS Exceptions: namespaces, function overloads, enums, and interfaces)
14185+ if (!(flags & (SymbolFlags.Namespace | SymbolFlags.Interface | SymbolFlags.Enum)) && declarations.length > 1) {
14186+ const exportedDeclarations: Declaration[] = filter(declarations, isNotOverload);
14187+ if (exportedDeclarations.length > 1) {
14188+ for (const declaration of exportedDeclarations) {
14189+ diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Cannot_redeclare_exported_variable_0, id));
14190+ }
14191+ }
14192+ }
14193+ }
1413514194 links.exportsChecked = true;
1413614195 }
14196+
14197+ function isNotOverload(declaration: Declaration): boolean {
14198+ return declaration.kind !== SyntaxKind.FunctionDeclaration || !!(declaration as FunctionDeclaration).body;
14199+ }
1413714200 }
1413814201
1413914202 function checkTypePredicate(node: TypePredicateNode) {
0 commit comments