diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2211436c847..b01ba9dc46f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -726,6 +726,7 @@ module ts { } function isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessiblityResult { + var aliasesToMakeVisible: ImportDeclaration[]; if (symbol && enclosingDeclaration && !(symbol.flags & SymbolFlags.TypeParameter)) { var initialSymbol = symbol; var meaningToLook = meaning; @@ -733,14 +734,14 @@ module ts { // Symbol is accessible if it by itself is accessible var accessibleSymbol = getAccessibleSymbol(symbol, enclosingDeclaration, meaningToLook); if (accessibleSymbol) { - if (forEach(accessibleSymbol.declarations, declaration => !isDeclarationVisible(declaration))) { + if (forEach(accessibleSymbol.declarations, declaration => !getIsDeclarationVisible(declaration))) { return { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning), - errorModuleName: symbol !== initialSymbol ? symbolToString(symbol, enclosingDeclaration, SymbolFlags.Namespace) : undefined + errorModuleName: symbol !== initialSymbol ? symbolToString(symbol, enclosingDeclaration, SymbolFlags.Namespace) : undefined, }; } - return { accessibility: SymbolAccessibility.Accessible }; + return { accessibility: SymbolAccessibility.Accessible, aliasesToMakeVisible: aliasesToMakeVisible }; } // TODO(shkamat): Handle static method of class @@ -769,6 +770,32 @@ module ts { } return { accessibility: SymbolAccessibility.Accessible }; + + function getIsDeclarationVisible(declaration: Declaration) { + if (!isDeclarationVisible(declaration)) { + // Mark the unexported alias as visible if its parent is visible + // because these kind of aliases can be used to name types in declaration file + if (declaration.kind === SyntaxKind.ImportDeclaration && + !(declaration.flags & NodeFlags.Export) && + isDeclarationVisible(declaration.parent)) { + getNodeLinks(declaration).isVisible = true; + if (aliasesToMakeVisible) { + if (!contains(aliasesToMakeVisible, declaration)) { + aliasesToMakeVisible.push(declaration); + } + } + else { + aliasesToMakeVisible = [declaration]; + } + return true; + } + + // Declaration is not visible + return false; + } + + return true; + } } // Enclosing declaration is optional when we dont want to get qualified name in the enclosing declaration scope @@ -1091,7 +1118,6 @@ module ts { case SyntaxKind.EnumDeclaration: case SyntaxKind.ImportDeclaration: if (!(node.flags & NodeFlags.Export)) { - // TODO(shkamat): non exported aliases can be visible if they are referenced else where for value/type/namespace return isGlobalSourceFile(node.parent) || isUsedInExportAssignment(node); } // Exported members are visible if parent is visible @@ -1125,7 +1151,7 @@ module ts { if (node) { var links = getNodeLinks(node); if (links.isVisible === undefined) { - links.isVisible = determineIfDeclarationIsVisible(); + links.isVisible = !!determineIfDeclarationIsVisible(); } return links.isVisible; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 66dc98c5757..60d7c18d546 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -9,6 +9,7 @@ module ts { getTextPos(): number; getLine(): number; getColumn(): number; + getIndent(): number; } var indentStrings: string[] = []; @@ -141,6 +142,7 @@ module ts { writeLine: writeLine, increaseIndent: () => indent++, decreaseIndent: () => indent--, + getIndent: () => indent, getTextPos: () => output.length, getLine: () => lineCount + 1, getColumn: () => lineStart ? indent * 4 + 1 : output.length - linePos + 1, @@ -1867,6 +1869,13 @@ module ts { var enclosingDeclaration: Node; var reportedDeclarationError = false; + var aliasDeclarationEmitInfo: { + declaration: ImportDeclaration; + outputPos: number; + indent: number; + asynchronousOutput?: string; // If the output for alias was written asynchronously, the corresponding output + }[] = []; + var getSymbolVisibilityDiagnosticMessage: (symbolAccesibilityResult: SymbolAccessiblityResult) => { errorNode: Node; diagnosticMessage: DiagnosticMessage; @@ -1875,9 +1884,25 @@ module ts { function writeSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) { var symbolAccesibilityResult = resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning); - // TODO(shkamat): Since we dont have error reporting for all the cases as yet we have this check on handler being present + // TODO(shkamat): Since we dont have error reporting for all the cases as yet we have this check on handler being present if (!getSymbolVisibilityDiagnosticMessage || symbolAccesibilityResult.accessibility === SymbolAccessibility.Accessible) { resolver.writeSymbol(symbol, enclosingDeclaration, meaning, writer); + + // write the aliases + if (symbolAccesibilityResult && symbolAccesibilityResult.aliasesToMakeVisible) { + var oldWriter = writer; + forEach(symbolAccesibilityResult.aliasesToMakeVisible.sort(), aliasToWrite => { + var aliasEmitInfo = forEach(aliasDeclarationEmitInfo, declEmitInfo => declEmitInfo.declaration === aliasToWrite ? declEmitInfo : undefined); + writer = createTextWriter(writeSymbol); + for (var declarationIndent = aliasEmitInfo.indent; declarationIndent; declarationIndent--) { + writer.increaseIndent(); + } + + writeImportDeclaration(aliasToWrite); + aliasEmitInfo.asynchronousOutput = writer.getText(); + }); + writer = oldWriter; + } } else { // Report error @@ -1951,26 +1976,39 @@ module ts { } function emitImportDeclaration(node: ImportDeclaration) { - if (resolver.isDeclarationVisible(node)) { - if (node.flags & NodeFlags.Export) { - write("export "); - } - write("import "); - emitSourceTextOfNode(node.name); - write(" = "); - if (node.entityName) { - emitSourceTextOfNode(node.entityName); - write(";"); - } - else { - write("require("); - emitSourceTextOfNode(node.externalModuleName); - write(");"); - } - writeLine(); + var nodeEmitInfo = { + declaration: node, + outputPos: writer.getTextPos(), + indent: writer.getIndent(), + hasWritten: resolver.isDeclarationVisible(node) + }; + aliasDeclarationEmitInfo.push(nodeEmitInfo); + if (nodeEmitInfo.hasWritten) { + writeImportDeclaration(node); } } + function writeImportDeclaration(node: ImportDeclaration) { + // note usage of writer. methods instead of aliases created, just to make sure we are using + // correct writer especially to handle asynchronous alias writing + if (node.flags & NodeFlags.Export) { + writer.write("export "); + } + writer.write("import "); + writer.write(getSourceTextOfLocalNode(node.name)); + writer.write(" = "); + if (node.entityName) { + writer.write(getSourceTextOfLocalNode(node.entityName)); + writer.write(";"); + } + else { + writer.write("require("); + writer.write(getSourceTextOfLocalNode(node.externalModuleName)); + writer.write(");"); + } + writer.writeLine(); + } + function emitModuleDeclaration(node: ModuleDeclaration) { if (resolver.isDeclarationVisible(node)) { emitDeclarationFlags(node); @@ -2484,7 +2522,19 @@ module ts { // TODO(shkamat): Should we not write any declaration file if any of them can produce error, // or should we just not write this file like we are doing now if (!reportedDeclarationError) { - writeFile(getModuleNameFromFilename(jsFilePath) + ".d.ts", referencePathsOutput + writer.getText()); + var declarationOutput = referencePathsOutput; + var synchronousDeclarationOutput = writer.getText(); + // apply additions + var appliedSyncOutputPos = 0; + forEach(aliasDeclarationEmitInfo, aliasEmitInfo => { + if (aliasEmitInfo.asynchronousOutput) { + declarationOutput += synchronousDeclarationOutput.substring(appliedSyncOutputPos, aliasEmitInfo.outputPos); + declarationOutput += aliasEmitInfo.asynchronousOutput; + appliedSyncOutputPos = aliasEmitInfo.outputPos; + } + }); + declarationOutput += synchronousDeclarationOutput.substring(appliedSyncOutputPos); + writeFile(getModuleNameFromFilename(jsFilePath) + ".d.ts", declarationOutput); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6f85619428a..911dc20b765 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -638,6 +638,7 @@ module ts { accessibility: SymbolAccessibility; errorSymbolName?: string // Optional symbol name that results in error errorModuleName?: string // If the symbol is not visibile from module, module's name + aliasesToMakeVisible?: ImportDeclaration[]; // aliases that need to have this symbol visible } export interface EmitResolver {