TypeScript/src/compiler/declarationEmitter.ts
2016-10-27 13:14:56 -07:00

1786 lines
93 KiB
TypeScript

/// <reference path="checker.ts"/>
/* @internal */
namespace ts {
interface ModuleElementDeclarationEmitInfo {
node: Node;
outputPos: number;
indent: number;
asynchronousOutput?: string; // If the output for alias was written asynchronously, the corresponding output
subModuleElementDeclarationEmitInfo?: ModuleElementDeclarationEmitInfo[];
isVisible?: boolean;
}
interface DeclarationEmit {
reportedDeclarationError: boolean;
moduleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[];
synchronousDeclarationOutput: string;
referencesOutput: string;
}
type GetSymbolAccessibilityDiagnostic = (symbolAccessibilityResult: SymbolAccessibilityResult) => SymbolAccessibilityDiagnostic;
interface EmitTextWriterWithSymbolWriter extends EmitTextWriter, SymbolWriter {
getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic;
}
interface SymbolAccessibilityDiagnostic {
errorNode: Node;
diagnosticMessage: DiagnosticMessage;
typeName?: DeclarationName;
}
export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver, targetSourceFile: SourceFile): Diagnostic[] {
const declarationDiagnostics = createDiagnosticCollection();
forEachExpectedEmitFile(host, getDeclarationDiagnosticsFromFile, targetSourceFile);
return declarationDiagnostics.getDiagnostics(targetSourceFile ? targetSourceFile.fileName : undefined);
function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sources: SourceFile[], isBundledEmit: boolean) {
emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sources, isBundledEmit, /*emitOnlyDtsFiles*/ false);
}
}
function emitDeclarations(host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, declarationFilePath: string,
sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean): DeclarationEmit {
const newLine = host.getNewLine();
const compilerOptions = host.getCompilerOptions();
let write: (s: string) => void;
let writeLine: () => void;
let increaseIndent: () => void;
let decreaseIndent: () => void;
let writeTextOfNode: (text: string, node: Node) => void;
let writer: EmitTextWriterWithSymbolWriter;
createAndSetNewTextWriterWithSymbolWriter();
let enclosingDeclaration: Node;
let resultHasExternalModuleIndicator: boolean;
let currentText: string;
let currentLineMap: number[];
let currentIdentifiers: Map<string, string>;
let isCurrentFileExternalModule: boolean;
let reportedDeclarationError = false;
let errorNameNode: DeclarationName;
const emitJsDocComments = compilerOptions.removeComments ? noop : writeJsDocComments;
const emit = compilerOptions.stripInternal ? stripInternal : emitNode;
let noDeclare: boolean;
let moduleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[] = [];
let asynchronousSubModuleDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[];
// Contains the reference paths that needs to go in the declaration file.
// Collecting this separately because reference paths need to be first thing in the declaration file
// and we could be collecting these paths from multiple files into single one with --out option
let referencesOutput = "";
let usedTypeDirectiveReferences: Set<string>;
// Emit references corresponding to each file
const emittedReferencedFiles: SourceFile[] = [];
let addedGlobalFileReference = false;
let allSourcesModuleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[] = [];
forEach(sourceFiles, sourceFile => {
// Dont emit for javascript file
if (isSourceFileJavaScript(sourceFile)) {
return;
}
// Check what references need to be added
if (!compilerOptions.noResolve) {
forEach(sourceFile.referencedFiles, fileReference => {
const referencedFile = tryResolveScriptReference(host, sourceFile, fileReference);
// Emit reference in dts, if the file reference was not already emitted
if (referencedFile && !contains(emittedReferencedFiles, referencedFile)) {
// Add a reference to generated dts file,
// global file reference is added only
// - if it is not bundled emit (because otherwise it would be self reference)
// - and it is not already added
if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference, emitOnlyDtsFiles)) {
addedGlobalFileReference = true;
}
emittedReferencedFiles.push(referencedFile);
}
});
}
resultHasExternalModuleIndicator = false;
if (!isBundledEmit || !isExternalModule(sourceFile)) {
noDeclare = false;
emitSourceFile(sourceFile);
}
else if (isExternalModule(sourceFile)) {
noDeclare = true;
write(`declare module "${getResolvedExternalModuleName(host, sourceFile)}" {`);
writeLine();
increaseIndent();
emitSourceFile(sourceFile);
decreaseIndent();
write("}");
writeLine();
}
// create asynchronous output for the importDeclarations
if (moduleElementDeclarationEmitInfo.length) {
const oldWriter = writer;
forEach(moduleElementDeclarationEmitInfo, aliasEmitInfo => {
if (aliasEmitInfo.isVisible && !aliasEmitInfo.asynchronousOutput) {
Debug.assert(aliasEmitInfo.node.kind === SyntaxKind.ImportDeclaration);
createAndSetNewTextWriterWithSymbolWriter();
Debug.assert(aliasEmitInfo.indent === 0 || (aliasEmitInfo.indent === 1 && isBundledEmit));
for (let i = 0; i < aliasEmitInfo.indent; i++) {
increaseIndent();
}
writeImportDeclaration(<ImportDeclaration>aliasEmitInfo.node);
aliasEmitInfo.asynchronousOutput = writer.getText();
for (let i = 0; i < aliasEmitInfo.indent; i++) {
decreaseIndent();
}
}
});
setWriter(oldWriter);
allSourcesModuleElementDeclarationEmitInfo = allSourcesModuleElementDeclarationEmitInfo.concat(moduleElementDeclarationEmitInfo);
moduleElementDeclarationEmitInfo = [];
}
if (!isBundledEmit && isExternalModule(sourceFile) && sourceFile.moduleAugmentations.length && !resultHasExternalModuleIndicator) {
// if file was external module with augmentations - this fact should be preserved in .d.ts as well.
// in case if we didn't write any external module specifiers in .d.ts we need to emit something
// that will force compiler to think that this file is an external module - 'export {}' is a reasonable choice here.
write("export {};");
writeLine();
}
});
if (usedTypeDirectiveReferences) {
usedTypeDirectiveReferences.forEach(directive => {
referencesOutput += `/// <reference types="${directive}" />${newLine}`;
});
}
return {
reportedDeclarationError,
moduleElementDeclarationEmitInfo: allSourcesModuleElementDeclarationEmitInfo,
synchronousDeclarationOutput: writer.getText(),
referencesOutput,
};
function hasInternalAnnotation(range: CommentRange) {
const comment = currentText.substring(range.pos, range.end);
return comment.indexOf("@internal") >= 0;
}
function stripInternal(node: Node) {
if (node) {
const leadingCommentRanges = getLeadingCommentRanges(currentText, node.pos);
if (forEach(leadingCommentRanges, hasInternalAnnotation)) {
return;
}
emitNode(node);
}
}
function createAndSetNewTextWriterWithSymbolWriter(): void {
const writer = <EmitTextWriterWithSymbolWriter>createTextWriter(newLine);
writer.trackSymbol = trackSymbol;
writer.reportInaccessibleThisError = reportInaccessibleThisError;
writer.writeKeyword = writer.write;
writer.writeOperator = writer.write;
writer.writePunctuation = writer.write;
writer.writeSpace = writer.write;
writer.writeStringLiteral = writer.writeLiteral;
writer.writeParameter = writer.write;
writer.writeSymbol = writer.write;
setWriter(writer);
}
function setWriter(newWriter: EmitTextWriterWithSymbolWriter) {
writer = newWriter;
write = newWriter.write;
writeTextOfNode = newWriter.writeTextOfNode;
writeLine = newWriter.writeLine;
increaseIndent = newWriter.increaseIndent;
decreaseIndent = newWriter.decreaseIndent;
}
function writeAsynchronousModuleElements(nodes: Node[]) {
const oldWriter = writer;
forEach(nodes, declaration => {
let nodeToCheck: Node;
if (declaration.kind === SyntaxKind.VariableDeclaration) {
nodeToCheck = declaration.parent.parent;
}
else if (declaration.kind === SyntaxKind.NamedImports || declaration.kind === SyntaxKind.ImportSpecifier || declaration.kind === SyntaxKind.ImportClause) {
Debug.fail("We should be getting ImportDeclaration instead to write");
}
else {
nodeToCheck = declaration;
}
let moduleElementEmitInfo = forEach(moduleElementDeclarationEmitInfo, declEmitInfo => declEmitInfo.node === nodeToCheck ? declEmitInfo : undefined);
if (!moduleElementEmitInfo && asynchronousSubModuleDeclarationEmitInfo) {
moduleElementEmitInfo = forEach(asynchronousSubModuleDeclarationEmitInfo, declEmitInfo => declEmitInfo.node === nodeToCheck ? declEmitInfo : undefined);
}
// If the alias was marked as not visible when we saw its declaration, we would have saved the aliasEmitInfo, but if we haven't yet visited the alias declaration
// then we don't need to write it at this point. We will write it when we actually see its declaration
// Eg.
// export function bar(a: foo.Foo) { }
// import foo = require("foo");
// Writing of function bar would mark alias declaration foo as visible but we haven't yet visited that declaration so do nothing,
// we would write alias foo declaration when we visit it since it would now be marked as visible
if (moduleElementEmitInfo) {
if (moduleElementEmitInfo.node.kind === SyntaxKind.ImportDeclaration) {
// we have to create asynchronous output only after we have collected complete information
// because it is possible to enable multiple bindings as asynchronously visible
moduleElementEmitInfo.isVisible = true;
}
else {
createAndSetNewTextWriterWithSymbolWriter();
for (let declarationIndent = moduleElementEmitInfo.indent; declarationIndent; declarationIndent--) {
increaseIndent();
}
if (nodeToCheck.kind === SyntaxKind.ModuleDeclaration) {
Debug.assert(asynchronousSubModuleDeclarationEmitInfo === undefined);
asynchronousSubModuleDeclarationEmitInfo = [];
}
writeModuleElement(nodeToCheck);
if (nodeToCheck.kind === SyntaxKind.ModuleDeclaration) {
moduleElementEmitInfo.subModuleElementDeclarationEmitInfo = asynchronousSubModuleDeclarationEmitInfo;
asynchronousSubModuleDeclarationEmitInfo = undefined;
}
moduleElementEmitInfo.asynchronousOutput = writer.getText();
}
}
});
setWriter(oldWriter);
}
function recordTypeReferenceDirectivesIfNecessary(typeReferenceDirectives: string[]): void {
if (!typeReferenceDirectives) {
return;
}
if (!usedTypeDirectiveReferences) {
usedTypeDirectiveReferences = createSet();
}
for (const directive of typeReferenceDirectives) {
if (!usedTypeDirectiveReferences.has(directive)) {
usedTypeDirectiveReferences.add(directive);
}
}
}
function handleSymbolAccessibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) {
if (symbolAccessibilityResult.accessibility === SymbolAccessibility.Accessible) {
// write the aliases
if (symbolAccessibilityResult && symbolAccessibilityResult.aliasesToMakeVisible) {
writeAsynchronousModuleElements(symbolAccessibilityResult.aliasesToMakeVisible);
}
}
else {
// Report error
reportedDeclarationError = true;
const errorInfo = writer.getSymbolAccessibilityDiagnostic(symbolAccessibilityResult);
if (errorInfo) {
if (errorInfo.typeName) {
emitterDiagnostics.add(createDiagnosticForNode(symbolAccessibilityResult.errorNode || errorInfo.errorNode,
errorInfo.diagnosticMessage,
getTextOfNodeFromSourceText(currentText, errorInfo.typeName),
symbolAccessibilityResult.errorSymbolName,
symbolAccessibilityResult.errorModuleName));
}
else {
emitterDiagnostics.add(createDiagnosticForNode(symbolAccessibilityResult.errorNode || errorInfo.errorNode,
errorInfo.diagnosticMessage,
symbolAccessibilityResult.errorSymbolName,
symbolAccessibilityResult.errorModuleName));
}
}
}
}
function trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) {
handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ true));
recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForSymbol(symbol, meaning));
}
function reportInaccessibleThisError() {
if (errorNameNode) {
reportedDeclarationError = true;
emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_this_type_A_type_annotation_is_necessary,
declarationNameToString(errorNameNode)));
}
}
function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, type: TypeNode, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic;
write(": ");
if (type) {
// Write the type
emitType(type);
}
else {
errorNameNode = declaration.name;
resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
errorNameNode = undefined;
}
}
function writeReturnTypeAtSignature(signature: SignatureDeclaration, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic;
write(": ");
if (signature.type) {
// Write the type
emitType(signature.type);
}
else {
errorNameNode = signature.name;
resolver.writeReturnTypeOfSignatureDeclaration(signature, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
errorNameNode = undefined;
}
}
function emitLines(nodes: Node[]) {
for (const node of nodes) {
emit(node);
}
}
function emitSeparatedList(nodes: Node[], separator: string, eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) {
let currentWriterPos = writer.getTextPos();
for (const node of nodes) {
if (!canEmitFn || canEmitFn(node)) {
if (currentWriterPos !== writer.getTextPos()) {
write(separator);
}
currentWriterPos = writer.getTextPos();
eachNodeEmitFn(node);
}
}
}
function emitCommaList(nodes: Node[], eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) {
emitSeparatedList(nodes, ", ", eachNodeEmitFn, canEmitFn);
}
function writeJsDocComments(declaration: Node) {
if (declaration) {
const jsDocComments = getJsDocCommentsFromText(declaration, currentText);
emitNewLineBeforeLeadingComments(currentLineMap, writer, declaration, jsDocComments);
// jsDoc comments are emitted at /*leading comment1 */space/*leading comment*/space
emitComments(currentText, currentLineMap, writer, jsDocComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeCommentRange);
}
}
function emitTypeWithNewGetSymbolAccessibilityDiagnostic(type: TypeNode | EntityName, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic;
emitType(type);
}
function emitType(type: TypeNode | Identifier | QualifiedName) {
switch (type.kind) {
case SyntaxKind.AnyKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.UndefinedKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.NeverKeyword:
case SyntaxKind.ThisType:
case SyntaxKind.LiteralType:
return writeTextOfNode(currentText, type);
case SyntaxKind.ExpressionWithTypeArguments:
return emitExpressionWithTypeArguments(<ExpressionWithTypeArguments>type);
case SyntaxKind.TypeReference:
return emitTypeReference(<TypeReferenceNode>type);
case SyntaxKind.TypeQuery:
return emitTypeQuery(<TypeQueryNode>type);
case SyntaxKind.ArrayType:
return emitArrayType(<ArrayTypeNode>type);
case SyntaxKind.TupleType:
return emitTupleType(<TupleTypeNode>type);
case SyntaxKind.UnionType:
return emitUnionType(<UnionTypeNode>type);
case SyntaxKind.IntersectionType:
return emitIntersectionType(<IntersectionTypeNode>type);
case SyntaxKind.ParenthesizedType:
return emitParenType(<ParenthesizedTypeNode>type);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
return emitSignatureDeclarationWithJsDocComments(<FunctionOrConstructorTypeNode>type);
case SyntaxKind.TypeLiteral:
return emitTypeLiteral(<TypeLiteralNode>type);
case SyntaxKind.Identifier:
return emitEntityName(<Identifier>type);
case SyntaxKind.QualifiedName:
return emitEntityName(<QualifiedName>type);
case SyntaxKind.TypePredicate:
return emitTypePredicate(<TypePredicateNode>type);
}
function writeEntityName(entityName: EntityName | Expression) {
if (entityName.kind === SyntaxKind.Identifier) {
writeTextOfNode(currentText, entityName);
}
else {
const left = entityName.kind === SyntaxKind.QualifiedName ? (<QualifiedName>entityName).left : (<PropertyAccessExpression>entityName).expression;
const right = entityName.kind === SyntaxKind.QualifiedName ? (<QualifiedName>entityName).right : (<PropertyAccessExpression>entityName).name;
writeEntityName(left);
write(".");
writeTextOfNode(currentText, right);
}
}
function emitEntityName(entityName: EntityNameOrEntityNameExpression) {
const visibilityResult = resolver.isEntityNameVisible(entityName,
// Aliases can be written asynchronously so use correct enclosing declaration
entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration ? entityName.parent : enclosingDeclaration);
handleSymbolAccessibilityError(visibilityResult);
recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForEntityName(entityName));
writeEntityName(entityName);
}
function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) {
if (isEntityNameExpression(node.expression)) {
Debug.assert(node.expression.kind === SyntaxKind.Identifier || node.expression.kind === SyntaxKind.PropertyAccessExpression);
emitEntityName(node.expression);
if (node.typeArguments) {
write("<");
emitCommaList(node.typeArguments, emitType);
write(">");
}
}
}
function emitTypeReference(type: TypeReferenceNode) {
emitEntityName(type.typeName);
if (type.typeArguments) {
write("<");
emitCommaList(type.typeArguments, emitType);
write(">");
}
}
function emitTypePredicate(type: TypePredicateNode) {
writeTextOfNode(currentText, type.parameterName);
write(" is ");
emitType(type.type);
}
function emitTypeQuery(type: TypeQueryNode) {
write("typeof ");
emitEntityName(type.exprName);
}
function emitArrayType(type: ArrayTypeNode) {
emitType(type.elementType);
write("[]");
}
function emitTupleType(type: TupleTypeNode) {
write("[");
emitCommaList(type.elementTypes, emitType);
write("]");
}
function emitUnionType(type: UnionTypeNode) {
emitSeparatedList(type.types, " | ", emitType);
}
function emitIntersectionType(type: IntersectionTypeNode) {
emitSeparatedList(type.types, " & ", emitType);
}
function emitParenType(type: ParenthesizedTypeNode) {
write("(");
emitType(type.type);
write(")");
}
function emitTypeLiteral(type: TypeLiteralNode) {
write("{");
if (type.members.length) {
writeLine();
increaseIndent();
// write members
emitLines(type.members);
decreaseIndent();
}
write("}");
}
}
function emitSourceFile(node: SourceFile) {
currentText = node.text;
currentLineMap = getLineStarts(node);
currentIdentifiers = node.identifiers;
isCurrentFileExternalModule = isExternalModule(node);
enclosingDeclaration = node;
emitDetachedComments(currentText, currentLineMap, writer, writeCommentRange, node, newLine, true /* remove comments */);
emitLines(node.statements);
}
// Return a temp variable name to be used in `export default` statements.
// The temp name will be of the form _default_counter.
// Note that export default is only allowed at most once in a module, so we
// do not need to keep track of created temp names.
function getExportDefaultTempVariableName(): string {
const baseName = "_default";
if (!currentIdentifiers.has(baseName)) {
return baseName;
}
let count = 0;
while (true) {
count++;
const name = baseName + "_" + count;
if (!currentIdentifiers.has(name)) {
return name;
}
}
}
function emitExportAssignment(node: ExportAssignment) {
if (node.expression.kind === SyntaxKind.Identifier) {
write(node.isExportEquals ? "export = " : "export default ");
writeTextOfNode(currentText, node.expression);
}
else {
// Expression
const tempVarName = getExportDefaultTempVariableName();
if (!noDeclare) {
write("declare ");
}
write("var ");
write(tempVarName);
write(": ");
writer.getSymbolAccessibilityDiagnostic = getDefaultExportAccessibilityDiagnostic;
resolver.writeTypeOfExpression(node.expression, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
write(";");
writeLine();
write(node.isExportEquals ? "export = " : "export default ");
write(tempVarName);
}
write(";");
writeLine();
// Make all the declarations visible for the export name
if (node.expression.kind === SyntaxKind.Identifier) {
const nodes = resolver.collectLinkedAliases(<Identifier>node.expression);
// write each of these declarations asynchronously
writeAsynchronousModuleElements(nodes);
}
function getDefaultExportAccessibilityDiagnostic(): SymbolAccessibilityDiagnostic {
return {
diagnosticMessage: Diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0,
errorNode: node
};
}
}
function isModuleElementVisible(node: Declaration) {
return resolver.isDeclarationVisible(node);
}
function emitModuleElement(node: Node, isModuleElementVisible: boolean) {
if (isModuleElementVisible) {
writeModuleElement(node);
}
// Import equals declaration in internal module can become visible as part of any emit so lets make sure we add these irrespective
else if (node.kind === SyntaxKind.ImportEqualsDeclaration ||
(node.parent.kind === SyntaxKind.SourceFile && isCurrentFileExternalModule)) {
let isVisible: boolean;
if (asynchronousSubModuleDeclarationEmitInfo && node.parent.kind !== SyntaxKind.SourceFile) {
// Import declaration of another module that is visited async so lets put it in right spot
asynchronousSubModuleDeclarationEmitInfo.push({
node,
outputPos: writer.getTextPos(),
indent: writer.getIndent(),
isVisible
});
}
else {
if (node.kind === SyntaxKind.ImportDeclaration) {
const importDeclaration = <ImportDeclaration>node;
if (importDeclaration.importClause) {
isVisible = (importDeclaration.importClause.name && resolver.isDeclarationVisible(importDeclaration.importClause)) ||
isVisibleNamedBinding(importDeclaration.importClause.namedBindings);
}
}
moduleElementDeclarationEmitInfo.push({
node,
outputPos: writer.getTextPos(),
indent: writer.getIndent(),
isVisible
});
}
}
}
function writeModuleElement(node: Node) {
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
return writeFunctionDeclaration(<FunctionLikeDeclaration>node);
case SyntaxKind.VariableStatement:
return writeVariableStatement(<VariableStatement>node);
case SyntaxKind.InterfaceDeclaration:
return writeInterfaceDeclaration(<InterfaceDeclaration>node);
case SyntaxKind.ClassDeclaration:
return writeClassDeclaration(<ClassDeclaration>node);
case SyntaxKind.TypeAliasDeclaration:
return writeTypeAliasDeclaration(<TypeAliasDeclaration>node);
case SyntaxKind.EnumDeclaration:
return writeEnumDeclaration(<EnumDeclaration>node);
case SyntaxKind.ModuleDeclaration:
return writeModuleDeclaration(<ModuleDeclaration>node);
case SyntaxKind.ImportEqualsDeclaration:
return writeImportEqualsDeclaration(<ImportEqualsDeclaration>node);
case SyntaxKind.ImportDeclaration:
return writeImportDeclaration(<ImportDeclaration>node);
default:
Debug.fail("Unknown symbol kind");
}
}
function emitModuleElementDeclarationFlags(node: Node) {
// If the node is parented in the current source file we need to emit export declare or just export
if (node.parent.kind === SyntaxKind.SourceFile) {
const modifiers = getModifierFlags(node);
// If the node is exported
if (modifiers & ModifierFlags.Export) {
write("export ");
}
if (modifiers & ModifierFlags.Default) {
write("default ");
}
else if (node.kind !== SyntaxKind.InterfaceDeclaration && !noDeclare) {
write("declare ");
}
}
}
function emitClassMemberDeclarationFlags(flags: ModifierFlags) {
if (flags & ModifierFlags.Private) {
write("private ");
}
else if (flags & ModifierFlags.Protected) {
write("protected ");
}
if (flags & ModifierFlags.Static) {
write("static ");
}
if (flags & ModifierFlags.Readonly) {
write("readonly ");
}
if (flags & ModifierFlags.Abstract) {
write("abstract ");
}
}
function writeImportEqualsDeclaration(node: ImportEqualsDeclaration) {
// note usage of writer. methods instead of aliases created, just to make sure we are using
// correct writer especially to handle asynchronous alias writing
emitJsDocComments(node);
if (hasModifier(node, ModifierFlags.Export)) {
write("export ");
}
write("import ");
writeTextOfNode(currentText, node.name);
write(" = ");
if (isInternalModuleImportEqualsDeclaration(node)) {
emitTypeWithNewGetSymbolAccessibilityDiagnostic(<EntityName>node.moduleReference, getImportEntityNameVisibilityError);
write(";");
}
else {
write("require(");
emitExternalModuleSpecifier(node);
write(");");
}
writer.writeLine();
function getImportEntityNameVisibilityError(): SymbolAccessibilityDiagnostic {
return {
diagnosticMessage: Diagnostics.Import_declaration_0_is_using_private_name_1,
errorNode: node,
typeName: node.name
};
}
}
function isVisibleNamedBinding(namedBindings: NamespaceImport | NamedImports): boolean {
if (namedBindings) {
if (namedBindings.kind === SyntaxKind.NamespaceImport) {
return resolver.isDeclarationVisible(<NamespaceImport>namedBindings);
}
else {
return forEach((<NamedImports>namedBindings).elements, namedImport => resolver.isDeclarationVisible(namedImport));
}
}
}
function writeImportDeclaration(node: ImportDeclaration) {
emitJsDocComments(node);
if (hasModifier(node, ModifierFlags.Export)) {
write("export ");
}
write("import ");
if (node.importClause) {
const currentWriterPos = writer.getTextPos();
if (node.importClause.name && resolver.isDeclarationVisible(node.importClause)) {
writeTextOfNode(currentText, node.importClause.name);
}
if (node.importClause.namedBindings && isVisibleNamedBinding(node.importClause.namedBindings)) {
if (currentWriterPos !== writer.getTextPos()) {
// If the default binding was emitted, write the separated
write(", ");
}
if (node.importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
write("* as ");
writeTextOfNode(currentText, (<NamespaceImport>node.importClause.namedBindings).name);
}
else {
write("{ ");
emitCommaList((<NamedImports>node.importClause.namedBindings).elements, emitImportOrExportSpecifier, resolver.isDeclarationVisible);
write(" }");
}
}
write(" from ");
}
emitExternalModuleSpecifier(node);
write(";");
writer.writeLine();
}
function emitExternalModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration) {
// emitExternalModuleSpecifier is usually called when we emit something in the.d.ts file that will make it an external module (i.e. import/export declarations).
// the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered
// external modules since they are indistinguishable from script files with ambient modules. To fix this in such d.ts files we'll emit top level 'export {}'
// so compiler will treat them as external modules.
resultHasExternalModuleIndicator = resultHasExternalModuleIndicator || parent.kind !== SyntaxKind.ModuleDeclaration;
let moduleSpecifier: Node;
if (parent.kind === SyntaxKind.ImportEqualsDeclaration) {
const node = parent as ImportEqualsDeclaration;
moduleSpecifier = getExternalModuleImportEqualsDeclarationExpression(node);
}
else if (parent.kind === SyntaxKind.ModuleDeclaration) {
moduleSpecifier = (<ModuleDeclaration>parent).name;
}
else {
const node = parent as (ImportDeclaration | ExportDeclaration);
moduleSpecifier = node.moduleSpecifier;
}
if (moduleSpecifier.kind === SyntaxKind.StringLiteral && isBundledEmit && (compilerOptions.out || compilerOptions.outFile)) {
const moduleName = getExternalModuleNameFromDeclaration(host, resolver, parent);
if (moduleName) {
write('"');
write(moduleName);
write('"');
return;
}
}
writeTextOfNode(currentText, moduleSpecifier);
}
function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) {
if (node.propertyName) {
writeTextOfNode(currentText, node.propertyName);
write(" as ");
}
writeTextOfNode(currentText, node.name);
}
function emitExportSpecifier(node: ExportSpecifier) {
emitImportOrExportSpecifier(node);
// Make all the declarations visible for the export name
const nodes = resolver.collectLinkedAliases(node.propertyName || node.name);
// write each of these declarations asynchronously
writeAsynchronousModuleElements(nodes);
}
function emitExportDeclaration(node: ExportDeclaration) {
emitJsDocComments(node);
write("export ");
if (node.exportClause) {
write("{ ");
emitCommaList(node.exportClause.elements, emitExportSpecifier);
write(" }");
}
else {
write("*");
}
if (node.moduleSpecifier) {
write(" from ");
emitExternalModuleSpecifier(node);
}
write(";");
writer.writeLine();
}
function writeModuleDeclaration(node: ModuleDeclaration) {
emitJsDocComments(node);
emitModuleElementDeclarationFlags(node);
if (isGlobalScopeAugmentation(node)) {
write("global ");
}
else {
if (node.flags & NodeFlags.Namespace) {
write("namespace ");
}
else {
write("module ");
}
if (isExternalModuleAugmentation(node)) {
emitExternalModuleSpecifier(node);
}
else {
writeTextOfNode(currentText, node.name);
}
}
while (node.body && node.body.kind !== SyntaxKind.ModuleBlock) {
node = <ModuleDeclaration>node.body;
write(".");
writeTextOfNode(currentText, node.name);
}
const prevEnclosingDeclaration = enclosingDeclaration;
if (node.body) {
enclosingDeclaration = node;
write(" {");
writeLine();
increaseIndent();
emitLines((<ModuleBlock>node.body).statements);
decreaseIndent();
write("}");
writeLine();
enclosingDeclaration = prevEnclosingDeclaration;
}
else {
write(";");
}
}
function writeTypeAliasDeclaration(node: TypeAliasDeclaration) {
const prevEnclosingDeclaration = enclosingDeclaration;
enclosingDeclaration = node;
emitJsDocComments(node);
emitModuleElementDeclarationFlags(node);
write("type ");
writeTextOfNode(currentText, node.name);
emitTypeParameters(node.typeParameters);
write(" = ");
emitTypeWithNewGetSymbolAccessibilityDiagnostic(node.type, getTypeAliasDeclarationVisibilityError);
write(";");
writeLine();
enclosingDeclaration = prevEnclosingDeclaration;
function getTypeAliasDeclarationVisibilityError(): SymbolAccessibilityDiagnostic {
return {
diagnosticMessage: Diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1,
errorNode: node.type,
typeName: node.name
};
}
}
function writeEnumDeclaration(node: EnumDeclaration) {
emitJsDocComments(node);
emitModuleElementDeclarationFlags(node);
if (isConst(node)) {
write("const ");
}
write("enum ");
writeTextOfNode(currentText, node.name);
write(" {");
writeLine();
increaseIndent();
emitLines(node.members);
decreaseIndent();
write("}");
writeLine();
}
function emitEnumMemberDeclaration(node: EnumMember) {
emitJsDocComments(node);
writeTextOfNode(currentText, node.name);
const enumMemberValue = resolver.getConstantValue(node);
if (enumMemberValue !== undefined) {
write(" = ");
write(enumMemberValue.toString());
}
write(",");
writeLine();
}
function isPrivateMethodTypeParameter(node: TypeParameterDeclaration) {
return node.parent.kind === SyntaxKind.MethodDeclaration && hasModifier(node.parent, ModifierFlags.Private);
}
function emitTypeParameters(typeParameters: TypeParameterDeclaration[]) {
function emitTypeParameter(node: TypeParameterDeclaration) {
increaseIndent();
emitJsDocComments(node);
decreaseIndent();
writeTextOfNode(currentText, node.name);
// If there is constraint present and this is not a type parameter of the private method emit the constraint
if (node.constraint && !isPrivateMethodTypeParameter(node)) {
write(" extends ");
if (node.parent.kind === SyntaxKind.FunctionType ||
node.parent.kind === SyntaxKind.ConstructorType ||
(node.parent.parent && node.parent.parent.kind === SyntaxKind.TypeLiteral)) {
Debug.assert(node.parent.kind === SyntaxKind.MethodDeclaration ||
node.parent.kind === SyntaxKind.MethodSignature ||
node.parent.kind === SyntaxKind.FunctionType ||
node.parent.kind === SyntaxKind.ConstructorType ||
node.parent.kind === SyntaxKind.CallSignature ||
node.parent.kind === SyntaxKind.ConstructSignature);
emitType(node.constraint);
}
else {
emitTypeWithNewGetSymbolAccessibilityDiagnostic(node.constraint, getTypeParameterConstraintVisibilityError);
}
}
function getTypeParameterConstraintVisibilityError(): SymbolAccessibilityDiagnostic {
// Type parameter constraints are named by user so we should always be able to name it
let diagnosticMessage: DiagnosticMessage;
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_class_has_or_is_using_private_name_1;
break;
case SyntaxKind.InterfaceDeclaration:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.ConstructSignature:
diagnosticMessage = Diagnostics.Type_parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.CallSignature:
diagnosticMessage = Diagnostics.Type_parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (hasModifier(node.parent, ModifierFlags.Static)) {
diagnosticMessage = Diagnostics.Type_parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
diagnosticMessage = Diagnostics.Type_parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1;
}
else {
diagnosticMessage = Diagnostics.Type_parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1;
}
break;
case SyntaxKind.FunctionDeclaration:
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_function_has_or_is_using_private_name_1;
break;
default:
Debug.fail("This is unknown parent for type parameter: " + node.parent.kind);
}
return {
diagnosticMessage,
errorNode: node,
typeName: node.name
};
}
}
if (typeParameters) {
write("<");
emitCommaList(typeParameters, emitTypeParameter);
write(">");
}
}
function emitHeritageClause(typeReferences: ExpressionWithTypeArguments[], isImplementsList: boolean) {
if (typeReferences) {
write(isImplementsList ? " implements " : " extends ");
emitCommaList(typeReferences, emitTypeOfTypeReference);
}
function emitTypeOfTypeReference(node: ExpressionWithTypeArguments) {
if (isEntityNameExpression(node.expression)) {
emitTypeWithNewGetSymbolAccessibilityDiagnostic(node, getHeritageClauseVisibilityError);
}
else if (!isImplementsList && node.expression.kind === SyntaxKind.NullKeyword) {
write("null");
}
else {
writer.getSymbolAccessibilityDiagnostic = getHeritageClauseVisibilityError;
resolver.writeBaseConstructorTypeOfClass(<ClassLikeDeclaration>enclosingDeclaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
}
function getHeritageClauseVisibilityError(): SymbolAccessibilityDiagnostic {
let diagnosticMessage: DiagnosticMessage;
// Heritage clause is written by user so it can always be named
if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
// Class or Interface implemented/extended is inaccessible
diagnosticMessage = isImplementsList ?
Diagnostics.Implements_clause_of_exported_class_0_has_or_is_using_private_name_1 :
Diagnostics.Extends_clause_of_exported_class_0_has_or_is_using_private_name_1;
}
else {
// interface is inaccessible
diagnosticMessage = Diagnostics.Extends_clause_of_exported_interface_0_has_or_is_using_private_name_1;
}
return {
diagnosticMessage,
errorNode: node,
typeName: (<Declaration>node.parent.parent).name
};
}
}
}
function writeClassDeclaration(node: ClassDeclaration) {
function emitParameterProperties(constructorDeclaration: ConstructorDeclaration) {
if (constructorDeclaration) {
forEach(constructorDeclaration.parameters, param => {
if (hasModifier(param, ModifierFlags.ParameterPropertyModifier)) {
emitPropertyDeclaration(param);
}
});
}
}
emitJsDocComments(node);
emitModuleElementDeclarationFlags(node);
if (hasModifier(node, ModifierFlags.Abstract)) {
write("abstract ");
}
write("class ");
writeTextOfNode(currentText, node.name);
const prevEnclosingDeclaration = enclosingDeclaration;
enclosingDeclaration = node;
emitTypeParameters(node.typeParameters);
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
if (baseTypeNode) {
emitHeritageClause([baseTypeNode], /*isImplementsList*/ false);
}
emitHeritageClause(getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true);
write(" {");
writeLine();
increaseIndent();
emitParameterProperties(getFirstConstructorWithBody(node));
emitLines(node.members);
decreaseIndent();
write("}");
writeLine();
enclosingDeclaration = prevEnclosingDeclaration;
}
function writeInterfaceDeclaration(node: InterfaceDeclaration) {
emitJsDocComments(node);
emitModuleElementDeclarationFlags(node);
write("interface ");
writeTextOfNode(currentText, node.name);
const prevEnclosingDeclaration = enclosingDeclaration;
enclosingDeclaration = node;
emitTypeParameters(node.typeParameters);
emitHeritageClause(getInterfaceBaseTypeNodes(node), /*isImplementsList*/ false);
write(" {");
writeLine();
increaseIndent();
emitLines(node.members);
decreaseIndent();
write("}");
writeLine();
enclosingDeclaration = prevEnclosingDeclaration;
}
function emitPropertyDeclaration(node: Declaration) {
if (hasDynamicName(node)) {
return;
}
emitJsDocComments(node);
emitClassMemberDeclarationFlags(getModifierFlags(node));
emitVariableDeclaration(<VariableDeclaration>node);
write(";");
writeLine();
}
function emitVariableDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration) {
// If we are emitting property it isn't moduleElement and hence we already know it needs to be emitted
// so there is no check needed to see if declaration is visible
if (node.kind !== SyntaxKind.VariableDeclaration || resolver.isDeclarationVisible(node)) {
if (isBindingPattern(node.name)) {
emitBindingPattern(<BindingPattern>node.name);
}
else {
// If this node is a computed name, it can only be a symbol, because we've already skipped
// it if it's not a well known symbol. In that case, the text of the name will be exactly
// what we want, namely the name expression enclosed in brackets.
writeTextOfNode(currentText, node.name);
// If optional property emit ? but in the case of parameterProperty declaration with "?" indicating optional parameter for the constructor
// we don't want to emit property declaration with "?"
if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature ||
(node.kind === SyntaxKind.Parameter && !isParameterPropertyDeclaration(<ParameterDeclaration>node))) && hasQuestionToken(node)) {
write("?");
}
if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) && node.parent.kind === SyntaxKind.TypeLiteral) {
emitTypeOfVariableDeclarationFromTypeLiteral(node);
}
else if (resolver.isLiteralConstDeclaration(node)) {
write(" = ");
resolver.writeLiteralConstValue(node, writer);
}
else if (!hasModifier(node, ModifierFlags.Private)) {
writeTypeOfDeclaration(node, node.type, getVariableDeclarationTypeVisibilityError);
}
}
}
function getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) {
if (node.kind === SyntaxKind.VariableDeclaration) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Exported_variable_0_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Exported_variable_0_has_or_is_using_private_name_1;
}
// This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit
else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) {
// TODO(jfreeman): Deal with computed properties in error reporting.
if (hasModifier(node, ModifierFlags.Static)) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1;
}
else {
// Interfaces cannot have types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1;
}
}
}
function getVariableDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
const diagnosticMessage = getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult);
return diagnosticMessage !== undefined ? {
diagnosticMessage,
errorNode: node,
typeName: node.name
} : undefined;
}
function emitBindingPattern(bindingPattern: BindingPattern) {
// Only select non-omitted expression from the bindingPattern's elements.
// We have to do this to avoid emitting trailing commas.
// For example:
// original: var [, c,,] = [ 2,3,4]
// emitted: declare var c: number; // instead of declare var c:number, ;
const elements: Node[] = [];
for (const element of bindingPattern.elements) {
if (element.kind !== SyntaxKind.OmittedExpression) {
elements.push(element);
}
}
emitCommaList(elements, emitBindingElement);
}
function emitBindingElement(bindingElement: BindingElement) {
function getBindingElementTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
const diagnosticMessage = getVariableDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult);
return diagnosticMessage !== undefined ? {
diagnosticMessage,
errorNode: bindingElement,
typeName: bindingElement.name
} : undefined;
}
if (bindingElement.name) {
if (isBindingPattern(bindingElement.name)) {
emitBindingPattern(<BindingPattern>bindingElement.name);
}
else {
writeTextOfNode(currentText, bindingElement.name);
writeTypeOfDeclaration(bindingElement, /*type*/ undefined, getBindingElementTypeVisibilityError);
}
}
}
}
function emitTypeOfVariableDeclarationFromTypeLiteral(node: VariableLikeDeclaration) {
// if this is property of type literal,
// or is parameter of method/call/construct/index signature of type literal
// emit only if type is specified
if (node.type) {
write(": ");
emitType(node.type);
}
}
function isVariableStatementVisible(node: VariableStatement) {
return forEach(node.declarationList.declarations, varDeclaration => resolver.isDeclarationVisible(varDeclaration));
}
function writeVariableStatement(node: VariableStatement) {
emitJsDocComments(node);
emitModuleElementDeclarationFlags(node);
if (isLet(node.declarationList)) {
write("let ");
}
else if (isConst(node.declarationList)) {
write("const ");
}
else {
write("var ");
}
emitCommaList(node.declarationList.declarations, emitVariableDeclaration, resolver.isDeclarationVisible);
write(";");
writeLine();
}
function emitAccessorDeclaration(node: AccessorDeclaration) {
if (hasDynamicName(node)) {
return;
}
const accessors = getAllAccessorDeclarations((<ClassDeclaration>node.parent).members, node);
let accessorWithTypeAnnotation: AccessorDeclaration;
if (node === accessors.firstAccessor) {
emitJsDocComments(accessors.getAccessor);
emitJsDocComments(accessors.setAccessor);
emitClassMemberDeclarationFlags(getModifierFlags(node) | (accessors.setAccessor ? 0 : ModifierFlags.Readonly));
writeTextOfNode(currentText, node.name);
if (!hasModifier(node, ModifierFlags.Private)) {
accessorWithTypeAnnotation = node;
let type = getTypeAnnotationFromAccessor(node);
if (!type) {
// couldn't get type for the first accessor, try the another one
const anotherAccessor = node.kind === SyntaxKind.GetAccessor ? accessors.setAccessor : accessors.getAccessor;
type = getTypeAnnotationFromAccessor(anotherAccessor);
if (type) {
accessorWithTypeAnnotation = anotherAccessor;
}
}
writeTypeOfDeclaration(node, type, getAccessorDeclarationTypeVisibilityError);
}
write(";");
writeLine();
}
function getTypeAnnotationFromAccessor(accessor: AccessorDeclaration): TypeNode {
if (accessor) {
return accessor.kind === SyntaxKind.GetAccessor
? accessor.type // Getter - return type
: accessor.parameters.length > 0
? accessor.parameters[0].type // Setter parameter type
: undefined;
}
}
function getAccessorDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
let diagnosticMessage: DiagnosticMessage;
if (accessorWithTypeAnnotation.kind === SyntaxKind.SetAccessor) {
// Setters have to have type named and cannot infer it so, the type should always be named
if (hasModifier(accessorWithTypeAnnotation.parent, ModifierFlags.Static)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_public_static_property_setter_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_public_static_property_setter_from_exported_class_has_or_is_using_private_name_1;
}
else {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_public_property_setter_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_public_property_setter_from_exported_class_has_or_is_using_private_name_1;
}
return {
diagnosticMessage,
errorNode: <Node>accessorWithTypeAnnotation.parameters[0],
// TODO(jfreeman): Investigate why we are passing node.name instead of node.parameters[0].name
typeName: accessorWithTypeAnnotation.name
};
}
else {
if (hasModifier(accessorWithTypeAnnotation, ModifierFlags.Static)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
Diagnostics.Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_private_name_0;
}
else {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_property_getter_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
Diagnostics.Return_type_of_public_property_getter_from_exported_class_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_public_property_getter_from_exported_class_has_or_is_using_private_name_0;
}
return {
diagnosticMessage,
errorNode: <Node>accessorWithTypeAnnotation.name,
typeName: undefined
};
}
}
}
function writeFunctionDeclaration(node: FunctionLikeDeclaration) {
if (hasDynamicName(node)) {
return;
}
// If we are emitting Method/Constructor it isn't moduleElement and hence already determined to be emitting
// so no need to verify if the declaration is visible
if (!resolver.isImplementationOfOverload(node)) {
emitJsDocComments(node);
if (node.kind === SyntaxKind.FunctionDeclaration) {
emitModuleElementDeclarationFlags(node);
}
else if (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.Constructor) {
emitClassMemberDeclarationFlags(getModifierFlags(node));
}
if (node.kind === SyntaxKind.FunctionDeclaration) {
write("function ");
writeTextOfNode(currentText, node.name);
}
else if (node.kind === SyntaxKind.Constructor) {
write("constructor");
}
else {
writeTextOfNode(currentText, node.name);
if (hasQuestionToken(node)) {
write("?");
}
}
emitSignatureDeclaration(node);
}
}
function emitSignatureDeclarationWithJsDocComments(node: SignatureDeclaration) {
emitJsDocComments(node);
emitSignatureDeclaration(node);
}
function emitSignatureDeclaration(node: SignatureDeclaration) {
const prevEnclosingDeclaration = enclosingDeclaration;
enclosingDeclaration = node;
let closeParenthesizedFunctionType = false;
if (node.kind === SyntaxKind.IndexSignature) {
// Index signature can have readonly modifier
emitClassMemberDeclarationFlags(getModifierFlags(node));
write("[");
}
else {
// Construct signature or constructor type write new Signature
if (node.kind === SyntaxKind.ConstructSignature || node.kind === SyntaxKind.ConstructorType) {
write("new ");
}
else if (node.kind === SyntaxKind.FunctionType) {
const currentOutput = writer.getText();
// Do not generate incorrect type when function type with type parameters is type argument
// This could happen if user used space between two '<' making it error free
// e.g var x: A< <Tany>(a: Tany)=>Tany>;
if (node.typeParameters && currentOutput.charAt(currentOutput.length - 1) === "<") {
closeParenthesizedFunctionType = true;
write("(");
}
}
emitTypeParameters(node.typeParameters);
write("(");
}
// Parameters
emitCommaList(node.parameters, emitParameterDeclaration);
if (node.kind === SyntaxKind.IndexSignature) {
write("]");
}
else {
write(")");
}
// If this is not a constructor and is not private, emit the return type
const isFunctionTypeOrConstructorType = node.kind === SyntaxKind.FunctionType || node.kind === SyntaxKind.ConstructorType;
if (isFunctionTypeOrConstructorType || node.parent.kind === SyntaxKind.TypeLiteral) {
// Emit type literal signature return type only if specified
if (node.type) {
write(isFunctionTypeOrConstructorType ? " => " : ": ");
emitType(node.type);
}
}
else if (node.kind !== SyntaxKind.Constructor && !hasModifier(node, ModifierFlags.Private)) {
writeReturnTypeAtSignature(node, getReturnTypeVisibilityError);
}
enclosingDeclaration = prevEnclosingDeclaration;
if (!isFunctionTypeOrConstructorType) {
write(";");
writeLine();
}
else if (closeParenthesizedFunctionType) {
write(")");
}
function getReturnTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
let diagnosticMessage: DiagnosticMessage;
switch (node.kind) {
case SyntaxKind.ConstructSignature:
// Interfaces cannot have return types that cannot be named
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_0;
break;
case SyntaxKind.CallSignature:
// Interfaces cannot have return types that cannot be named
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_private_name_0;
break;
case SyntaxKind.IndexSignature:
// Interfaces cannot have return types that cannot be named
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_private_name_0;
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (hasModifier(node, ModifierFlags.Static)) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_private_name_0;
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_private_name_0;
}
else {
// Interfaces cannot have return types that cannot be named
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
Diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_private_name_0;
}
break;
case SyntaxKind.FunctionDeclaration:
diagnosticMessage = symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named :
Diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_private_module_1 :
Diagnostics.Return_type_of_exported_function_has_or_is_using_private_name_0;
break;
default:
Debug.fail("This is unknown kind for signature: " + node.kind);
}
return {
diagnosticMessage,
errorNode: <Node>node.name || node
};
}
}
function emitParameterDeclaration(node: ParameterDeclaration) {
increaseIndent();
emitJsDocComments(node);
if (node.dotDotDotToken) {
write("...");
}
if (isBindingPattern(node.name)) {
// For bindingPattern, we can't simply writeTextOfNode from the source file
// because we want to omit the initializer and using writeTextOfNode will result in initializer get emitted.
// Therefore, we will have to recursively emit each element in the bindingPattern.
emitBindingPattern(<BindingPattern>node.name);
}
else {
writeTextOfNode(currentText, node.name);
}
if (resolver.isOptionalParameter(node)) {
write("?");
}
decreaseIndent();
if (node.parent.kind === SyntaxKind.FunctionType ||
node.parent.kind === SyntaxKind.ConstructorType ||
node.parent.parent.kind === SyntaxKind.TypeLiteral) {
emitTypeOfVariableDeclarationFromTypeLiteral(node);
}
else if (!hasModifier(node.parent, ModifierFlags.Private)) {
writeTypeOfDeclaration(node, node.type, getParameterDeclarationTypeVisibilityError);
}
function getParameterDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
const diagnosticMessage: DiagnosticMessage = getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult);
return diagnosticMessage !== undefined ? {
diagnosticMessage,
errorNode: node,
typeName: node.name
} : undefined;
}
function getParameterDeclarationTypeVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult): DiagnosticMessage {
switch (node.parent.kind) {
case SyntaxKind.Constructor:
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_private_name_1;
case SyntaxKind.ConstructSignature:
// Interfaces cannot have parameter types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1;
case SyntaxKind.CallSignature:
// Interfaces cannot have parameter types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (hasModifier(node.parent, ModifierFlags.Static)) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1;
}
else {
// Interfaces cannot have parameter types that cannot be named
return symbolAccessibilityResult.errorModuleName ?
Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1;
}
case SyntaxKind.FunctionDeclaration:
return symbolAccessibilityResult.errorModuleName ?
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
Diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Parameter_0_of_exported_function_has_or_is_using_private_name_1;
default:
Debug.fail("This is unknown parent for parameter: " + node.parent.kind);
}
}
function emitBindingPattern(bindingPattern: BindingPattern) {
// We have to explicitly emit square bracket and bracket because these tokens are not store inside the node.
if (bindingPattern.kind === SyntaxKind.ObjectBindingPattern) {
write("{");
emitCommaList(bindingPattern.elements, emitBindingElement);
write("}");
}
else if (bindingPattern.kind === SyntaxKind.ArrayBindingPattern) {
write("[");
const elements = bindingPattern.elements;
emitCommaList(elements, emitBindingElement);
if (elements && elements.hasTrailingComma) {
write(", ");
}
write("]");
}
}
function emitBindingElement(bindingElement: BindingElement | OmittedExpression) {
if (bindingElement.kind === SyntaxKind.OmittedExpression) {
// If bindingElement is an omittedExpression (i.e. containing elision),
// we will emit blank space (although this may differ from users' original code,
// it allows emitSeparatedList to write separator appropriately)
// Example:
// original: function foo([, x, ,]) {}
// emit : function foo([ , x, , ]) {}
write(" ");
}
else if (bindingElement.kind === SyntaxKind.BindingElement) {
if (bindingElement.propertyName) {
// bindingElement has propertyName property in the following case:
// { y: [a,b,c] ...} -> bindingPattern will have a property called propertyName for "y"
// We have to explicitly emit the propertyName before descending into its binding elements.
// Example:
// original: function foo({y: [a,b,c]}) {}
// emit : declare function foo({y: [a, b, c]}: { y: [any, any, any] }) void;
writeTextOfNode(currentText, bindingElement.propertyName);
write(": ");
}
if (bindingElement.name) {
if (isBindingPattern(bindingElement.name)) {
// If it is a nested binding pattern, we will recursively descend into each element and emit each one separately.
// In the case of rest element, we will omit rest element.
// Example:
// original: function foo([a, [[b]], c] = [1,[["string"]], 3]) {}
// emit : declare function foo([a, [[b]], c]: [number, [[string]], number]): void;
// original with rest: function foo([a, ...c]) {}
// emit : declare function foo([a, ...c]): void;
emitBindingPattern(<BindingPattern>bindingElement.name);
}
else {
Debug.assert(bindingElement.name.kind === SyntaxKind.Identifier);
// If the node is just an identifier, we will simply emit the text associated with the node's name
// Example:
// original: function foo({y = 10, x}) {}
// emit : declare function foo({y, x}: {number, any}): void;
if (bindingElement.dotDotDotToken) {
write("...");
}
writeTextOfNode(currentText, bindingElement.name);
}
}
}
}
}
function emitNode(node: Node) {
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.EnumDeclaration:
return emitModuleElement(node, isModuleElementVisible(<Declaration>node));
case SyntaxKind.VariableStatement:
return emitModuleElement(node, isVariableStatementVisible(<VariableStatement>node));
case SyntaxKind.ImportDeclaration:
// Import declaration without import clause is visible, otherwise it is not visible
return emitModuleElement(node, /*isModuleElementVisible*/!(<ImportDeclaration>node).importClause);
case SyntaxKind.ExportDeclaration:
return emitExportDeclaration(<ExportDeclaration>node);
case SyntaxKind.Constructor:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return writeFunctionDeclaration(<FunctionLikeDeclaration>node);
case SyntaxKind.ConstructSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.IndexSignature:
return emitSignatureDeclarationWithJsDocComments(<SignatureDeclaration>node);
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return emitAccessorDeclaration(<AccessorDeclaration>node);
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
return emitPropertyDeclaration(<PropertyDeclaration>node);
case SyntaxKind.EnumMember:
return emitEnumMemberDeclaration(<EnumMember>node);
case SyntaxKind.ExportAssignment:
return emitExportAssignment(<ExportAssignment>node);
case SyntaxKind.SourceFile:
return emitSourceFile(<SourceFile>node);
}
}
/**
* Adds the reference to referenced file, returns true if global file reference was emitted
* @param referencedFile
* @param addBundledFileReference Determines if global file reference corresponding to bundled file should be emitted or not
*/
function writeReferencePath(referencedFile: SourceFile, addBundledFileReference: boolean, emitOnlyDtsFiles: boolean): boolean {
let declFileName: string;
let addedBundledEmitReference = false;
if (isDeclarationFile(referencedFile)) {
// Declaration file, use declaration file name
declFileName = referencedFile.fileName;
}
else {
// Get the declaration file path
forEachExpectedEmitFile(host, getDeclFileName, referencedFile, emitOnlyDtsFiles);
}
if (declFileName) {
declFileName = getRelativePathToDirectoryOrUrl(
getDirectoryPath(normalizeSlashes(declarationFilePath)),
declFileName,
host.getCurrentDirectory(),
host.getCanonicalFileName,
/*isAbsolutePathAnUrl*/ false);
referencesOutput += `/// <reference path="${declFileName}" />${newLine}`;
}
return addedBundledEmitReference;
function getDeclFileName(emitFileNames: EmitFileNames, _sourceFiles: SourceFile[], isBundledEmit: boolean) {
// Dont add reference path to this file if it is a bundled emit and caller asked not emit bundled file path
if (isBundledEmit && !addBundledFileReference) {
return;
}
Debug.assert(!!emitFileNames.declarationFilePath || isSourceFileJavaScript(referencedFile), "Declaration file is not present only for javascript files");
declFileName = emitFileNames.declarationFilePath || emitFileNames.jsFilePath;
addedBundledEmitReference = isBundledEmit;
}
}
}
/* @internal */
export function writeDeclarationFile(declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, emitOnlyDtsFiles: boolean) {
const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFiles, isBundledEmit, emitOnlyDtsFiles);
const emitSkipped = emitDeclarationResult.reportedDeclarationError || host.isEmitBlocked(declarationFilePath) || host.getCompilerOptions().noEmit;
if (!emitSkipped) {
const declarationOutput = emitDeclarationResult.referencesOutput
+ getDeclarationOutput(emitDeclarationResult.synchronousDeclarationOutput, emitDeclarationResult.moduleElementDeclarationEmitInfo);
writeFile(host, emitterDiagnostics, declarationFilePath, declarationOutput, host.getCompilerOptions().emitBOM, sourceFiles);
}
return emitSkipped;
function getDeclarationOutput(synchronousDeclarationOutput: string, moduleElementDeclarationEmitInfo: ModuleElementDeclarationEmitInfo[]) {
let appliedSyncOutputPos = 0;
let declarationOutput = "";
// apply asynchronous additions to the synchronous output
forEach(moduleElementDeclarationEmitInfo, aliasEmitInfo => {
if (aliasEmitInfo.asynchronousOutput) {
declarationOutput += synchronousDeclarationOutput.substring(appliedSyncOutputPos, aliasEmitInfo.outputPos);
declarationOutput += getDeclarationOutput(aliasEmitInfo.asynchronousOutput, aliasEmitInfo.subModuleElementDeclarationEmitInfo);
appliedSyncOutputPos = aliasEmitInfo.outputPos;
}
});
declarationOutput += synchronousDeclarationOutput.substring(appliedSyncOutputPos);
return declarationOutput;
}
}
}