Skip to content

Commit 875d0c0

Browse files
committed
Emit unexported aliases that need to be emitted to .d.ts to make correct result
1 parent 0b227d5 commit 875d0c0

File tree

3 files changed

+101
-24
lines changed

3 files changed

+101
-24
lines changed

src/compiler/checker.ts

+31-5
Original file line numberDiff line numberDiff line change
@@ -726,21 +726,22 @@ module ts {
726726
}
727727

728728
function isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessiblityResult {
729+
var aliasesToMakeVisible: ImportDeclaration[];
729730
if (symbol && enclosingDeclaration && !(symbol.flags & SymbolFlags.TypeParameter)) {
730731
var initialSymbol = symbol;
731732
var meaningToLook = meaning;
732733
while (symbol) {
733734
// Symbol is accessible if it by itself is accessible
734735
var accessibleSymbol = getAccessibleSymbol(symbol, enclosingDeclaration, meaningToLook);
735736
if (accessibleSymbol) {
736-
if (forEach(accessibleSymbol.declarations, declaration => !isDeclarationVisible(declaration))) {
737+
if (forEach(accessibleSymbol.declarations, declaration => !getIsDeclarationVisible(declaration))) {
737738
return {
738739
accessibility: SymbolAccessibility.NotAccessible,
739740
errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning),
740-
errorModuleName: symbol !== initialSymbol ? symbolToString(symbol, enclosingDeclaration, SymbolFlags.Namespace) : undefined
741+
errorModuleName: symbol !== initialSymbol ? symbolToString(symbol, enclosingDeclaration, SymbolFlags.Namespace) : undefined,
741742
};
742743
}
743-
return { accessibility: SymbolAccessibility.Accessible };
744+
return { accessibility: SymbolAccessibility.Accessible, aliasesToMakeVisible: aliasesToMakeVisible };
744745
}
745746

746747
// TODO(shkamat): Handle static method of class
@@ -769,6 +770,32 @@ module ts {
769770
}
770771

771772
return { accessibility: SymbolAccessibility.Accessible };
773+
774+
function getIsDeclarationVisible(declaration: Declaration) {
775+
if (!isDeclarationVisible(declaration)) {
776+
// Mark the unexported alias as visible if its parent is visible
777+
// because these kind of aliases can be used to name types in declaration file
778+
if (declaration.kind === SyntaxKind.ImportDeclaration &&
779+
!(declaration.flags & NodeFlags.Export) &&
780+
isDeclarationVisible(declaration.parent)) {
781+
getNodeLinks(declaration).isVisible = true;
782+
if (aliasesToMakeVisible) {
783+
if (!contains(aliasesToMakeVisible, declaration)) {
784+
aliasesToMakeVisible.push(declaration);
785+
}
786+
}
787+
else {
788+
aliasesToMakeVisible = [declaration];
789+
}
790+
return true;
791+
}
792+
793+
// Declaration is not visible
794+
return false;
795+
}
796+
797+
return true;
798+
}
772799
}
773800

774801
// Enclosing declaration is optional when we dont want to get qualified name in the enclosing declaration scope
@@ -1091,7 +1118,6 @@ module ts {
10911118
case SyntaxKind.EnumDeclaration:
10921119
case SyntaxKind.ImportDeclaration:
10931120
if (!(node.flags & NodeFlags.Export)) {
1094-
// TODO(shkamat): non exported aliases can be visible if they are referenced else where for value/type/namespace
10951121
return isGlobalSourceFile(node.parent) || isUsedInExportAssignment(node);
10961122
}
10971123
// Exported members are visible if parent is visible
@@ -1125,7 +1151,7 @@ module ts {
11251151
if (node) {
11261152
var links = getNodeLinks(node);
11271153
if (links.isVisible === undefined) {
1128-
links.isVisible = determineIfDeclarationIsVisible();
1154+
links.isVisible = !!determineIfDeclarationIsVisible();
11291155
}
11301156
return links.isVisible;
11311157
}

src/compiler/emitter.ts

+69-19
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module ts {
99
getTextPos(): number;
1010
getLine(): number;
1111
getColumn(): number;
12+
getIndent(): number;
1213
}
1314

1415
var indentStrings: string[] = [];
@@ -141,6 +142,7 @@ module ts {
141142
writeLine: writeLine,
142143
increaseIndent: () => indent++,
143144
decreaseIndent: () => indent--,
145+
getIndent: () => indent,
144146
getTextPos: () => output.length,
145147
getLine: () => lineCount + 1,
146148
getColumn: () => lineStart ? indent * 4 + 1 : output.length - linePos + 1,
@@ -1867,6 +1869,13 @@ module ts {
18671869
var enclosingDeclaration: Node;
18681870
var reportedDeclarationError = false;
18691871

1872+
var aliasDeclarationEmitInfo: {
1873+
declaration: ImportDeclaration;
1874+
outputPos: number;
1875+
indent: number;
1876+
asynchronousOutput?: string; // If the output for alias was written asynchronously, the corresponding output
1877+
}[] = [];
1878+
18701879
var getSymbolVisibilityDiagnosticMessage: (symbolAccesibilityResult: SymbolAccessiblityResult) => {
18711880
errorNode: Node;
18721881
diagnosticMessage: DiagnosticMessage;
@@ -1875,9 +1884,25 @@ module ts {
18751884

18761885
function writeSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) {
18771886
var symbolAccesibilityResult = resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning);
1878-
// TODO(shkamat): Since we dont have error reporting for all the cases as yet we have this check on handler being present
1887+
// TODO(shkamat): Since we dont have error reporting for all the cases as yet we have this check on handler being present
18791888
if (!getSymbolVisibilityDiagnosticMessage || symbolAccesibilityResult.accessibility === SymbolAccessibility.Accessible) {
18801889
resolver.writeSymbol(symbol, enclosingDeclaration, meaning, writer);
1890+
1891+
// write the aliases
1892+
if (symbolAccesibilityResult && symbolAccesibilityResult.aliasesToMakeVisible) {
1893+
var oldWriter = writer;
1894+
forEach(symbolAccesibilityResult.aliasesToMakeVisible.sort(), aliasToWrite => {
1895+
var aliasEmitInfo = forEach(aliasDeclarationEmitInfo, declEmitInfo => declEmitInfo.declaration === aliasToWrite ? declEmitInfo : undefined);
1896+
writer = createTextWriter(writeSymbol);
1897+
for (var declarationIndent = aliasEmitInfo.indent; declarationIndent; declarationIndent--) {
1898+
writer.increaseIndent();
1899+
}
1900+
1901+
writeImportDeclaration(aliasToWrite);
1902+
aliasEmitInfo.asynchronousOutput = writer.getText();
1903+
});
1904+
writer = oldWriter;
1905+
}
18811906
}
18821907
else {
18831908
// Report error
@@ -1951,24 +1976,37 @@ module ts {
19511976
}
19521977

19531978
function emitImportDeclaration(node: ImportDeclaration) {
1954-
if (resolver.isDeclarationVisible(node)) {
1955-
if (node.flags & NodeFlags.Export) {
1956-
write("export ");
1957-
}
1958-
write("import ");
1959-
emitSourceTextOfNode(node.name);
1960-
write(" = ");
1961-
if (node.entityName) {
1962-
emitSourceTextOfNode(node.entityName);
1963-
write(";");
1964-
}
1965-
else {
1966-
write("require(");
1967-
emitSourceTextOfNode(node.externalModuleName);
1968-
write(");");
1969-
}
1970-
writeLine();
1979+
var nodeEmitInfo = {
1980+
declaration: node,
1981+
outputPos: writer.getTextPos(),
1982+
indent: writer.getIndent(),
1983+
hasWritten: resolver.isDeclarationVisible(node)
1984+
};
1985+
aliasDeclarationEmitInfo.push(nodeEmitInfo);
1986+
if (nodeEmitInfo.hasWritten) {
1987+
writeImportDeclaration(node);
1988+
}
1989+
}
1990+
1991+
function writeImportDeclaration(node: ImportDeclaration) {
1992+
// note usage of writer. methods instead of aliases created, just to make sure we are using
1993+
// correct writer especially to handle asynchronous alias writing
1994+
if (node.flags & NodeFlags.Export) {
1995+
writer.write("export ");
1996+
}
1997+
writer.write("import ");
1998+
writer.write(getSourceTextOfLocalNode(node.name));
1999+
writer.write(" = ");
2000+
if (node.entityName) {
2001+
writer.write(getSourceTextOfLocalNode(node.entityName));
2002+
writer.write(";");
2003+
}
2004+
else {
2005+
writer.write("require(");
2006+
writer.write(getSourceTextOfLocalNode(node.externalModuleName));
2007+
writer.write(");");
19712008
}
2009+
writer.writeLine();
19722010
}
19732011

19742012
function emitModuleDeclaration(node: ModuleDeclaration) {
@@ -2484,7 +2522,19 @@ module ts {
24842522
// TODO(shkamat): Should we not write any declaration file if any of them can produce error,
24852523
// or should we just not write this file like we are doing now
24862524
if (!reportedDeclarationError) {
2487-
writeFile(getModuleNameFromFilename(jsFilePath) + ".d.ts", referencePathsOutput + writer.getText());
2525+
var declarationOutput = referencePathsOutput;
2526+
var synchronousDeclarationOutput = writer.getText();
2527+
// apply additions
2528+
var appliedSyncOutputPos = 0;
2529+
forEach(aliasDeclarationEmitInfo, aliasEmitInfo => {
2530+
if (aliasEmitInfo.asynchronousOutput) {
2531+
declarationOutput += synchronousDeclarationOutput.substring(appliedSyncOutputPos, aliasEmitInfo.outputPos);
2532+
declarationOutput += aliasEmitInfo.asynchronousOutput;
2533+
appliedSyncOutputPos = aliasEmitInfo.outputPos;
2534+
}
2535+
});
2536+
declarationOutput += synchronousDeclarationOutput.substring(appliedSyncOutputPos);
2537+
writeFile(getModuleNameFromFilename(jsFilePath) + ".d.ts", declarationOutput);
24882538
}
24892539
}
24902540

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,7 @@ module ts {
638638
accessibility: SymbolAccessibility;
639639
errorSymbolName?: string // Optional symbol name that results in error
640640
errorModuleName?: string // If the symbol is not visibile from module, module's name
641+
aliasesToMakeVisible?: ImportDeclaration[]; // aliases that need to have this symbol visible
641642
}
642643

643644
export interface EmitResolver {

0 commit comments

Comments
 (0)