TypeScript/src/compiler/emitter.ts

2465 lines
114 KiB
TypeScript
Raw Normal View History

2014-07-13 01:04:16 +02:00
/// <reference path="types.ts"/>
/// <reference path="core.ts"/>
/// <reference path="scanner.ts"/>
/// <reference path="parser.ts"/>
module ts {
interface EmitTextWriter extends TextWriter {
writeLiteral(s: string): void;
getTextPos(): number;
getLine(): number;
getColumn(): number;
2014-07-13 01:04:16 +02:00
}
var indentStrings: string[] = [];
function getIndentString(level: number) {
return indentStrings[level] || (indentStrings[level] = level === 0 ? "" : getIndentString(level - 1) + " ");
2014-07-13 01:04:16 +02:00
}
export function emitFiles(resolver: EmitResolver): EmitResult {
2014-07-13 01:04:16 +02:00
var program = resolver.getProgram();
var compilerHost = program.getCompilerHost();
var compilerOptions = program.getCompilerOptions();
var sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap ? [] : undefined;
var diagnostics: Diagnostic[] = [];
function getSourceFilePathInNewDir(newDirPath: string, sourceFile: SourceFile) {
var sourceFilePath = getNormalizedPathFromPathCompoments(getNormalizedPathComponents(sourceFile.filename, compilerHost.getCurrentDirectory()));
sourceFilePath = sourceFilePath.replace(program.getCommonSourceDirectory(), "");
return combinePaths(newDirPath, sourceFilePath);
}
function shouldEmitToOwnFile(sourceFile: SourceFile) {
if (!(sourceFile.flags & NodeFlags.DeclarationFile)) {
if ((sourceFile.flags & NodeFlags.ExternalModule || !compilerOptions.out) && !fileExtensionIs(sourceFile.filename, ".js")) {
return true;
2014-07-13 01:04:16 +02:00
}
}
}
function getOwnEmitOutputFilePath(sourceFile: SourceFile, extension: string) {
if (program.getCompilerOptions().outDir) {
var emitOutputFilePathWithoutExtension = getModuleNameFromFilename(getSourceFilePathInNewDir(program.getCompilerOptions().outDir, sourceFile));
}
else {
var emitOutputFilePathWithoutExtension = getModuleNameFromFilename(sourceFile.filename);
2014-07-13 01:04:16 +02:00
}
return emitOutputFilePathWithoutExtension + extension;
2014-07-13 01:04:16 +02:00
}
function isExternalModuleOrDeclarationFile(sourceFile: SourceFile) {
return !!(sourceFile.flags & (NodeFlags.ExternalModule | NodeFlags.DeclarationFile));
}
2014-07-13 01:04:16 +02:00
function getFirstConstructorWithBody(node: ClassDeclaration): ConstructorDeclaration {
return forEach(node.members, member => {
if (member.kind === SyntaxKind.Constructor && (<ConstructorDeclaration>member).body) {
return <ConstructorDeclaration>member;
}
});
}
function getAllAccessorDeclarations(node: ClassDeclaration, accessor: AccessorDeclaration) {
var firstAccessor: AccessorDeclaration;
var getAccessor: AccessorDeclaration;
var setAccessor: AccessorDeclaration;
forEach(node.members, (member: Declaration) => {
if ((member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor) &&
member.name.text === accessor.name.text &&
(member.flags & NodeFlags.Static) === (accessor.flags & NodeFlags.Static)) {
if (!firstAccessor) {
firstAccessor = <AccessorDeclaration>member;
}
if (member.kind === SyntaxKind.GetAccessor && !getAccessor) {
getAccessor = <AccessorDeclaration>member;
}
2014-07-13 01:04:16 +02:00
if (member.kind === SyntaxKind.SetAccessor && !setAccessor) {
setAccessor = <AccessorDeclaration>member;
}
2014-07-13 01:04:16 +02:00
}
});
return {
firstAccessor: firstAccessor,
getAccessor: getAccessor,
setAccessor: setAccessor
};
2014-07-13 01:04:16 +02:00
}
function createTextWriter(writeSymbol: (symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags)=> void): EmitTextWriter {
var output = "";
var indent = 0;
var lineStart = true;
var lineCount = 0;
var linePos = 0;
function write(s: string) {
if (s && s.length) {
if (lineStart) {
output += getIndentString(indent);
lineStart = false;
}
output += s;
}
}
function writeLiteral(s: string) {
if (s && s.length) {
write(s);
var pos = 0;
while (pos < s.length) {
switch (s.charCodeAt(pos++)) {
case CharacterCodes.carriageReturn:
if (pos < s.length && s.charCodeAt(pos) === CharacterCodes.lineFeed) {
pos++;
}
case CharacterCodes.lineFeed:
lineCount++;
linePos = output.length - s.length + pos;
break;
}
2014-07-13 01:04:16 +02:00
}
}
}
function writeLine() {
if (!lineStart) {
output += sys.newLine;
lineCount++;
linePos = output.length;
lineStart = true;
}
2014-07-13 01:04:16 +02:00
}
return {
write: write,
writeSymbol: writeSymbol,
writeLiteral: writeLiteral,
writeLine: writeLine,
increaseIndent: () => indent++,
decreaseIndent: () => indent--,
getTextPos: () => output.length,
getLine: () => lineCount + 1,
getColumn: () => lineStart ? indent * 4 + 1 : output.length - linePos + 1,
getText: () => output
};
2014-07-13 01:04:16 +02:00
}
// Get source text of node in the current source file. Unlike getSourceTextOfNode this function
// doesn't walk the parent chain to find the containing source file, rather it assumes the node is
// in the source file currently being processed.
var currentSourceFile: SourceFile;
function getSourceTextOfLocalNode(node: Node): string {
var text = currentSourceFile.text;
return text.substring(skipTrivia(text, node.pos), node.end);
}
2014-07-13 01:04:16 +02:00
function writeFile(filename: string, data: string) {
compilerHost.writeFile(filename, data, hostErrorMessage => {
diagnostics.push(createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, filename, hostErrorMessage));
});
}
2014-07-13 01:04:16 +02:00
function emitJavaScript(jsFilePath: string, root?: SourceFile) {
var writer = createTextWriter(writeSymbol);
var write = writer.write;
var writeLine = writer.writeLine;
var increaseIndent = writer.increaseIndent;
var decreaseIndent = writer.decreaseIndent;
2014-07-13 01:04:16 +02:00
var extendsEmitted = false;
2014-07-13 01:04:16 +02:00
/** write emitted output to disk*/
var writeEmittedFiles = writeJavaScriptFile;
2014-07-13 01:04:16 +02:00
/** Emit a node */
var emit = emitNode;
2014-07-13 01:04:16 +02:00
/** Called just before starting emit of a node */
var emitStart = function (node: Node) { }
2014-07-13 01:04:16 +02:00
/** Called once the emit of the node is done */
var emitEnd = function (node: Node) { }
2014-07-13 01:04:16 +02:00
/** Emit the text for the given token that comes after startPos
* This by default writes the text provided with the given tokenKind
* but if optional emitFn callback is provided the text is emitted using the callback instead of default text
* @param tokenKind the kind of the token to search and emit
* @param startPos the position in the source to start searching for the token
* @param emitFn if given will be invoked to emit the text instead of actual token emit */
var emitToken = emitTokenText;
2014-07-13 01:04:16 +02:00
/** Called to notify start of new source file emit */
var emitNewSourceFileStart = function (node: SourceFile) { }
2014-07-13 01:04:16 +02:00
/** Called to before starting the lexical scopes as in function/class in the emitted code because of node
* @param scopeDeclaration node that starts the lexical scope
* @param scopeName Optional name of this scope instead of deducing one from the declaration node */
var scopeEmitStart = function (scopeDeclaration: Node, scopeName?: string) { }
2014-07-13 01:04:16 +02:00
/** Called after coming out of the scope */
var scopeEmitEnd = function () { }
2014-07-13 01:04:16 +02:00
/** Sourcemap data that will get encoded */
var sourceMapData: SourceMapData;
2014-07-13 01:04:16 +02:00
function writeSymbol(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags) { }
function initializeEmitterWithSourceMaps() {
var sourceMapDir: string; // The directory in which sourcemap will be
2014-07-13 01:04:16 +02:00
// Current source map file and its index in the sources list
var sourceMapSourceIndex = -1;
2014-07-13 01:04:16 +02:00
// Names and its index map
var sourceMapNameIndexMap: Map<number> = {};
var sourceMapNameIndices: number[] = [];
function getSourceMapNameIndex() {
return sourceMapNameIndices.length ? sourceMapNameIndices[sourceMapNameIndices.length - 1] : -1;
}
2014-07-13 01:04:16 +02:00
// Last recorded and encoded spans
var lastRecordedSourceMapSpan: SourceMapSpan;
var lastEncodedSourceMapSpan: SourceMapSpan = {
emittedLine: 1,
emittedColumn: 1,
sourceLine: 1,
sourceColumn: 1,
sourceIndex: 0
};
var lastEncodedNameIndex = 0;
// Encoding for sourcemap span
function encodeLastRecordedSourceMapSpan() {
if (!lastRecordedSourceMapSpan || lastRecordedSourceMapSpan === lastEncodedSourceMapSpan) {
return;
}
var prevEncodedEmittedColumn = lastEncodedSourceMapSpan.emittedColumn;
// Line/Comma deliminators
if (lastEncodedSourceMapSpan.emittedLine == lastRecordedSourceMapSpan.emittedLine) {
// Emit comma to separate the entry
if (sourceMapData.sourceMapMappings) {
sourceMapData.sourceMapMappings += ",";
}
}
else {
// Emit line deliminators
for (var encodedLine = lastEncodedSourceMapSpan.emittedLine; encodedLine < lastRecordedSourceMapSpan.emittedLine; encodedLine++) {
sourceMapData.sourceMapMappings += ";";
}
prevEncodedEmittedColumn = 1;
}
// 1. Relative Column 0 based
sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.emittedColumn - prevEncodedEmittedColumn);
2014-07-13 01:04:16 +02:00
// 2. Relative sourceIndex
sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.sourceIndex - lastEncodedSourceMapSpan.sourceIndex);
2014-07-13 01:04:16 +02:00
// 3. Relative sourceLine 0 based
sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.sourceLine - lastEncodedSourceMapSpan.sourceLine);
2014-07-13 01:04:16 +02:00
// 4. Relative sourceColumn 0 based
sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.sourceColumn - lastEncodedSourceMapSpan.sourceColumn);
2014-07-13 01:04:16 +02:00
// 5. Relative namePosition 0 based
if (lastRecordedSourceMapSpan.nameIndex >= 0) {
sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.nameIndex - lastEncodedNameIndex);
lastEncodedNameIndex = lastRecordedSourceMapSpan.nameIndex;
}
lastEncodedSourceMapSpan = lastRecordedSourceMapSpan;
sourceMapData.sourceMapDecodedMappings.push(lastEncodedSourceMapSpan);
2014-07-13 01:04:16 +02:00
function base64VLQFormatEncode(inValue: number) {
function base64FormatEncode(inValue: number) {
if (inValue < 64) {
return 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.charAt(inValue);
}
throw TypeError(inValue + ": not a 64 based value");
}
// Add a new least significant bit that has the sign of the value.
// if negative number the least significant bit that gets added to the number has value 1
// else least significant bit value that gets added is 0
// eg. -1 changes to binary : 01 [1] => 3
// +1 changes to binary : 01 [0] => 2
if (inValue < 0) {
inValue = ((-inValue) << 1) + 1;
}
else {
inValue = inValue << 1;
}
// Encode 5 bits at a time starting from least significant bits
var encodedStr = "";
do {
var currentDigit = inValue & 31; // 11111
inValue = inValue >> 5;
if (inValue > 0) {
// There are still more digits to decode, set the msb (6th bit)
currentDigit = currentDigit | 32;
}
encodedStr = encodedStr + base64FormatEncode(currentDigit);
} while (inValue > 0);
return encodedStr;
2014-07-13 01:04:16 +02:00
}
}
function recordSourceMapSpan(pos: number) {
var sourceLinePos = currentSourceFile.getLineAndCharacterFromPosition(pos);
var emittedLine = writer.getLine();
var emittedColumn = writer.getColumn();
// If this location wasnt recorded or the location in source is going backwards, record the span
if (!lastRecordedSourceMapSpan ||
lastRecordedSourceMapSpan.emittedLine != emittedLine ||
lastRecordedSourceMapSpan.emittedColumn != emittedColumn ||
lastRecordedSourceMapSpan.sourceLine > sourceLinePos.line ||
(lastRecordedSourceMapSpan.sourceLine === sourceLinePos.line && lastRecordedSourceMapSpan.sourceColumn > sourceLinePos.character)) {
// Encode the last recordedSpan before assigning new
encodeLastRecordedSourceMapSpan();
// New span
lastRecordedSourceMapSpan = {
emittedLine: emittedLine,
emittedColumn: emittedColumn,
sourceLine: sourceLinePos.line,
sourceColumn: sourceLinePos.character,
nameIndex: getSourceMapNameIndex(),
sourceIndex: sourceMapSourceIndex
};
}
else {
// Take the new pos instead since there is no change in emittedLine and column since last location
lastRecordedSourceMapSpan.sourceLine = sourceLinePos.line;
lastRecordedSourceMapSpan.sourceColumn = sourceLinePos.character;
2014-07-13 01:04:16 +02:00
}
}
function recordEmitNodeStartSpan(node: Node) {
// Get the token pos after skipping to the token (ignoring the leading trivia)
recordSourceMapSpan(ts.getTokenPosOfNode(node));
}
function recordEmitNodeEndSpan(node: Node) {
recordSourceMapSpan(node.end);
}
2014-07-13 01:04:16 +02:00
function writeTextWithSpanRecord(tokenKind: SyntaxKind, startPos: number, emitFn?: () => void) {
var tokenStartPos = ts.skipTrivia(currentSourceFile.text, startPos);
recordSourceMapSpan(tokenStartPos);
var tokenEndPos = emitTokenText(tokenKind, tokenStartPos, emitFn);
recordSourceMapSpan(tokenEndPos);
return tokenEndPos;
}
2014-07-13 01:04:16 +02:00
function recordNewSourceFileStart(node: SourceFile) {
// Add the the file to tsFilePaths
// If sourceroot option: Use the relative path corresponding to the common directory path
// otherwise source locations relative to map file location
var sourcesDirectoryPath = compilerOptions.sourceRoot ? program.getCommonSourceDirectory() : sourceMapDir;
2014-07-13 01:04:16 +02:00
sourceMapData.sourceMapSources.push(getRelativePathToDirectoryOrUrl(sourcesDirectoryPath,
node.filename,
compilerHost.getCurrentDirectory(),
/*isAbsolutePathAnUrl*/ true));
sourceMapSourceIndex = sourceMapData.sourceMapSources.length - 1;
2014-07-13 01:04:16 +02:00
// The one that can be used from program to get the actual source file
sourceMapData.inputSourceFileNames.push(node.filename);
2014-07-13 01:04:16 +02:00
}
function recordScopeNameOfNode(node: Node, scopeName?: string) {
function recordScopeNameIndex(scopeNameIndex: number) {
sourceMapNameIndices.push(scopeNameIndex);
}
function recordScopeNameStart(scopeName: string) {
var scopeNameIndex = -1;
if (scopeName) {
var parentIndex = getSourceMapNameIndex();
if (parentIndex !== -1) {
scopeName = sourceMapData.sourceMapNames[parentIndex] + "." + scopeName;
}
2014-07-13 01:04:16 +02:00
scopeNameIndex = getProperty(sourceMapNameIndexMap, scopeName);
if (scopeNameIndex === undefined) {
scopeNameIndex = sourceMapData.sourceMapNames.length;
sourceMapData.sourceMapNames.push(scopeName);
sourceMapNameIndexMap[scopeName] = scopeNameIndex;
}
2014-07-13 01:04:16 +02:00
}
recordScopeNameIndex(scopeNameIndex);
2014-07-13 01:04:16 +02:00
}
if (scopeName) {
// The scope was already given a name use it
recordScopeNameStart(scopeName);
}
else if (node.kind === SyntaxKind.FunctionDeclaration ||
node.kind === SyntaxKind.FunctionExpression ||
node.kind === SyntaxKind.Method ||
node.kind === SyntaxKind.GetAccessor ||
node.kind === SyntaxKind.SetAccessor ||
node.kind === SyntaxKind.ModuleDeclaration ||
node.kind === SyntaxKind.ClassDeclaration ||
node.kind === SyntaxKind.EnumDeclaration) {
// Declaration and has associated name use it
if ((<Declaration>node).name) {
scopeName = (<Declaration>node).name.text;
}
recordScopeNameStart(scopeName);
2014-07-13 01:04:16 +02:00
}
else {
// Block just use the name from upper level scope
recordScopeNameIndex(getSourceMapNameIndex());
2014-07-13 01:04:16 +02:00
}
}
function recordScopeNameEnd() {
sourceMapNameIndices.pop();
};
2014-07-13 01:04:16 +02:00
function writeJavaScriptAndSourceMapFile(emitOutput: string) {
// Write source map file
2014-07-13 01:04:16 +02:00
encodeLastRecordedSourceMapSpan();
writeFile(sourceMapData.sourceMapFilePath, JSON.stringify({
version: 3,
file: sourceMapData.sourceMapFile,
sourceRoot: sourceMapData.sourceMapSourceRoot,
sources: sourceMapData.sourceMapSources,
names: sourceMapData.sourceMapNames,
mappings: sourceMapData.sourceMapMappings
}));
sourceMapDataList.push(sourceMapData);
// Write sourcemap url to the js file and write the js file
writeJavaScriptFile(emitOutput + "//# sourceMappingURL=" + sourceMapData.jsSourceMappingURL);
}
// Initialize source map data
var sourceMapJsFile = getBaseFilename(normalizeSlashes(jsFilePath));
sourceMapData = {
sourceMapFilePath: jsFilePath + ".map",
jsSourceMappingURL: sourceMapJsFile + ".map",
sourceMapFile: sourceMapJsFile,
sourceMapSourceRoot: compilerOptions.sourceRoot || "",
sourceMapSources: [],
inputSourceFileNames: [],
sourceMapNames: [],
sourceMapMappings: "",
sourceMapDecodedMappings: []
};
// Normalize source root and make sure it has trailing "/" so that it can be used to combine paths with the
// relative paths of the sources list in the sourcemap
sourceMapData.sourceMapSourceRoot = ts.normalizeSlashes(sourceMapData.sourceMapSourceRoot);
if (sourceMapData.sourceMapSourceRoot.length && sourceMapData.sourceMapSourceRoot.charCodeAt(sourceMapData.sourceMapSourceRoot.length - 1) !== CharacterCodes.slash) {
sourceMapData.sourceMapSourceRoot += directorySeparator;
}
if (compilerOptions.mapRoot) {
sourceMapDir = normalizeSlashes(compilerOptions.mapRoot);
if (root) { // emitting single module file
// For modules or multiple emit files the mapRoot will have directory structure like the sources
// So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map
sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(sourceMapDir, root));
}
2014-07-13 01:04:16 +02:00
if (!isRootedDiskPath(sourceMapDir) && !isUrl(sourceMapDir)) {
// The relative paths are relative to the common directory
sourceMapDir = combinePaths(program.getCommonSourceDirectory(), sourceMapDir);
sourceMapData.jsSourceMappingURL = getRelativePathToDirectoryOrUrl(
getDirectoryPath(normalizePath(jsFilePath)), // get the relative sourceMapDir path based on jsFilePath
combinePaths(sourceMapDir, sourceMapData.jsSourceMappingURL), // this is where user expects to see sourceMap
compilerHost.getCurrentDirectory(),
/*isAbsolutePathAnUrl*/ true);
}
else {
sourceMapData.jsSourceMappingURL = combinePaths(sourceMapDir, sourceMapData.jsSourceMappingURL);
}
2014-07-13 01:04:16 +02:00
}
else {
sourceMapDir = getDirectoryPath(normalizePath(jsFilePath));
2014-07-13 01:04:16 +02:00
}
function emitNodeWithMap(node: Node) {
if (node) {
if (node.kind != SyntaxKind.SourceFile) {
recordEmitNodeStartSpan(node);
emitNode(node);
recordEmitNodeEndSpan(node);
}
else {
recordNewSourceFileStart(<SourceFile>node);
emitNode(node);
}
}
}
2014-07-13 01:04:16 +02:00
writeEmittedFiles = writeJavaScriptAndSourceMapFile;
emit = emitNodeWithMap;
emitStart = recordEmitNodeStartSpan;
emitEnd = recordEmitNodeEndSpan;
emitToken = writeTextWithSpanRecord;
emitNewSourceFileStart = recordNewSourceFileStart;
scopeEmitStart = recordScopeNameOfNode;
scopeEmitEnd = recordScopeNameEnd;
2014-07-13 01:04:16 +02:00
}
function writeJavaScriptFile(emitOutput: string) {
writeFile(jsFilePath, emitOutput);
2014-07-13 01:04:16 +02:00
}
function emitTokenText(tokenKind: SyntaxKind, startPos: number, emitFn?: () => void) {
var tokenString = tokenToString(tokenKind);
if (emitFn) {
emitFn();
}
else {
write(tokenString);
}
return startPos + tokenString.length;
2014-07-13 01:04:16 +02:00
}
function emitOptional(prefix: string, node: Node) {
if (node) {
write(prefix);
emit(node);
2014-07-13 01:04:16 +02:00
}
}
2014-07-13 01:04:16 +02:00
function emitCommaList(nodes: Node[], count?: number) {
if (!(count >= 0)) count = nodes.length;
if (nodes) {
for (var i = 0; i < count; i++) {
if (i) write(", ");
emit(nodes[i]);
2014-07-13 01:04:16 +02:00
}
}
}
2014-07-13 01:04:16 +02:00
function emitMultiLineList(nodes: Node[]) {
if (nodes) {
for (var i = 0; i < nodes.length; i++) {
if (i) write(",");
writeLine();
emit(nodes[i]);
2014-07-13 01:04:16 +02:00
}
}
}
function emitLines(nodes: Node[]) {
emitLinesStartingAt(nodes, /*startIndex*/ 0);
}
function emitLinesStartingAt(nodes: Node[], startIndex: number): void {
for (var i = startIndex; i < nodes.length; i++) {
writeLine();
emit(nodes[i]);
2014-07-13 01:04:16 +02:00
}
}
2014-07-13 01:04:16 +02:00
function emitLiteral(node: LiteralExpression) {
var text = getSourceTextOfLocalNode(node);
if (node.kind === SyntaxKind.StringLiteral && compilerOptions.sourceMap) {
writer.writeLiteral(text);
2014-07-13 01:04:16 +02:00
}
else {
write(text);
2014-07-13 01:04:16 +02:00
}
}
// This function specifically handles numeric/string literals for enum and accessor 'identifiers'.
// In a sense, it does not actually emit identifiers as much as it declares a name for a specific property.
function emitQuotedIdentifier(node: Identifier) {
if (node.kind === SyntaxKind.StringLiteral) {
emitLiteral(node);
}
else {
write("\"");
if (node.kind === SyntaxKind.NumericLiteral) {
write(node.text);
2014-07-13 01:04:16 +02:00
}
else {
write(getSourceTextOfLocalNode(node));
}
write("\"");
}
}
function isNonExpressionIdentifier(node: Identifier) {
var parent = node.parent;
switch (parent.kind) {
case SyntaxKind.Parameter:
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Property:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.EnumMember:
case SyntaxKind.Method:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ImportDeclaration:
return (<Declaration>parent).name === node;
case SyntaxKind.BreakStatement:
case SyntaxKind.ContinueStatement:
case SyntaxKind.ExportAssignment:
return false;
case SyntaxKind.LabelledStatement:
return (<LabelledStatement>node.parent).label === node;
case SyntaxKind.CatchBlock:
return (<CatchBlock>node.parent).variable === node;
}
}
function emitIdentifier(node: Identifier) {
if (!isNonExpressionIdentifier(node)) {
var prefix = resolver.getExpressionNamePrefix(node);
if (prefix) {
write(prefix);
write(".");
2014-07-13 01:04:16 +02:00
}
}
write(getSourceTextOfLocalNode(node));
2014-07-13 01:04:16 +02:00
}
function emitThis(node: Node) {
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.LexicalThis) {
write("_this");
}
else {
write("this");
}
2014-07-13 01:04:16 +02:00
}
function emitSuper(node: Node) {
var flags = resolver.getNodeCheckFlags(node);
if (flags & NodeCheckFlags.SuperInstance) {
write("_super.prototype");
}
else if (flags & NodeCheckFlags.SuperStatic) {
write("_super");
}
else {
write("super");
}
2014-07-13 01:04:16 +02:00
}
function emitArrayLiteral(node: ArrayLiteral) {
if (node.flags & NodeFlags.MultiLine) {
write("[");
increaseIndent();
emitMultiLineList(node.elements);
decreaseIndent();
writeLine();
write("]");
}
else {
write("[");
emitCommaList(node.elements);
write("]");
2014-07-13 01:04:16 +02:00
}
}
function emitObjectLiteral(node: ObjectLiteral) {
if (!node.properties.length) {
write("{}");
}
else if (node.flags & NodeFlags.MultiLine) {
write("{");
increaseIndent();
emitMultiLineList(node.properties);
decreaseIndent();
2014-07-13 01:04:16 +02:00
writeLine();
write("}");
}
else {
write("{ ");
emitCommaList(node.properties);
write(" }");
2014-07-13 01:04:16 +02:00
}
}
function emitPropertyAssignment(node: PropertyDeclaration) {
emit(node.name);
write(": ");
emit(node.initializer);
2014-07-13 01:04:16 +02:00
}
function emitPropertyAccess(node: PropertyAccess) {
var text = resolver.getPropertyAccessSubstitution(node);
if (text) {
write(text);
return;
}
emit(node.left);
write(".");
emit(node.right);
2014-07-13 01:04:16 +02:00
}
function emitIndexedAccess(node: IndexedAccess) {
emit(node.object);
write("[");
emit(node.index);
write("]");
2014-07-13 01:04:16 +02:00
}
function emitCallExpression(node: CallExpression) {
var superCall = false;
if (node.func.kind === SyntaxKind.SuperKeyword) {
write("_super");
superCall = true;
2014-07-13 01:04:16 +02:00
}
else {
emit(node.func);
superCall = node.func.kind === SyntaxKind.PropertyAccess && (<PropertyAccess>node.func).left.kind === SyntaxKind.SuperKeyword;
}
if (superCall) {
write(".call(");
emitThis(node.func);
if (node.arguments.length) {
write(", ");
emitCommaList(node.arguments);
}
write(")");
}
else {
write("(");
emitCommaList(node.arguments);
write(")");
2014-07-13 01:04:16 +02:00
}
}
function emitNewExpression(node: NewExpression) {
write("new ");
emit(node.func);
if (node.arguments) {
write("(");
emitCommaList(node.arguments);
write(")");
}
2014-07-13 01:04:16 +02:00
}
function emitParenExpression(node: ParenExpression) {
if (node.expression.kind === SyntaxKind.TypeAssertion) {
var operand = (<TypeAssertion>node.expression).operand;
// Make sure we consider all nested cast expressions, e.g.:
// (<any><number><any>-A).x;
while (operand.kind == SyntaxKind.TypeAssertion) {
operand = (<TypeAssertion>operand).operand;
}
// We have an expression of the form: (<Type>SubExpr)
// Emitting this as (SubExpr) is really not desirable. We would like to emit the subexpr as is.
// Omitting the parentheses, however, could cause change in the semantics of the generated
// code if the casted expression has a lower precedence than the rest of the expression, e.g.:
// (<any>new A).foo should be emitted as (new A).foo and not new A.foo
// (<any>typeof A).toString() should be emitted as (typeof A).toString() and not typeof A.toString()
// new (<any>A()) should be emitted as new (A()) and not new A()
// (<any>function foo() { })() should be emitted as an IIF (function foo(){})() and not declaration function foo(){} ()
if (operand.kind !== SyntaxKind.PrefixOperator && operand.kind !== SyntaxKind.PostfixOperator && operand.kind !== SyntaxKind.NewExpression &&
!(operand.kind === SyntaxKind.CallExpression && node.parent.kind === SyntaxKind.NewExpression) &&
!(operand.kind === SyntaxKind.FunctionExpression && node.parent.kind === SyntaxKind.CallExpression)) {
emit(operand);
return;
}
2014-07-13 01:04:16 +02:00
}
write("(");
emit(node.expression);
write(")");
2014-07-13 01:04:16 +02:00
}
function emitUnaryExpression(node: UnaryExpression) {
if (node.kind === SyntaxKind.PrefixOperator) {
write(tokenToString(node.operator));
}
// In some cases, we need to emit a space between the operator and the operand. One obvious case
// is when the operator is an identifer, like delete or typeof. We also need to do this for plus
// and minus expressions in certain cases. Specifically, consider the following two cases (parens
// are just for clarity of exposition, and not part of the source code):
//
// (+(+1))
// (+(++1))
//
// We need to emit a space in both cases. In the first case, the absence of a space will make
// the resulting expression a prefix increment operation. And in the second, it will make the resulting
// expression a prefix increment whose operand is a plus expression - (++(+x))
// The same is true of minus of course.
if (node.operator >= SyntaxKind.Identifier) {
write(" ");
}
else if (node.kind === SyntaxKind.PrefixOperator && node.operand.kind === SyntaxKind.PrefixOperator) {
var operand = <UnaryExpression>node.operand;
if (node.operator === SyntaxKind.PlusToken && (operand.operator === SyntaxKind.PlusToken || operand.operator === SyntaxKind.PlusPlusToken)) {
write(" ");
}
else if (node.operator === SyntaxKind.MinusToken && (operand.operator === SyntaxKind.MinusToken || operand.operator === SyntaxKind.MinusMinusToken)) {
write(" ");
}
}
emit(node.operand);
if (node.kind === SyntaxKind.PostfixOperator) {
write(tokenToString(node.operator));
}
2014-07-13 01:04:16 +02:00
}
function emitBinaryExpression(node: BinaryExpression) {
emit(node.left);
if (node.operator !== SyntaxKind.CommaToken) write(" ");
write(tokenToString(node.operator));
write(" ");
emit(node.right);
2014-07-13 01:04:16 +02:00
}
function emitConditionalExpression(node: ConditionalExpression) {
emit(node.condition);
write(" ? ");
emit(node.whenTrue);
write(" : ");
emit(node.whenFalse);
2014-07-13 01:04:16 +02:00
}
function emitBlock(node: Block) {
emitToken(SyntaxKind.OpenBraceToken, node.pos);
2014-07-13 01:04:16 +02:00
increaseIndent();
scopeEmitStart(node.parent);
if (node.kind === SyntaxKind.ModuleBlock) {
Debug.assert(node.parent.kind === SyntaxKind.ModuleDeclaration);
emitCaptureThisForNodeIfNecessary(node.parent);
}
emitLines(node.statements);
2014-07-13 01:04:16 +02:00
decreaseIndent();
writeLine();
emitToken(SyntaxKind.CloseBraceToken, node.statements.end);
scopeEmitEnd();
2014-07-13 01:04:16 +02:00
}
function emitEmbeddedStatement(node: Node) {
if (node.kind === SyntaxKind.Block) {
write(" ");
emit(<Block>node);
}
else {
increaseIndent();
writeLine();
emit(node);
decreaseIndent();
}
2014-07-13 01:04:16 +02:00
}
function emitExpressionStatement(node: ExpressionStatement) {
var isArrowExpression = node.expression.kind === SyntaxKind.ArrowFunction;
if (isArrowExpression) write("(");
emit(node.expression);
if (isArrowExpression) write(")");
write(";");
2014-07-13 01:04:16 +02:00
}
function emitIfStatement(node: IfStatement) {
var endPos = emitToken(SyntaxKind.IfKeyword, node.pos);
write(" ");
endPos = emitToken(SyntaxKind.OpenParenToken, endPos);
emit(node.expression);
emitToken(SyntaxKind.CloseParenToken, node.expression.end);
emitEmbeddedStatement(node.thenStatement);
if (node.elseStatement) {
writeLine();
emitToken(SyntaxKind.ElseKeyword, node.thenStatement.end);
if (node.elseStatement.kind === SyntaxKind.IfStatement) {
write(" ");
emit(node.elseStatement);
}
else {
emitEmbeddedStatement(node.elseStatement);
}
2014-07-13 01:04:16 +02:00
}
}
function emitDoStatement(node: DoStatement) {
write("do");
emitEmbeddedStatement(node.statement);
if (node.statement.kind === SyntaxKind.Block) {
write(" ");
}
else {
writeLine();
}
write("while (");
emit(node.expression);
write(");");
2014-07-13 01:04:16 +02:00
}
function emitWhileStatement(node: WhileStatement) {
write("while (");
emit(node.expression);
2014-07-13 01:04:16 +02:00
write(")");
emitEmbeddedStatement(node.statement);
2014-07-13 01:04:16 +02:00
}
function emitForStatement(node: ForStatement) {
var endPos = emitToken(SyntaxKind.ForKeyword, node.pos);
write(" ");
endPos = emitToken(SyntaxKind.OpenParenToken, endPos);
if (node.declarations) {
emitToken(SyntaxKind.VarKeyword, endPos);
write(" ");
emitCommaList(node.declarations);
}
if (node.initializer) {
emit(node.initializer);
2014-07-13 01:04:16 +02:00
}
write(";");
emitOptional(" ", node.condition);
write(";");
emitOptional(" ", node.iterator);
write(")");
emitEmbeddedStatement(node.statement);
2014-07-13 01:04:16 +02:00
}
function emitForInStatement(node: ForInStatement) {
var endPos = emitToken(SyntaxKind.ForKeyword, node.pos);
2014-07-13 01:04:16 +02:00
write(" ");
endPos = emitToken(SyntaxKind.OpenParenToken, endPos);
if (node.declaration) {
emitToken(SyntaxKind.VarKeyword, endPos);
2014-07-13 01:04:16 +02:00
write(" ");
emit(node.declaration);
2014-07-13 01:04:16 +02:00
}
else {
emit(node.variable);
2014-07-13 01:04:16 +02:00
}
write(" in ");
emit(node.expression);
emitToken(SyntaxKind.CloseParenToken, node.expression.end);
emitEmbeddedStatement(node.statement);
2014-07-13 01:04:16 +02:00
}
function emitBreakOrContinueStatement(node: BreakOrContinueStatement) {
emitToken(node.kind === SyntaxKind.BreakStatement ? SyntaxKind.BreakKeyword : SyntaxKind.ContinueKeyword, node.pos);
emitOptional(" ", node.label);
write(";");
}
2014-07-13 01:04:16 +02:00
function emitReturnStatement(node: ReturnStatement) {
emitToken(SyntaxKind.ReturnKeyword, node.pos);
emitOptional(" ", node.expression);
write(";");
}
2014-07-13 01:04:16 +02:00
function emitWithStatement(node: WhileStatement) {
write("with (");
emit(node.expression);
write(")");
emitEmbeddedStatement(node.statement);
2014-07-13 01:04:16 +02:00
}
function emitSwitchStatement(node: SwitchStatement) {
var endPos = emitToken(SyntaxKind.SwitchKeyword, node.pos);
2014-07-13 01:04:16 +02:00
write(" ");
emitToken(SyntaxKind.OpenParenToken, endPos);
emit(node.expression);
endPos = emitToken(SyntaxKind.CloseParenToken, node.expression.end);
write(" ");
emitToken(SyntaxKind.OpenBraceToken, endPos);
2014-07-13 01:04:16 +02:00
increaseIndent();
emitLines(node.clauses);
2014-07-13 01:04:16 +02:00
decreaseIndent();
writeLine();
emitToken(SyntaxKind.CloseBraceToken, node.clauses.end);
2014-07-13 01:04:16 +02:00
}
function emitCaseOrDefaultClause(node: CaseOrDefaultClause) {
if (node.kind === SyntaxKind.CaseClause) {
write("case ");
emit(node.expression);
write(":");
2014-07-13 01:04:16 +02:00
}
else {
write("default:");
2014-07-13 01:04:16 +02:00
}
increaseIndent();
emitLines(node.statements);
decreaseIndent();
2014-07-13 01:04:16 +02:00
}
function emitThrowStatement(node: ThrowStatement) {
write("throw ");
emit(node.expression);
write(";");
2014-07-13 01:04:16 +02:00
}
function emitTryStatement(node: TryStatement) {
write("try ");
emit(node.tryBlock);
emit(node.catchBlock);
if (node.finallyBlock) {
writeLine();
write("finally ");
emit(node.finallyBlock);
}
2014-07-13 01:04:16 +02:00
}
function emitCatchBlock(node: CatchBlock) {
writeLine();
var endPos = emitToken(SyntaxKind.CatchKeyword, node.pos);
2014-07-13 01:04:16 +02:00
write(" ");
emitToken(SyntaxKind.OpenParenToken, endPos);
2014-07-13 01:04:16 +02:00
emit(node.variable);
emitToken(SyntaxKind.CloseParenToken, node.variable.end);
write(" ");
emitBlock(node);
2014-07-13 01:04:16 +02:00
}
function emitDebuggerStatement(node: Node) {
emitToken(SyntaxKind.DebuggerKeyword, node.pos);
write(";");
2014-07-13 01:04:16 +02:00
}
function emitLabelledStatement(node: LabelledStatement) {
emit(node.label);
write(": ");
emit(node.statement);
2014-07-13 01:04:16 +02:00
}
function getContainingModule(node: Node): ModuleDeclaration {
do {
node = node.parent;
} while (node && node.kind !== SyntaxKind.ModuleDeclaration);
return <ModuleDeclaration>node;
2014-07-13 01:04:16 +02:00
}
function emitModuleMemberName(node: Declaration) {
emitStart(node.name);
if (node.flags & NodeFlags.Export) {
var container = getContainingModule(node);
write(container ? resolver.getModuleObjectName(container) : "exports");
write(".");
2014-07-13 01:04:16 +02:00
}
emitNode(node.name);
emitEnd(node.name);
}
2014-07-13 01:04:16 +02:00
function emitVariableDeclaration(node: VariableDeclaration) {
emitModuleMemberName(node);
emitOptional(" = ", node.initializer);
2014-07-13 01:04:16 +02:00
}
function emitVariableStatement(node: VariableStatement) {
if (!(node.flags & NodeFlags.Export)) write("var ");
emitCommaList(node.declarations);
write(";");
}
2014-07-13 01:04:16 +02:00
function emitParameter(node: ParameterDeclaration) {
2014-07-13 01:04:16 +02:00
emit(node.name);
}
function emitDefaultValueAssignments(node: FunctionDeclaration) {
forEach(node.parameters, param => {
if (param.initializer) {
writeLine();
emitStart(param);
write("if (");
emitNode(param.name);
write(" === void 0)");
emitEnd(param);
write(" { ");
emitStart(param);
emitNode(param.name);
write(" = ");
emitNode(param.initializer);
emitEnd(param);
write("; }");
}
});
2014-07-13 01:04:16 +02:00
}
function emitRestParameter(node: FunctionDeclaration) {
if (hasRestParameters(node)) {
var restIndex = node.parameters.length - 1;
var restParam = node.parameters[restIndex];
writeLine();
emitStart(restParam);
write("var ");
emitNode(restParam.name);
write(" = [];");
emitEnd(restParam);
writeLine();
write("for (");
emitStart(restParam);
write("var _i = " + restIndex + ";");
emitEnd(restParam);
write(" ");
emitStart(restParam);
write("_i < arguments.length;");
emitEnd(restParam);
write(" ");
emitStart(restParam);
write("_i++");
emitEnd(restParam);
write(") {");
increaseIndent();
writeLine();
emitStart(restParam);
emitNode(restParam.name);
write("[_i - " + restIndex + "] = arguments[_i];");
emitEnd(restParam);
decreaseIndent();
2014-07-13 01:04:16 +02:00
writeLine();
write("}");
}
}
function emitAccessor(node: AccessorDeclaration) {
write(node.kind === SyntaxKind.GetAccessor ? "get " : "set ");
2014-07-13 01:04:16 +02:00
emit(node.name);
emitSignatureAndBody(node);
2014-07-13 01:04:16 +02:00
}
function emitFunctionDeclaration(node: FunctionDeclaration) {
if (!node.body) return;
write("function ");
if (node.kind === SyntaxKind.FunctionDeclaration || (node.kind === SyntaxKind.FunctionExpression && node.name)) {
emit(node.name);
2014-07-13 01:04:16 +02:00
}
emitSignatureAndBody(node);
2014-07-13 01:04:16 +02:00
}
function emitCaptureThisForNodeIfNecessary(node: Node): void {
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureThis) {
2014-07-13 01:04:16 +02:00
writeLine();
emitStart(node);
write("var _this = this;");
emitEnd(node);
2014-07-13 01:04:16 +02:00
}
}
2014-07-18 18:56:10 +02:00
function emitSignatureParameters(node: FunctionDeclaration) {
write("(");
if (node) {
emitCommaList(node.parameters, node.parameters.length - (hasRestParameters(node) ? 1 : 0));
}
write(")");
}
function emitSignatureAndBody(node: FunctionDeclaration) {
2014-07-18 18:56:10 +02:00
emitSignatureParameters(node);
write(" {");
scopeEmitStart(node);
increaseIndent();
var outPos = writer.getTextPos();
emitCaptureThisForNodeIfNecessary(node);
emitDefaultValueAssignments(node);
emitRestParameter(node);
if (node.body.kind !== SyntaxKind.FunctionBlock && outPos === writer.getTextPos()) {
decreaseIndent();
write(" ");
emitStart(node.body);
write("return ");
emitNode(node.body);
emitEnd(node.body);
write("; ");
emitStart(node.body);
write("}");
emitEnd(node.body);
}
else {
if (node.body.kind === SyntaxKind.FunctionBlock) {
emitLines((<Block>node.body).statements);
}
else {
writeLine();
write("return ");
emit(node.body);
write(";");
}
decreaseIndent();
2014-07-13 01:04:16 +02:00
writeLine();
if (node.body.kind === SyntaxKind.FunctionBlock) {
emitToken(SyntaxKind.CloseBraceToken, (<Block>node.body).statements.end);
}
else {
emitStart(node.body);
write("}");
emitEnd(node.body);
2014-07-13 01:04:16 +02:00
}
}
scopeEmitEnd();
if (node.flags & NodeFlags.Export) {
writeLine();
emitStart(node);
emitModuleMemberName(node);
2014-07-13 01:04:16 +02:00
write(" = ");
emit(node.name);
emitEnd(node);
2014-07-13 01:04:16 +02:00
write(";");
}
}
function findInitialSuperCall(ctor: ConstructorDeclaration): ExpressionStatement {
if (ctor.body) {
var statement = (<Block>ctor.body).statements[0];
if (statement && statement.kind === SyntaxKind.ExpressionStatement) {
var expr = (<ExpressionStatement>statement).expression;
if (expr && expr.kind === SyntaxKind.CallExpression) {
var func = (<CallExpression>expr).func;
if (func && func.kind === SyntaxKind.SuperKeyword) {
return <ExpressionStatement>statement;
}
}
}
}
}
function emitParameterPropertyAssignments(node: ConstructorDeclaration) {
forEach(node.parameters, param => {
if (param.flags & (NodeFlags.Public | NodeFlags.Private)) {
writeLine();
emitStart(param);
emitStart(param.name);
write("this.");
emitNode(param.name);
emitEnd(param.name);
write(" = ");
emit(param.name);
write(";");
emitEnd(param);
}
});
}
function emitMemberAccess(memberName: Identifier) {
if (memberName.kind === SyntaxKind.StringLiteral || memberName.kind === SyntaxKind.NumericLiteral) {
write("[");
emitNode(memberName);
write("]");
}
else {
write(".");
emitNode(memberName);
}
}
function emitMemberAssignments(node: ClassDeclaration, staticFlag: NodeFlags) {
forEach(node.members, member => {
if (member.kind === SyntaxKind.Property && (member.flags & NodeFlags.Static) === staticFlag && (<PropertyDeclaration>member).initializer) {
writeLine();
emitStart(member);
emitStart((<PropertyDeclaration>member).name);
if (staticFlag) {
emitNode(node.name);
}
else {
write("this");
}
emitMemberAccess((<PropertyDeclaration>member).name);
emitEnd((<PropertyDeclaration>member).name);
write(" = ");
emit((<PropertyDeclaration>member).initializer);
write(";");
emitEnd(member);
}
});
}
function emitMemberFunctions(node: ClassDeclaration) {
forEach(node.members, member => {
if (member.kind === SyntaxKind.Method) {
if (!(<MethodDeclaration>member).body) return;
2014-07-13 01:04:16 +02:00
writeLine();
emitStart(member);
emitStart((<MethodDeclaration>member).name);
2014-07-13 01:04:16 +02:00
emitNode(node.name);
if (!(member.flags & NodeFlags.Static)) {
write(".prototype");
}
emitMemberAccess((<MethodDeclaration>member).name);
emitEnd((<MethodDeclaration>member).name);
write(" = ");
emitStart(member);
emitFunctionDeclaration(<MethodDeclaration>member);
emitEnd(member);
emitEnd(member);
write(";");
}
else if (member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor) {
var accessors = getAllAccessorDeclarations(node, <AccessorDeclaration>member);
if (member === accessors.firstAccessor) {
2014-07-13 01:04:16 +02:00
writeLine();
emitStart(member);
write("Object.defineProperty(");
emitStart((<AccessorDeclaration>member).name);
emitNode(node.name);
if (!(member.flags & NodeFlags.Static)) {
write(".prototype");
}
write(", ");
emitQuotedIdentifier((<AccessorDeclaration>member).name);
emitEnd((<AccessorDeclaration>member).name);
write(", {");
increaseIndent();
if (accessors.getAccessor) {
writeLine();
write("get: ");
emitStart(accessors.getAccessor);
write("function ");
emitSignatureAndBody(accessors.getAccessor);
emitEnd(accessors.getAccessor);
write(",");
}
if (accessors.setAccessor) {
writeLine();
write("set: ");
emitStart(accessors.setAccessor);
write("function ");
emitSignatureAndBody(accessors.setAccessor);
emitEnd(accessors.setAccessor);
write(",");
}
writeLine();
write("enumerable: true,");
2014-07-13 01:04:16 +02:00
writeLine();
write("configurable: true");
decreaseIndent();
writeLine();
write("});");
emitEnd(member);
2014-07-13 01:04:16 +02:00
}
}
});
}
2014-07-13 01:04:16 +02:00
function emitClassDeclaration(node: ClassDeclaration) {
var ctor = getFirstConstructorWithBody(node);
write("var ");
2014-07-13 01:04:16 +02:00
emit(node.name);
write(" = (function (");
if (node.baseType) write("_super");
write(") {");
increaseIndent();
scopeEmitStart(node);
2014-07-13 01:04:16 +02:00
if (node.baseType) {
writeLine();
emitStart(node.baseType);
write("__extends(");
emit(node.name);
write(", _super);");
2014-07-13 01:04:16 +02:00
emitEnd(node.baseType);
}
writeLine();
emitStart(<Node>ctor || node);
write("function ");
2014-07-13 01:04:16 +02:00
emit(node.name);
2014-07-18 18:56:10 +02:00
emitSignatureParameters(ctor);
write(" {");
scopeEmitStart(node, "constructor");
increaseIndent();
if (ctor) {
emitDefaultValueAssignments(ctor);
emitRestParameter(ctor);
if (node.baseType) {
var superCall = findInitialSuperCall(ctor);
if (superCall) {
writeLine();
emit(superCall);
}
}
emitParameterPropertyAssignments(ctor);
2014-07-13 01:04:16 +02:00
}
else {
if (node.baseType) {
writeLine();
emitStart(node.baseType);
write("_super.apply(this, arguments);");
emitEnd(node.baseType);
}
2014-07-13 01:04:16 +02:00
}
emitCaptureThisForNodeIfNecessary(node);
emitMemberAssignments(node, /*nonstatic*/0);
if (ctor) {
var statements: Node[] = (<Block>ctor.body).statements;
if (superCall) statements = statements.slice(1);
emitLines(statements);
}
decreaseIndent();
writeLine();
emitToken(SyntaxKind.CloseBraceToken, ctor ? (<Block>ctor.body).statements.end : node.members.end);
scopeEmitEnd();
emitEnd(<Node>ctor || node);
emitMemberFunctions(node);
emitMemberAssignments(node, NodeFlags.Static);
writeLine();
function emitClassReturnStatement() {
write("return ");
emitNode(node.name);
}
emitToken(SyntaxKind.CloseBraceToken, node.members.end, emitClassReturnStatement);
2014-07-13 01:04:16 +02:00
write(";");
decreaseIndent();
2014-07-13 01:04:16 +02:00
writeLine();
emitToken(SyntaxKind.CloseBraceToken, node.members.end);
scopeEmitEnd();
2014-07-13 01:04:16 +02:00
emitStart(node);
write(")(");
if (node.baseType) {
emit(node.baseType.typeName);
}
write(");");
2014-07-13 01:04:16 +02:00
emitEnd(node);
if (node.flags & NodeFlags.Export) {
writeLine();
emitStart(node);
emitModuleMemberName(node);
write(" = ");
emit(node.name);
emitEnd(node);
write(";");
}
2014-07-13 01:04:16 +02:00
}
function emitEnumDeclaration(node: EnumDeclaration) {
if (!(node.flags & NodeFlags.Export)) {
emitStart(node);
write("var ");
emit(node.name);
emitEnd(node);
write(";");
}
writeLine();
2014-07-13 01:04:16 +02:00
emitStart(node);
write("(function (");
2014-07-13 01:04:16 +02:00
emit(node.name);
write(") {");
2014-07-13 01:04:16 +02:00
increaseIndent();
scopeEmitStart(node);
forEach(node.members, member => {
writeLine();
emitStart(member);
emitNode(node.name);
write("[");
emitNode(node.name);
write("[");
emitQuotedIdentifier(member.name);
write("] = ");
if (member.initializer) {
emit(member.initializer);
}
else {
write(resolver.getEnumMemberValue(member).toString());
}
write("] = ");
emitQuotedIdentifier(member.name);
emitEnd(member);
write(";");
});
2014-07-13 01:04:16 +02:00
decreaseIndent();
writeLine();
emitToken(SyntaxKind.CloseBraceToken, node.members.end);
2014-07-13 01:04:16 +02:00
scopeEmitEnd();
write(")(");
emitModuleMemberName(node);
write(" || (");
2014-07-13 01:04:16 +02:00
emitModuleMemberName(node);
write(" = {}));");
2014-07-13 01:04:16 +02:00
emitEnd(node);
if (node.flags & NodeFlags.Export) {
writeLine();
emitStart(node);
write("var ");
emit(node.name);
write(" = ");
emitModuleMemberName(node);
emitEnd(node);
write(";");
}
2014-07-13 01:04:16 +02:00
}
function getInnerMostModuleDeclarationFromDottedModule(moduleDeclaration: ModuleDeclaration): ModuleDeclaration {
if (moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
var recursiveInnerModule = getInnerMostModuleDeclarationFromDottedModule(<ModuleDeclaration>moduleDeclaration.body);
return recursiveInnerModule || <ModuleDeclaration>moduleDeclaration.body;
}
2014-07-13 01:04:16 +02:00
}
function emitModuleDeclaration(node: ModuleDeclaration) {
if (!isInstantiated(node)) return;
if (!(node.flags & NodeFlags.Export)) {
emitStart(node);
write("var ");
emit(node.name);
write(";");
emitEnd(node);
writeLine();
}
emitStart(node);
write("(function (");
emit(node.name);
write(") ");
if (node.body.kind === SyntaxKind.ModuleBlock) {
emit(node.body);
2014-07-13 01:04:16 +02:00
}
else {
write("{");
increaseIndent();
scopeEmitStart(node);
emitCaptureThisForNodeIfNecessary(node);
writeLine();
emit(node.body);
decreaseIndent();
writeLine();
var moduleBlock = <Block>getInnerMostModuleDeclarationFromDottedModule(node).body;
emitToken(SyntaxKind.CloseBraceToken, moduleBlock.statements.end);
scopeEmitEnd();
}
write(")(");
emitModuleMemberName(node);
write(" || (");
emitModuleMemberName(node);
write(" = {}));");
emitEnd(node);
if (node.flags & NodeFlags.Export) {
2014-07-13 01:04:16 +02:00
writeLine();
emitStart(node);
write("var ");
emit(node.name);
2014-07-13 01:04:16 +02:00
write(" = ");
emitModuleMemberName(node);
2014-07-13 01:04:16 +02:00
emitEnd(node);
write(";");
2014-07-13 01:04:16 +02:00
}
}
function emitImportDeclaration(node: ImportDeclaration) {
var emitImportDeclaration = resolver.isReferencedImportDeclaration(node);
if (!emitImportDeclaration) {
// preserve old compiler's behavior: emit 'var' for import declaration (even if we do not consider them referenced) when
// - current file is not external module
// - import declaration is top level and target is value imported by entity name
emitImportDeclaration = !(currentSourceFile.flags & NodeFlags.ExternalModule) && resolver.isTopLevelValueImportedViaEntityName(node);
2014-07-13 01:04:16 +02:00
}
if (emitImportDeclaration) {
if (node.externalModuleName && node.parent.kind === SyntaxKind.SourceFile && compilerOptions.module === ModuleKind.AMD) {
if (node.flags & NodeFlags.Export) {
writeLine();
emitStart(node);
emitModuleMemberName(node);
write(" = ");
emit(node.name);
write(";");
emitEnd(node);
}
}
else {
writeLine();
emitStart(node);
if (!(node.flags & NodeFlags.Export)) write("var ");
emitModuleMemberName(node);
write(" = ");
if (node.entityName) {
emit(node.entityName);
}
else {
write("require(");
emitStart(node.externalModuleName);
emitLiteral(node.externalModuleName);
emitEnd(node.externalModuleName);
emitToken(SyntaxKind.CloseParenToken, node.externalModuleName.end);
}
write(";");
emitEnd(node);
}
2014-07-13 01:04:16 +02:00
}
}
2014-07-13 01:04:16 +02:00
function getExternalImportDeclarations(node: SourceFile): ImportDeclaration[] {
var result: ImportDeclaration[] = [];
forEach(node.statements, stat => {
if (stat.kind === SyntaxKind.ImportDeclaration && (<ImportDeclaration>stat).externalModuleName && resolver.isReferencedImportDeclaration(stat)) {
result.push(<ImportDeclaration>stat);
}
});
return result;
2014-07-13 01:04:16 +02:00
}
function getFirstExportAssignment(sourceFile: SourceFile) {
return forEach(sourceFile.statements, node => {
if (node.kind === SyntaxKind.ExportAssignment) {
return <ExportAssignment>node;
}
});
2014-07-13 01:04:16 +02:00
}
function emitAMDModule(node: SourceFile, startIndex: number) {
var imports = getExternalImportDeclarations(node);
2014-07-13 01:04:16 +02:00
writeLine();
write("define([\"require\", \"exports\"");
forEach(imports, imp => {
write(", ");
emitLiteral(imp.externalModuleName);
});
forEach(node.amdDependencies, amdDependency => {
var text = "\"" + amdDependency + "\"";
write(", ");
write(text);
});
write("], function (require, exports");
forEach(imports, imp => {
write(", ");
emit(imp.name);
});
write(") {");
2014-07-13 01:04:16 +02:00
increaseIndent();
emitCaptureThisForNodeIfNecessary(node);
emitLinesStartingAt(node.statements, startIndex);
var exportName = resolver.getExportAssignmentName(node);
if (exportName) {
writeLine();
var exportAssignement = getFirstExportAssignment(node);
emitStart(exportAssignement);
write("return ");
emitStart(exportAssignement.exportName);
write(exportName);
emitEnd(exportAssignement.exportName);
write(";");
emitEnd(exportAssignement);
}
2014-07-13 01:04:16 +02:00
decreaseIndent();
writeLine();
write("});");
2014-07-13 01:04:16 +02:00
}
function emitCommonJSModule(node: SourceFile, startIndex: number) {
emitCaptureThisForNodeIfNecessary(node);
emitLinesStartingAt(node.statements, startIndex);
var exportName = resolver.getExportAssignmentName(node);
if (exportName) {
writeLine();
var exportAssignement = getFirstExportAssignment(node);
emitStart(exportAssignement);
write("module.exports = ");
emitStart(exportAssignement.exportName);
write(exportName);
emitEnd(exportAssignement.exportName);
write(";");
emitEnd(exportAssignement);
}
}
function isDirectivePrologue(n: Statement): boolean {
return n.kind === SyntaxKind.ExpressionStatement && (<ExpressionStatement>n).expression.kind === SyntaxKind.StringLiteral;
}
function emitSourceFile(node: SourceFile) {
currentSourceFile = node;
var startIndex = 0;
for (; startIndex < node.statements.length; ++startIndex) {
// emit prologue directives prior to __extends
if (isDirectivePrologue(node.statements[startIndex])) {
if (startIndex > 0) {
writeLine();
}
emit(node.statements[startIndex]);
}
else {
break;
}
}
if (!extendsEmitted && resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitExtends) {
writeLine();
write("var __extends = this.__extends || function (d, b) {");
increaseIndent();
writeLine();
write("for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];");
writeLine();
write("function __() { this.constructor = d; }");
writeLine();
write("__.prototype = b.prototype;");
writeLine();
write("d.prototype = new __();");
decreaseIndent();
writeLine();
write("};");
extendsEmitted = true;
}
if (node.flags & NodeFlags.ExternalModule) {
if (compilerOptions.module === ModuleKind.AMD) {
emitAMDModule(node, startIndex);
}
else {
emitCommonJSModule(node, startIndex);
}
2014-07-13 01:04:16 +02:00
}
else {
emitCaptureThisForNodeIfNecessary(node);
emitLinesStartingAt(node.statements, startIndex);
}
}
function emitNode(node: Node) {
if (!node || node.flags & NodeFlags.Ambient) return;
switch (node.kind) {
case SyntaxKind.Identifier:
return emitIdentifier(<Identifier>node);
case SyntaxKind.Parameter:
return emitParameter(<ParameterDeclaration>node);
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return emitAccessor(<AccessorDeclaration>node);
case SyntaxKind.ThisKeyword:
return emitThis(node);
case SyntaxKind.SuperKeyword:
return emitSuper(node);
case SyntaxKind.NullKeyword:
return write("null");
case SyntaxKind.TrueKeyword:
return write("true");
case SyntaxKind.FalseKeyword:
return write("false");
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
case SyntaxKind.RegularExpressionLiteral:
return emitLiteral(<LiteralExpression>node);
case SyntaxKind.QualifiedName:
return emitPropertyAccess(<QualifiedName>node);
case SyntaxKind.ArrayLiteral:
return emitArrayLiteral(<ArrayLiteral>node);
case SyntaxKind.ObjectLiteral:
return emitObjectLiteral(<ObjectLiteral>node);
case SyntaxKind.PropertyAssignment:
return emitPropertyAssignment(<PropertyDeclaration>node);
case SyntaxKind.PropertyAccess:
return emitPropertyAccess(<PropertyAccess>node);
case SyntaxKind.IndexedAccess:
return emitIndexedAccess(<IndexedAccess>node);
case SyntaxKind.CallExpression:
return emitCallExpression(<CallExpression>node);
case SyntaxKind.NewExpression:
return emitNewExpression(<NewExpression>node);
case SyntaxKind.TypeAssertion:
return emit((<TypeAssertion>node).operand);
case SyntaxKind.ParenExpression:
return emitParenExpression(<ParenExpression>node);
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return emitFunctionDeclaration(<FunctionDeclaration>node);
case SyntaxKind.PrefixOperator:
case SyntaxKind.PostfixOperator:
return emitUnaryExpression(<UnaryExpression>node);
case SyntaxKind.BinaryExpression:
return emitBinaryExpression(<BinaryExpression>node);
case SyntaxKind.ConditionalExpression:
return emitConditionalExpression(<ConditionalExpression>node);
case SyntaxKind.OmittedExpression:
return;
case SyntaxKind.Block:
case SyntaxKind.TryBlock:
case SyntaxKind.FinallyBlock:
case SyntaxKind.FunctionBlock:
case SyntaxKind.ModuleBlock:
return emitBlock(<Block>node);
case SyntaxKind.VariableStatement:
return emitVariableStatement(<VariableStatement>node);
case SyntaxKind.EmptyStatement:
return write(";");
case SyntaxKind.ExpressionStatement:
return emitExpressionStatement(<ExpressionStatement>node);
case SyntaxKind.IfStatement:
return emitIfStatement(<IfStatement>node);
case SyntaxKind.DoStatement:
return emitDoStatement(<DoStatement>node);
case SyntaxKind.WhileStatement:
return emitWhileStatement(<WhileStatement>node);
case SyntaxKind.ForStatement:
return emitForStatement(<ForStatement>node);
case SyntaxKind.ForInStatement:
return emitForInStatement(<ForInStatement>node);
case SyntaxKind.ContinueStatement:
case SyntaxKind.BreakStatement:
return emitBreakOrContinueStatement(<BreakOrContinueStatement>node);
case SyntaxKind.ReturnStatement:
return emitReturnStatement(<ReturnStatement>node);
case SyntaxKind.WithStatement:
return emitWithStatement(<WithStatement>node);
case SyntaxKind.SwitchStatement:
return emitSwitchStatement(<SwitchStatement>node);
case SyntaxKind.CaseClause:
case SyntaxKind.DefaultClause:
return emitCaseOrDefaultClause(<CaseOrDefaultClause>node);
case SyntaxKind.LabelledStatement:
return emitLabelledStatement(<LabelledStatement>node);
case SyntaxKind.ThrowStatement:
return emitThrowStatement(<ThrowStatement>node);
case SyntaxKind.TryStatement:
return emitTryStatement(<TryStatement>node);
case SyntaxKind.CatchBlock:
return emitCatchBlock(<CatchBlock>node);
case SyntaxKind.DebuggerStatement:
return emitDebuggerStatement(node);
case SyntaxKind.VariableDeclaration:
return emitVariableDeclaration(<VariableDeclaration>node);
case SyntaxKind.ClassDeclaration:
return emitClassDeclaration(<ClassDeclaration>node);
case SyntaxKind.EnumDeclaration:
return emitEnumDeclaration(<EnumDeclaration>node);
case SyntaxKind.ModuleDeclaration:
return emitModuleDeclaration(<ModuleDeclaration>node);
case SyntaxKind.ImportDeclaration:
return emitImportDeclaration(<ImportDeclaration>node);
case SyntaxKind.SourceFile:
return emitSourceFile(<SourceFile>node);
}
}
if (compilerOptions.sourceMap) {
initializeEmitterWithSourceMaps();
}
if (root) {
emit(root);
2014-07-13 01:04:16 +02:00
}
else {
forEach(program.getSourceFiles(), sourceFile => {
if (!isExternalModuleOrDeclarationFile(sourceFile)) {
emit(sourceFile);
}
});
2014-07-13 01:04:16 +02:00
}
writeLine();
writeEmittedFiles(writer.getText());
2014-07-13 01:04:16 +02:00
}
function emitDeclarations(jsFilePath: string, root?: SourceFile) {
var writer = createTextWriter(writeSymbol);
var write = writer.write;
var writeLine = writer.writeLine;
var increaseIndent = writer.increaseIndent;
var decreaseIndent = writer.decreaseIndent;
2014-07-13 01:04:16 +02:00
var enclosingDeclaration: Node;
var reportedDeclarationError = false;
var getSymbolVisibilityDiagnosticMessage: (symbolAccesibilityResult: SymbolAccessiblityResult) => {
errorNode: Node;
diagnosticMessage: DiagnosticMessage;
typeName: Identifier
}
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
if (!getSymbolVisibilityDiagnosticMessage || symbolAccesibilityResult.accessibility === SymbolAccessibility.Accessible) {
resolver.writeSymbol(symbol, enclosingDeclaration, meaning, writer);
}
else {
// Report error
reportedDeclarationError = true;
var errorInfo = getSymbolVisibilityDiagnosticMessage(symbolAccesibilityResult);
diagnostics.push(createDiagnosticForNode(errorInfo.errorNode,
errorInfo.diagnosticMessage,
getSourceTextOfLocalNode(errorInfo.typeName),
symbolAccesibilityResult.errorSymbolName,
symbolAccesibilityResult.errorModuleName));
}
}
function emitLines(nodes: Node[]) {
for (var i = 0, n = nodes.length; i < n; i++) {
emitNode(nodes[i]);
}
2014-07-13 01:04:16 +02:00
}
function emitCommaList(nodes: Node[], eachNodeEmitFn: (node: Node) => void) {
var currentWriterPos = writer.getTextPos();
for (var i = 0, n = nodes.length; i < n; i++) {
if (currentWriterPos !== writer.getTextPos()) {
write(", ");
}
currentWriterPos = writer.getTextPos();
eachNodeEmitFn(nodes[i]);
2014-07-13 01:04:16 +02:00
}
}
function emitSourceTextOfNode(node: Node) {
write(getSourceTextOfLocalNode(node));
}
2014-07-13 01:04:16 +02:00
function emitSourceFile(node: SourceFile) {
currentSourceFile = node;
enclosingDeclaration = node;
emitLines(node.statements);
}
2014-07-13 01:04:16 +02:00
function emitExportAssignment(node: ExportAssignment) {
write("export = ");
emitSourceTextOfNode(node.exportName);
write(";");
writeLine();
}
2014-07-13 01:04:16 +02:00
function emitDeclarationFlags(node: Declaration) {
if (node.flags & NodeFlags.Static) {
if (node.flags & NodeFlags.Private) {
write("private ");
}
write("static ");
2014-07-13 01:04:16 +02:00
}
else {
if (node.flags & NodeFlags.Private) {
write("private ");
}
// If the node is parented in the current source file we need to emit export declare or just export
else if (node.parent === currentSourceFile) {
// If the node is exported
if (node.flags & NodeFlags.Export) {
write("export ");
}
if (node.kind !== SyntaxKind.InterfaceDeclaration) {
write("declare ");
}
}
2014-07-13 01:04:16 +02:00
}
}
function emitImportDeclaration(node: ImportDeclaration) {
if (resolver.isDeclarationVisible(node)) {
2014-07-13 01:04:16 +02:00
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(");");
2014-07-13 01:04:16 +02:00
}
writeLine();
2014-07-13 01:04:16 +02:00
}
}
function emitModuleDeclaration(node: ModuleDeclaration) {
if (resolver.isDeclarationVisible(node)) {
emitDeclarationFlags(node);
write("module ");
emitSourceTextOfNode(node.name);
while (node.body.kind !== SyntaxKind.ModuleBlock) {
node = <ModuleDeclaration>node.body;
write(".");
emitSourceTextOfNode(node.name);
}
var prevEnclosingDeclaration = enclosingDeclaration;
enclosingDeclaration = node;
write(" {");
writeLine();
increaseIndent();
emitLines((<Block>node.body).statements);
decreaseIndent();
write("}");
writeLine();
enclosingDeclaration = prevEnclosingDeclaration;
}
}
function emitEnumDeclaration(node: EnumDeclaration) {
if (resolver.isDeclarationVisible(node)) {
emitDeclarationFlags(node);
write("enum ");
2014-07-13 01:04:16 +02:00
emitSourceTextOfNode(node.name);
write(" {");
writeLine();
increaseIndent();
emitLines(node.members);
decreaseIndent();
write("}");
writeLine();
2014-07-13 01:04:16 +02:00
}
}
function emitEnumMemberDeclaration(node: EnumMember) {
2014-07-13 01:04:16 +02:00
emitSourceTextOfNode(node.name);
var enumMemberValue = resolver.getEnumMemberValue(node);
if (enumMemberValue !== undefined) {
write(" = ");
write(enumMemberValue.toString());
}
write(",");
2014-07-13 01:04:16 +02:00
writeLine();
}
function emitTypeParameters(typeParameters: TypeParameterDeclaration[]) {
function emitTypeParameter(node: TypeParameterDeclaration) {
function getTypeParameterConstraintVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult) {
// TODO(shkamat) Cannot access name errors
var diagnosticMessage: DiagnosticMessage;
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
Diagnostics.TypeParameter_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.TypeParameter_0_of_exported_class_has_or_is_using_private_name_1;
break;
case SyntaxKind.InterfaceDeclaration:
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
Diagnostics.TypeParameter_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.TypeParameter_0_of_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.ConstructSignature:
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
Diagnostics.TypeParameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.TypeParameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.CallSignature:
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
Diagnostics.TypeParameter_0_of_call_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.TypeParameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1;
break;
case SyntaxKind.Method:
if (node.parent.flags & NodeFlags.Static) {
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
Diagnostics.TypeParameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.TypeParameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1;
}
else if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
Diagnostics.TypeParameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.TypeParameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1;
}
else {
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
Diagnostics.TypeParameter_0_of_method_from_exported_interface_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.TypeParameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1;
}
break;
case SyntaxKind.FunctionDeclaration:
diagnosticMessage = symbolAccesibilityResult.errorModuleName ?
Diagnostics.TypeParameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.TypeParameter_0_of_exported_function_has_or_is_using_private_name_1;
break;
default:
Debug.fail("This is unknown parent for type parameter: " + SyntaxKind[node.parent.kind]);
}
return {
diagnosticMessage: diagnosticMessage,
errorNode: node,
typeName: node.name
}
}
emitSourceTextOfNode(node.name);
if (node.constraint) {
write(" extends ");
getSymbolVisibilityDiagnosticMessage = getTypeParameterConstraintVisibilityError;
resolver.writeTypeAtLocation(node.constraint, enclosingDeclaration, TypeFormatFlags.None, writer);
// TODO(shkamat) This is just till we get rest of the error reporting up
getSymbolVisibilityDiagnosticMessage = undefined;
}
}
2014-07-13 01:04:16 +02:00
if (typeParameters) {
write("<");
emitCommaList(typeParameters, emitTypeParameter);
write(">");
}
}
function emitHeritageClause(typeReferences: TypeReferenceNode[], isImplementsList: boolean) {
function emitTypeOfTypeReference(node: Node) {
function getHeritageClauseVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult) {
var diagnosticMessage: DiagnosticMessage;
if (node.parent.kind === SyntaxKind.ClassDeclaration) {
// Class
if (symbolAccesibilityResult.accessibility == SymbolAccessibility.NotAccessible) {
if (symbolAccesibilityResult.errorModuleName) {
// Module is inaccessible
diagnosticMessage = isImplementsList ?
Diagnostics.Implements_clause_of_exported_class_0_has_or_is_using_name_1_from_private_module_2 :
Diagnostics.Extends_clause_of_exported_class_0_has_or_is_using_name_1_from_private_module_2;
}
else {
// 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 {
// CannotBeNamed
// TODO(shkamat): CannotBeNamed error needs to be handled
}
}
else {
// Interface
if (symbolAccesibilityResult.accessibility == SymbolAccessibility.NotAccessible) {
if (symbolAccesibilityResult.errorModuleName) {
// Module is inaccessible
diagnosticMessage = Diagnostics.Extends_clause_of_exported_interface_0_has_or_is_using_name_1_from_private_module_2;
}
else {
// interface is inaccessible
diagnosticMessage = Diagnostics.Extends_clause_of_exported_interface_0_has_or_is_using_private_name_1;
}
}
else {
// CannotBeNamed
// TODO(shkamat): CannotBeNamed error needs to be handled
}
}
return {
diagnosticMessage: diagnosticMessage,
errorNode: node,
typeName: (<Declaration>node.parent).name
}
}
getSymbolVisibilityDiagnosticMessage = getHeritageClauseVisibilityError;
resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.WriteArrayAsGenericType, writer);
// TODO(shkamat) This is just till we get rest of the error reporting up
getSymbolVisibilityDiagnosticMessage = undefined;
}
2014-07-13 01:04:16 +02:00
if (typeReferences) {
write(isImplementsList ? " implements " : " extends ");
emitCommaList(typeReferences, emitTypeOfTypeReference);
}
2014-07-12 01:36:06 +02:00
}
function emitClassDeclaration(node: ClassDeclaration) {
function emitParameterProperties(constructorDeclaration: ConstructorDeclaration) {
if (constructorDeclaration) {
forEach(constructorDeclaration.parameters, param => {
if (param.flags & (NodeFlags.Public | NodeFlags.Private)) {
emitPropertyDeclaration(param);
}
});
}
}
2014-07-13 01:04:16 +02:00
if (resolver.isDeclarationVisible(node)) {
emitDeclarationFlags(node);
write("class ");
emitSourceTextOfNode(node.name);
var prevEnclosingDeclaration = enclosingDeclaration;
enclosingDeclaration = node;
emitTypeParameters(node.typeParameters);
if (node.baseType) {
emitHeritageClause([node.baseType], /*isImplementsList*/ false);
}
emitHeritageClause(node.implementedTypes, /*isImplementsList*/ true);
write(" {");
writeLine();
increaseIndent();
emitParameterProperties(getFirstConstructorWithBody(node));
emitLines(node.members);
decreaseIndent();
write("}");
writeLine();
enclosingDeclaration = prevEnclosingDeclaration;
2014-07-13 01:04:16 +02:00
}
}
function emitInterfaceDeclaration(node: InterfaceDeclaration) {
if (resolver.isDeclarationVisible(node)) {
emitDeclarationFlags(node);
write("interface ");
emitSourceTextOfNode(node.name);
var prevEnclosingDeclaration = enclosingDeclaration;
enclosingDeclaration = node;
emitTypeParameters(node.typeParameters);
emitHeritageClause(node.baseTypes, /*isImplementsList*/ false);
write(" {");
writeLine();
increaseIndent();
emitLines(node.members);
decreaseIndent();
write("}");
writeLine();
enclosingDeclaration = prevEnclosingDeclaration;
2014-07-13 01:04:16 +02:00
}
}
function emitPropertyDeclaration(node: PropertyDeclaration) {
2014-07-13 01:04:16 +02:00
emitDeclarationFlags(node);
emitVariableDeclaration(node);
write(";");
2014-07-13 01:04:16 +02:00
writeLine();
}
function emitVariableDeclaration(node: VariableDeclaration) {
// If we are emitting property it isnt 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)) {
emitSourceTextOfNode(node.name);
// If optional property emit ?
if (node.kind === SyntaxKind.Property && (node.flags & NodeFlags.QuestionMark)) {
write("?");
}
if (!(node.flags & NodeFlags.Private)) {
write(": ");
resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.None, writer);
}
}
2014-07-13 01:04:16 +02:00
}
function emitVariableStatement(node: VariableStatement) {
var hasDeclarationWithEmit = forEach(node.declarations, varDeclaration => resolver.isDeclarationVisible(varDeclaration));
if (hasDeclarationWithEmit) {
emitDeclarationFlags(node);
write("var ");
emitCommaList(node.declarations, emitVariableDeclaration);
write(";");
writeLine();
2014-07-13 01:04:16 +02:00
}
}
function emitAccessorDeclaration(node: AccessorDeclaration) {
var accessors = getAllAccessorDeclarations(<ClassDeclaration>node.parent, node);
if (node === accessors.firstAccessor) {
emitDeclarationFlags(node);
2014-07-13 01:04:16 +02:00
emitSourceTextOfNode(node.name);
if (!(node.flags & NodeFlags.Private)) {
write(": ");
resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.None, writer);
}
write(";");
writeLine();
2014-07-13 01:04:16 +02:00
}
}
function emitFunctionDeclaration(node: FunctionDeclaration) {
// If we are emitting Method/Constructor it isnt moduleElement and hence already determined to be emitting
// so no need to verify if the declaration is visible
if ((node.kind !== SyntaxKind.FunctionDeclaration || resolver.isDeclarationVisible(node)) &&
!resolver.isImplementationOfOverload(node)) {
emitDeclarationFlags(node);
if (node.kind === SyntaxKind.FunctionDeclaration) {
write("function ");
emitSourceTextOfNode(node.name);
}
else if (node.kind === SyntaxKind.Constructor) {
write("constructor");
2014-07-13 01:04:16 +02:00
}
else {
emitSourceTextOfNode(node.name);
if (node.flags & NodeFlags.QuestionMark) {
write("?");
}
}
emitSignatureDeclaration(node);
2014-07-13 01:04:16 +02:00
}
}
function emitConstructSignatureDeclaration(node: SignatureDeclaration) {
write("new ");
2014-07-13 01:04:16 +02:00
emitSignatureDeclaration(node);
}
function emitSignatureDeclaration(node: SignatureDeclaration) {
emitTypeParameters(node.typeParameters);
if (node.kind === SyntaxKind.IndexSignature) {
write("[");
}
else {
write("(");
}
2014-07-13 01:04:16 +02:00
// Parameters
emitCommaList(node.parameters, emitParameterDeclaration);
2014-07-13 01:04:16 +02:00
if (node.kind === SyntaxKind.IndexSignature) {
write("]");
}
else {
write(")");
}
2014-07-13 01:04:16 +02:00
// If this is not a constructor and is not private, emit the return type
if (node.kind !== SyntaxKind.Constructor && !(node.flags & NodeFlags.Private)) {
write(": ");
resolver.writeReturnTypeOfSignatureDeclaration(node, enclosingDeclaration, TypeFormatFlags.None, writer);
}
write(";");
writeLine();
2014-07-13 01:04:16 +02:00
}
function emitParameterDeclaration(node: ParameterDeclaration) {
if (node.flags & NodeFlags.Rest) {
write("...");
}
emitSourceTextOfNode(node.name);
if (node.initializer || (node.flags & NodeFlags.QuestionMark)) {
write("?");
}
2014-07-13 01:04:16 +02:00
if (!(node.parent.flags & NodeFlags.Private)) {
write(": ");
resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.None, writer);
}
2014-07-13 01:04:16 +02:00
}
function emitNode(node: Node) {
switch (node.kind) {
case SyntaxKind.Constructor:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.Method:
return emitFunctionDeclaration(<FunctionDeclaration>node);
case SyntaxKind.ConstructSignature:
return emitConstructSignatureDeclaration(<SignatureDeclaration>node);
case SyntaxKind.CallSignature:
case SyntaxKind.IndexSignature:
return emitSignatureDeclaration(<SignatureDeclaration>node);
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return emitAccessorDeclaration(<AccessorDeclaration>node);
case SyntaxKind.VariableStatement:
return emitVariableStatement(<VariableStatement>node);
case SyntaxKind.Property:
return emitPropertyDeclaration(<PropertyDeclaration>node);
case SyntaxKind.InterfaceDeclaration:
return emitInterfaceDeclaration(<InterfaceDeclaration>node);
case SyntaxKind.ClassDeclaration:
return emitClassDeclaration(<ClassDeclaration>node);
case SyntaxKind.EnumMember:
return emitEnumMemberDeclaration(<EnumMember>node);
case SyntaxKind.EnumDeclaration:
return emitEnumDeclaration(<EnumDeclaration>node);
case SyntaxKind.ModuleDeclaration:
return emitModuleDeclaration(<ModuleDeclaration>node);
case SyntaxKind.ImportDeclaration:
return emitImportDeclaration(<ImportDeclaration>node);
case SyntaxKind.ExportAssignment:
return emitExportAssignment(<ExportAssignment>node);
case SyntaxKind.SourceFile:
return emitSourceFile(<SourceFile>node);
}
}
function resolveScriptReference(sourceFile: SourceFile, reference: FileReference) {
var referenceFileName = compilerOptions.noResolve
? reference.filename
: normalizePath(combinePaths(getDirectoryPath(sourceFile.filename), reference.filename));
return program.getSourceFile(referenceFileName);
}
// 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
var referencePathsOutput = "";
function writeReferencePath(referencedFile: SourceFile) {
var declFileName = referencedFile.flags & NodeFlags.DeclarationFile
? referencedFile.filename // Declaration file, use declaration file name
: shouldEmitToOwnFile(referencedFile)
? getOwnEmitOutputFilePath(referencedFile, ".d.ts") // Own output file so get the .d.ts file
: getModuleNameFromFilename(compilerOptions.out) + ".d.ts";// Global out file
declFileName = getRelativePathToDirectoryOrUrl(
getDirectoryPath(normalizeSlashes(jsFilePath)),
declFileName,
compilerHost.getCurrentDirectory(),
/*isAbsolutePathAnUrl*/ false);
2014-07-13 01:04:16 +02:00
referencePathsOutput += "/// <reference path='" + declFileName + "' />" + sys.newLine;
2014-07-13 01:04:16 +02:00
}
if (root) {
// Emiting single file so emit references in this file only
var addedGlobalFileReference = false;
forEach(root.referencedFiles, fileReference => {
var referencedFile = resolveScriptReference(root, fileReference);
2014-07-13 01:04:16 +02:00
// All the references that are not going to be part of same file
if ((referencedFile.flags & NodeFlags.DeclarationFile) || // This is a declare file reference
shouldEmitToOwnFile(referencedFile) || // This is referenced file is emitting its own js file
!addedGlobalFileReference) { // Or the global out file corresponding to this reference was not added
2014-07-13 01:04:16 +02:00
writeReferencePath(referencedFile);
if (!isExternalModuleOrDeclarationFile(referencedFile)) {
addedGlobalFileReference = true;
}
}
});
2014-07-13 01:04:16 +02:00
emitNode(root);
}
else {
// Emit references corresponding to this file
var emittedReferencedFiles: SourceFile[] = [];
forEach(program.getSourceFiles(), sourceFile => {
if (!isExternalModuleOrDeclarationFile(sourceFile)) {
// Check what references need to be added
forEach(sourceFile.referencedFiles, fileReference => {
var referencedFile = resolveScriptReference(sourceFile, fileReference);
// If the reference file is declaration file or external module emit that reference
if (isExternalModuleOrDeclarationFile(referencedFile) &&
!contains(emittedReferencedFiles, referencedFile)) { // If the file refernece was not already emitted
writeReferencePath(referencedFile);
emittedReferencedFiles.push(referencedFile);
}
});
2014-07-13 01:04:16 +02:00
emitNode(sourceFile);
2014-07-13 01:04:16 +02:00
}
});
}
2014-07-13 01:04:16 +02:00
// 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());
}
2014-07-13 01:04:16 +02:00
}
var shouldEmitDeclarations = resolver.shouldEmitDeclarations();
function emitFile(jsFilePath: string, sourceFile?: SourceFile) {
emitJavaScript(jsFilePath, sourceFile);
if (shouldEmitDeclarations) {
emitDeclarations(jsFilePath, sourceFile);
}
2014-07-13 01:04:16 +02:00
}
forEach(program.getSourceFiles(), sourceFile => {
if (shouldEmitToOwnFile(sourceFile)) {
var jsFilePath = getOwnEmitOutputFilePath(sourceFile, ".js");
emitFile(jsFilePath, sourceFile);
}
});
if (compilerOptions.out) {
emitFile(compilerOptions.out);
}
return {
errors: diagnostics,
sourceMaps: sourceMapDataList
};
2014-07-13 01:04:16 +02:00
}
}