TypeScript/src/compiler/emitter.ts

8401 lines
398 KiB
TypeScript
Raw Normal View History

/// <reference path="checker.ts"/>
/// <reference path="sourcemap.ts" />
/// <reference path="declarationEmitter.ts"/>
2014-07-13 01:04:16 +02:00
/* @internal */
namespace ts {
2015-11-03 01:54:12 +01:00
export function getResolvedExternalModuleName(host: EmitHost, file: SourceFile): string {
2015-11-02 21:53:27 +01:00
return file.moduleName || getExternalModuleNameFromPath(host, file.fileName);
}
2015-11-03 01:54:12 +01:00
export function getExternalModuleNameFromDeclaration(host: EmitHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration): string {
2015-11-09 21:50:38 +01:00
const file = resolver.getExternalModuleFileFromDeclaration(declaration);
2015-11-03 01:54:12 +01:00
if (!file || isDeclarationFile(file)) {
return undefined;
}
return getResolvedExternalModuleName(host, file);
}
type DependencyGroup = Array<ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration>;
2015-10-27 18:41:21 +01:00
const enum Jump {
Break = 1 << 1,
Continue = 1 << 2,
Return = 1 << 3
}
2016-08-16 01:41:32 +02:00
const entities = createMap({
"quot": 0x0022,
"amp": 0x0026,
"apos": 0x0027,
"lt": 0x003C,
"gt": 0x003E,
"nbsp": 0x00A0,
"iexcl": 0x00A1,
"cent": 0x00A2,
"pound": 0x00A3,
"curren": 0x00A4,
"yen": 0x00A5,
"brvbar": 0x00A6,
"sect": 0x00A7,
"uml": 0x00A8,
"copy": 0x00A9,
"ordf": 0x00AA,
"laquo": 0x00AB,
"not": 0x00AC,
"shy": 0x00AD,
"reg": 0x00AE,
"macr": 0x00AF,
"deg": 0x00B0,
"plusmn": 0x00B1,
"sup2": 0x00B2,
"sup3": 0x00B3,
"acute": 0x00B4,
"micro": 0x00B5,
"para": 0x00B6,
"middot": 0x00B7,
"cedil": 0x00B8,
"sup1": 0x00B9,
"ordm": 0x00BA,
"raquo": 0x00BB,
"frac14": 0x00BC,
"frac12": 0x00BD,
"frac34": 0x00BE,
"iquest": 0x00BF,
"Agrave": 0x00C0,
"Aacute": 0x00C1,
"Acirc": 0x00C2,
"Atilde": 0x00C3,
"Auml": 0x00C4,
"Aring": 0x00C5,
"AElig": 0x00C6,
"Ccedil": 0x00C7,
"Egrave": 0x00C8,
"Eacute": 0x00C9,
"Ecirc": 0x00CA,
"Euml": 0x00CB,
"Igrave": 0x00CC,
"Iacute": 0x00CD,
"Icirc": 0x00CE,
"Iuml": 0x00CF,
"ETH": 0x00D0,
"Ntilde": 0x00D1,
"Ograve": 0x00D2,
"Oacute": 0x00D3,
"Ocirc": 0x00D4,
"Otilde": 0x00D5,
"Ouml": 0x00D6,
"times": 0x00D7,
"Oslash": 0x00D8,
"Ugrave": 0x00D9,
"Uacute": 0x00DA,
"Ucirc": 0x00DB,
"Uuml": 0x00DC,
"Yacute": 0x00DD,
"THORN": 0x00DE,
"szlig": 0x00DF,
"agrave": 0x00E0,
"aacute": 0x00E1,
"acirc": 0x00E2,
"atilde": 0x00E3,
"auml": 0x00E4,
"aring": 0x00E5,
"aelig": 0x00E6,
"ccedil": 0x00E7,
"egrave": 0x00E8,
"eacute": 0x00E9,
"ecirc": 0x00EA,
"euml": 0x00EB,
"igrave": 0x00EC,
"iacute": 0x00ED,
"icirc": 0x00EE,
"iuml": 0x00EF,
"eth": 0x00F0,
"ntilde": 0x00F1,
"ograve": 0x00F2,
"oacute": 0x00F3,
"ocirc": 0x00F4,
"otilde": 0x00F5,
"ouml": 0x00F6,
"divide": 0x00F7,
"oslash": 0x00F8,
"ugrave": 0x00F9,
"uacute": 0x00FA,
"ucirc": 0x00FB,
"uuml": 0x00FC,
"yacute": 0x00FD,
"thorn": 0x00FE,
"yuml": 0x00FF,
"OElig": 0x0152,
"oelig": 0x0153,
"Scaron": 0x0160,
"scaron": 0x0161,
"Yuml": 0x0178,
"fnof": 0x0192,
"circ": 0x02C6,
"tilde": 0x02DC,
"Alpha": 0x0391,
"Beta": 0x0392,
"Gamma": 0x0393,
"Delta": 0x0394,
"Epsilon": 0x0395,
"Zeta": 0x0396,
"Eta": 0x0397,
"Theta": 0x0398,
"Iota": 0x0399,
"Kappa": 0x039A,
"Lambda": 0x039B,
"Mu": 0x039C,
"Nu": 0x039D,
"Xi": 0x039E,
"Omicron": 0x039F,
"Pi": 0x03A0,
"Rho": 0x03A1,
"Sigma": 0x03A3,
"Tau": 0x03A4,
"Upsilon": 0x03A5,
"Phi": 0x03A6,
"Chi": 0x03A7,
"Psi": 0x03A8,
"Omega": 0x03A9,
"alpha": 0x03B1,
"beta": 0x03B2,
"gamma": 0x03B3,
"delta": 0x03B4,
"epsilon": 0x03B5,
"zeta": 0x03B6,
"eta": 0x03B7,
"theta": 0x03B8,
"iota": 0x03B9,
"kappa": 0x03BA,
"lambda": 0x03BB,
"mu": 0x03BC,
"nu": 0x03BD,
"xi": 0x03BE,
"omicron": 0x03BF,
"pi": 0x03C0,
"rho": 0x03C1,
"sigmaf": 0x03C2,
"sigma": 0x03C3,
"tau": 0x03C4,
"upsilon": 0x03C5,
"phi": 0x03C6,
"chi": 0x03C7,
"psi": 0x03C8,
"omega": 0x03C9,
"thetasym": 0x03D1,
"upsih": 0x03D2,
"piv": 0x03D6,
"ensp": 0x2002,
"emsp": 0x2003,
"thinsp": 0x2009,
"zwnj": 0x200C,
"zwj": 0x200D,
"lrm": 0x200E,
"rlm": 0x200F,
"ndash": 0x2013,
"mdash": 0x2014,
"lsquo": 0x2018,
"rsquo": 0x2019,
"sbquo": 0x201A,
"ldquo": 0x201C,
"rdquo": 0x201D,
"bdquo": 0x201E,
"dagger": 0x2020,
"Dagger": 0x2021,
"bull": 0x2022,
"hellip": 0x2026,
"permil": 0x2030,
"prime": 0x2032,
"Prime": 0x2033,
"lsaquo": 0x2039,
"rsaquo": 0x203A,
"oline": 0x203E,
"frasl": 0x2044,
"euro": 0x20AC,
"image": 0x2111,
"weierp": 0x2118,
"real": 0x211C,
"trade": 0x2122,
"alefsym": 0x2135,
"larr": 0x2190,
"uarr": 0x2191,
"rarr": 0x2192,
"darr": 0x2193,
"harr": 0x2194,
"crarr": 0x21B5,
"lArr": 0x21D0,
"uArr": 0x21D1,
"rArr": 0x21D2,
"dArr": 0x21D3,
"hArr": 0x21D4,
"forall": 0x2200,
"part": 0x2202,
"exist": 0x2203,
"empty": 0x2205,
"nabla": 0x2207,
"isin": 0x2208,
"notin": 0x2209,
"ni": 0x220B,
"prod": 0x220F,
"sum": 0x2211,
"minus": 0x2212,
"lowast": 0x2217,
"radic": 0x221A,
"prop": 0x221D,
"infin": 0x221E,
"ang": 0x2220,
"and": 0x2227,
"or": 0x2228,
"cap": 0x2229,
"cup": 0x222A,
"int": 0x222B,
"there4": 0x2234,
"sim": 0x223C,
"cong": 0x2245,
"asymp": 0x2248,
"ne": 0x2260,
"equiv": 0x2261,
"le": 0x2264,
"ge": 0x2265,
"sub": 0x2282,
"sup": 0x2283,
"nsub": 0x2284,
"sube": 0x2286,
"supe": 0x2287,
"oplus": 0x2295,
"otimes": 0x2297,
"perp": 0x22A5,
"sdot": 0x22C5,
"lceil": 0x2308,
"rceil": 0x2309,
"lfloor": 0x230A,
"rfloor": 0x230B,
"lang": 0x2329,
"rang": 0x232A,
"loz": 0x25CA,
"spades": 0x2660,
"clubs": 0x2663,
"hearts": 0x2665,
"diams": 0x2666
2016-08-16 01:41:32 +02:00
});
// Flags enum to track count of temp variables and a few dedicated names
const enum TempFlags {
Auto = 0x00000000, // No preferred name
CountMask = 0x0FFFFFFF, // Temp variable counter
_i = 0x10000000, // Use/preference flag for '_i'
2015-03-24 00:16:29 +01:00
}
const enum CopyDirection {
ToOriginal,
ToOutParameter
}
/**
* If loop contains block scoped binding captured in some function then loop body is converted to a function.
* Lexical bindings declared in loop initializer will be passed into the loop body function as parameters,
* however if this binding is modified inside the body - this new value should be propagated back to the original binding.
* This is done by declaring new variable (out parameter holder) outside of the loop for every binding that is reassigned inside the body.
* On every iteration this variable is initialized with value of corresponding binding.
* At every point where control flow leaves the loop either explicitly (break/continue) or implicitly (at the end of loop body)
* we copy the value inside the loop to the out parameter holder.
*
* for (let x;;) {
* let a = 1;
* let b = () => a;
* x++
* if (...) break;
* ...
* }
*
* will be converted to
*
* var out_x;
* var loop = function(x) {
* var a = 1;
* var b = function() { return a; }
* x++;
* if (...) return out_x = x, "break";
* ...
* out_x = x;
* }
* for (var x;;) {
* out_x = x;
* var state = loop(x);
* x = out_x;
* if (state === "break") break;
* }
*
* NOTE: values to out parameters are not copies if loop is abrupted with 'return' - in this case this will end the entire enclosing function
* so nobody can observe this new value.
*/
interface LoopOutParameter {
originalName: Identifier;
outParamName: string;
}
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean): EmitResult {
// emit output for the __extends helper function
const extendsHelper = `
2015-05-01 19:49:54 +02:00
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};`;
const assignHelper = `
var __assign = (this && this.__assign) || Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};`;
// emit output for the __decorate helper function
const decorateHelper = `
2015-05-01 19:49:54 +02:00
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2015-09-11 01:27:35 +02:00
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};`;
// emit output for the __metadata helper function
const metadataHelper = `
2015-05-01 19:49:54 +02:00
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};`;
// emit output for the __param helper function
const paramHelper = `
2015-05-01 19:49:54 +02:00
var __param = (this && this.__param) || function (paramIndex, decorator) {
2015-04-24 00:36:07 +02:00
return function (target, key) { decorator(target, key, paramIndex); }
};`;
2015-05-07 02:33:58 +02:00
const awaiterHelper = `
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments)).next());
});
};`;
2015-06-18 23:01:49 +02:00
2015-11-04 23:02:33 +01:00
const compilerOptions = host.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
const modulekind = getEmitModuleKind(compilerOptions);
2015-11-04 23:02:33 +01:00
const sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined;
const emittedFilesList: string[] = compilerOptions.listEmittedFiles ? [] : undefined;
const emitterDiagnostics = createDiagnosticCollection();
let emitSkipped = false;
2015-11-04 23:02:33 +01:00
const newLine = host.getNewLine();
const emitJavaScript = createFileEmitter();
forEachExpectedEmitFile(host, emitFile, targetSourceFile, emitOnlyDtsFiles);
return {
emitSkipped,
diagnostics: emitterDiagnostics.getDiagnostics(),
emittedFiles: emittedFilesList,
sourceMaps: sourceMapDataList
};
2015-03-24 00:16:29 +01:00
function isUniqueLocalName(name: string, container: Node): boolean {
for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) {
2016-08-15 21:03:39 +02:00
if (node.locals && name in node.locals) {
2015-03-24 00:16:29 +01:00
// We conservatively include alias symbols to cover cases where they're emitted as locals
if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) {
return false;
}
}
}
return true;
}
interface ConvertedLoopState {
2015-10-19 23:10:27 +02:00
/*
* set of labels that occurred inside the converted loop
2015-10-19 23:10:27 +02:00
* used to determine if labeled jump can be emitted as is or it should be dispatched to calling code
*/
labels?: Map<string>;
2015-10-19 23:10:27 +02:00
/*
* collection of labeled jumps that transfer control outside the converted loop.
* maps store association 'label -> labelMarker' where
* - label - value of label as it appear in code
2015-10-19 23:10:27 +02:00
* - label marker - return value that should be interpreted by calling code as 'jump to <label>'
2015-10-19 23:21:53 +02:00
*/
labeledNonLocalBreaks?: Map<string>;
labeledNonLocalContinues?: Map<string>;
2015-10-19 23:10:27 +02:00
/*
* set of non-labeled jumps that transfer control outside the converted loop
* used to emit dispatching logic in the caller of converted loop
*/
nonLocalJumps?: Jump;
2015-10-19 23:10:27 +02:00
/*
* set of non-labeled jumps that should be interpreted as local
* i.e. if converted loop contains normal loop or switch statement then inside this loop break should be treated as local jump
*/
allowedNonLabeledJumps?: Jump;
2015-10-19 23:10:27 +02:00
/*
* alias for 'arguments' object from the calling code stack frame
* i.e.
* for (let x;;) <statement that captures x in closure and uses 'arguments'>
* should be converted to
* var loop = function(x) { <code where 'arguments' is replaced with 'arguments_1'> }
2015-10-19 23:10:27 +02:00
* var arguments_1 = arguments
* for (var x;;) loop(x);
* otherwise semantics of the code will be different since 'arguments' inside converted loop body
2015-10-19 23:10:27 +02:00
* will refer to function that holds converted loop.
* This value is set on demand.
*/
argumentsName?: string;
/*
* alias for 'this' from the calling code stack frame in case if this was used inside the converted loop
*/
thisName?: string;
2015-10-19 23:10:27 +02:00
/*
* list of non-block scoped variable declarations that appear inside converted loop
* such variable declarations should be moved outside the loop body
2015-10-19 23:10:27 +02:00
* for (let x;;) {
* var y = 1;
* ...
2015-10-19 23:10:27 +02:00
* }
* should be converted to
* var loop = function(x) {
* y = 1;
* ...
* }
* var y;
* for (var x;;) loop(x);
*/
hoistedLocalVariables?: Identifier[];
/**
* List of loop out parameters - detailed descripion can be found in the comment to LoopOutParameter
*/
loopOutParameters?: LoopOutParameter[];
}
function setLabeledJump(state: ConvertedLoopState, isBreak: boolean, labelText: string, labelMarker: string): void {
if (isBreak) {
if (!state.labeledNonLocalBreaks) {
2016-08-11 01:47:06 +02:00
state.labeledNonLocalBreaks = createMap<string>();
}
state.labeledNonLocalBreaks[labelText] = labelMarker;
}
else {
if (!state.labeledNonLocalContinues) {
2016-08-11 01:47:06 +02:00
state.labeledNonLocalContinues = createMap<string>();
}
state.labeledNonLocalContinues[labelText] = labelMarker;
}
}
function hoistVariableDeclarationFromLoop(state: ConvertedLoopState, declaration: VariableDeclaration): void {
if (!state.hoistedLocalVariables) {
state.hoistedLocalVariables = [];
}
visit(declaration.name);
function visit(node: Identifier | BindingPattern) {
if (node.kind === SyntaxKind.Identifier) {
state.hoistedLocalVariables.push((<Identifier>node));
}
else {
for (const element of (<BindingPattern>node).elements) {
visit(element.name);
}
}
}
}
function createFileEmitter(): (jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) => void {
2015-11-04 23:02:33 +01:00
const writer = createTextWriter(newLine);
const { write, writeTextOfNode, writeLine, increaseIndent, decreaseIndent } = writer;
2015-12-03 23:20:25 +01:00
let sourceMap = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? createSourceMapWriter(host, writer) : getNullSourceMapWriter();
let { setSourceFile, emitStart, emitEnd, emitPos } = sourceMap;
2015-03-13 20:34:12 +01:00
let currentSourceFile: SourceFile;
let currentText: string;
let currentLineMap: number[];
let currentFileIdentifiers: Map<string>;
2016-08-16 01:41:32 +02:00
let renamedDependencies: Map<string>;
let isEs6Module: boolean;
let isCurrentFileExternalModule: boolean;
2015-04-22 07:27:33 +02:00
// name of an exporter function if file is a System external module
// System.register([...], function (<exporter>) {...})
// exporting in System modules looks like:
// export var x; ... x = 1
// =>
// var x;... exporter("x", x = 1)
2015-04-10 21:10:38 +02:00
let exportFunctionForFile: string;
let contextObjectForFile: string;
let generatedNameSet: Map<string>;
let nodeToGeneratedName: string[];
let computedPropertyNamesToGeneratedNames: string[];
let decoratedClassAliases: string[];
let convertedLoopState: ConvertedLoopState;
let extendsEmitted: boolean;
let assignEmitted: boolean;
let decorateEmitted: boolean;
let paramEmitted: boolean;
let awaiterEmitted: boolean;
let tempFlags: TempFlags = 0;
2015-03-13 20:34:12 +01:00
let tempVariables: Identifier[];
let tempParameters: Identifier[];
let externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[];
2015-03-13 20:34:12 +01:00
let exportSpecifiers: Map<ExportSpecifier[]>;
let exportEquals: ExportAssignment;
let hasExportStarsToExportValues: boolean;
2014-07-13 01:04:16 +02:00
2015-03-13 20:34:12 +01:00
let detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number }[];
/** Sourcemap data that will get encoded */
2015-03-13 20:34:12 +01:00
let sourceMapData: SourceMapData;
/** Is the file being emitted into its own file */
let isOwnFileEmit: boolean;
2015-09-10 22:05:51 +02:00
/** If removeComments is true, no leading-comments needed to be emitted **/
2015-11-04 23:02:33 +01:00
const emitLeadingCommentsOfPosition = compilerOptions.removeComments ? function (pos: number) { } : emitLeadingCommentsOfPositionWorker;
2015-12-03 23:20:25 +01:00
const setSourceMapWriterEmit = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? changeSourceMapEmit : function (writer: SourceMapWriter) { };
2016-08-16 01:41:32 +02:00
const moduleEmitDelegates = createMap<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void>({
[ModuleKind.ES6]: emitES6Module,
[ModuleKind.AMD]: emitAMDModule,
[ModuleKind.System]: emitSystemModule,
[ModuleKind.UMD]: emitUMDModule,
[ModuleKind.CommonJS]: emitCommonJSModule,
2016-08-16 01:41:32 +02:00
});
2015-09-10 22:05:51 +02:00
2016-08-16 01:41:32 +02:00
const bundleEmitDelegates = createMap<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void>({
2015-10-03 02:43:58 +02:00
[ModuleKind.ES6]() {},
[ModuleKind.AMD]: emitAMDModule,
[ModuleKind.System]: emitSystemModule,
2015-10-03 02:43:58 +02:00
[ModuleKind.UMD]() {},
[ModuleKind.CommonJS]() {},
2016-08-16 01:41:32 +02:00
});
return doEmit;
function doEmit(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
2016-08-11 01:47:06 +02:00
generatedNameSet = createMap<string>();
nodeToGeneratedName = [];
decoratedClassAliases = [];
isOwnFileEmit = !isBundledEmit;
// Emit helpers from all the files
if (isBundledEmit && modulekind) {
forEach(sourceFiles, emitEmitHelpers);
}
2015-11-09 21:50:38 +01:00
// Do not call emit directly. It does not set the currentSourceFile.
forEach(sourceFiles, emitSourceFile);
writeLine();
const sourceMappingURL = sourceMap.getSourceMappingURL();
if (sourceMappingURL) {
write(`//# ${"sourceMappingURL"}=${sourceMappingURL}`); // Sometimes tools can sometimes see this line as a source mapping url comment
}
writeEmittedFiles(writer.getText(), jsFilePath, sourceMapFilePath, /*writeByteOrderMark*/ compilerOptions.emitBOM, sourceFiles);
// reset the state
sourceMap.reset();
writer.reset();
currentSourceFile = undefined;
currentText = undefined;
currentLineMap = undefined;
exportFunctionForFile = undefined;
contextObjectForFile = undefined;
generatedNameSet = undefined;
nodeToGeneratedName = undefined;
decoratedClassAliases = undefined;
computedPropertyNamesToGeneratedNames = undefined;
convertedLoopState = undefined;
extendsEmitted = false;
decorateEmitted = false;
paramEmitted = false;
awaiterEmitted = false;
assignEmitted = false;
tempFlags = 0;
tempVariables = undefined;
tempParameters = undefined;
externalImports = undefined;
exportSpecifiers = undefined;
exportEquals = undefined;
2015-11-09 19:46:50 +01:00
hasExportStarsToExportValues = undefined;
detachedCommentsInfo = undefined;
sourceMapData = undefined;
isEs6Module = false;
renamedDependencies = undefined;
isCurrentFileExternalModule = false;
}
2015-03-09 21:32:02 +01:00
function emitSourceFile(sourceFile: SourceFile): void {
currentSourceFile = sourceFile;
currentText = sourceFile.text;
currentLineMap = getLineStarts(sourceFile);
2015-04-10 21:10:38 +02:00
exportFunctionForFile = undefined;
contextObjectForFile = undefined;
isEs6Module = sourceFile.symbol && sourceFile.symbol.exports && !!sourceFile.symbol.exports["___esModule"];
renamedDependencies = sourceFile.renamedDependencies;
currentFileIdentifiers = sourceFile.identifiers;
isCurrentFileExternalModule = isExternalModule(sourceFile);
setSourceFile(sourceFile);
emitNodeWithCommentsAndWithoutSourcemap(sourceFile);
2015-03-09 21:32:02 +01:00
}
2015-03-24 00:16:29 +01:00
function isUniqueName(name: string): boolean {
return !resolver.hasGlobalName(name) &&
2016-08-15 21:03:39 +02:00
!(name in currentFileIdentifiers) &&
!(name in generatedNameSet);
}
// Return the next available name in the pattern _a ... _z, _0, _1, ...
// TempFlags._i or TempFlags._n may be used to express a preference for that dedicated name.
// Note that names generated by makeTempVariableName and makeUniqueName will never conflict.
function makeTempVariableName(flags: TempFlags): string {
if (flags && !(tempFlags & flags)) {
2015-11-04 23:02:33 +01:00
const name = flags === TempFlags._i ? "_i" : "_n";
if (isUniqueName(name)) {
tempFlags |= flags;
return name;
2015-03-24 00:16:29 +01:00
}
}
while (true) {
2015-11-04 23:02:33 +01:00
const count = tempFlags & TempFlags.CountMask;
tempFlags++;
2015-03-27 00:32:27 +01:00
// Skip over 'i' and 'n'
if (count !== 8 && count !== 13) {
2015-11-04 23:02:33 +01:00
const name = count < 26 ? "_" + String.fromCharCode(CharacterCodes.a + count) : "_" + (count - 26);
2015-03-27 00:32:27 +01:00
if (isUniqueName(name)) {
return name;
2015-03-24 00:16:29 +01:00
}
}
}
}
// Generate a name that is unique within the current file and doesn't conflict with any names
// in global scope. The name is formed by adding an '_n' suffix to the specified base name,
// where n is a positive integer. Note that names generated by makeTempVariableName and
// makeUniqueName are guaranteed to never conflict.
2015-03-24 00:16:29 +01:00
function makeUniqueName(baseName: string): string {
// Find the first unique 'name_n', where n is a positive number
if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) {
baseName += "_";
}
2015-03-24 00:16:29 +01:00
let i = 1;
while (true) {
2015-11-04 23:02:33 +01:00
const generatedName = baseName + i;
2015-03-24 00:16:29 +01:00
if (isUniqueName(generatedName)) {
2015-03-27 00:32:27 +01:00
return generatedNameSet[generatedName] = generatedName;
2015-03-24 00:16:29 +01:00
}
i++;
}
}
2015-03-24 00:16:29 +01:00
function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) {
2015-11-04 23:02:33 +01:00
const name = node.name.text;
// Use module/enum name itself if it is unique, otherwise make a unique variation
return isUniqueLocalName(name, node) ? name : makeUniqueName(name);
2015-03-24 00:16:29 +01:00
}
2015-03-04 08:46:51 +01:00
2015-03-24 00:16:29 +01:00
function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) {
2015-11-04 23:02:33 +01:00
const expr = getExternalModuleName(node);
const baseName = expr.kind === SyntaxKind.StringLiteral ?
2015-03-24 00:16:29 +01:00
escapeIdentifier(makeIdentifierFromModuleName((<LiteralExpression>expr).text)) : "module";
return makeUniqueName(baseName);
2015-03-24 00:16:29 +01:00
}
function generateNameForExportDefault() {
return makeUniqueName("default");
}
function generateNameForClassExpression() {
return makeUniqueName("class");
}
function generateNameForNode(node: Node) {
switch (node.kind) {
case SyntaxKind.Identifier:
return makeUniqueName((<Identifier>node).text);
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.EnumDeclaration:
return generateNameForModuleOrEnum(<ModuleDeclaration | EnumDeclaration>node);
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ExportDeclaration:
return generateNameForImportOrExportDeclaration(<ImportDeclaration | ExportDeclaration>node);
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ExportAssignment:
return generateNameForExportDefault();
case SyntaxKind.ClassExpression:
return generateNameForClassExpression();
default:
Debug.fail();
}
2015-03-24 00:16:29 +01:00
}
function getGeneratedNameForNode(node: Node) {
2015-11-04 23:02:33 +01:00
const id = getNodeId(node);
return nodeToGeneratedName[id] || (nodeToGeneratedName[id] = unescapeIdentifier(generateNameForNode(node)));
}
/** Write emitted output to disk */
function writeEmittedFiles(emitOutput: string, jsFilePath: string, sourceMapFilePath: string, writeByteOrderMark: boolean, sourceFiles: SourceFile[]) {
if (compilerOptions.sourceMap && !compilerOptions.inlineSourceMap) {
writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap.getText(), /*writeByteOrderMark*/ false, sourceFiles);
}
if (sourceMapDataList) {
sourceMapDataList.push(sourceMap.getSourceMapData());
}
writeFile(host, emitterDiagnostics, jsFilePath, emitOutput, writeByteOrderMark, sourceFiles);
2014-07-13 01:04:16 +02:00
}
2015-03-24 00:16:29 +01:00
// Create a temporary variable with a unique unused name.
function createTempVariable(flags: TempFlags): Identifier {
2015-11-04 23:02:33 +01:00
const result = <Identifier>createSynthesizedNode(SyntaxKind.Identifier);
result.text = makeTempVariableName(flags);
return result;
2014-07-13 01:04:16 +02:00
}
function recordTempDeclaration(name: Identifier): void {
if (!tempVariables) {
tempVariables = [];
}
tempVariables.push(name);
2014-07-13 01:04:16 +02:00
}
function createAndRecordTempVariable(flags: TempFlags): Identifier {
2015-11-04 23:02:33 +01:00
const temp = createTempVariable(flags);
recordTempDeclaration(temp);
return temp;
}
function emitTempDeclarations(newLine: boolean) {
if (tempVariables) {
if (newLine) {
writeLine();
}
else {
write(" ");
}
write("var ");
emitCommaList(tempVariables);
write(";");
}
}
/** 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 */
function emitToken(tokenKind: SyntaxKind, startPos: number, emitFn?: () => void) {
const tokenStartPos = skipTrivia(currentText, startPos);
emitPos(tokenStartPos);
2015-11-04 23:02:33 +01:00
const tokenString = tokenToString(tokenKind);
if (emitFn) {
emitFn();
2014-07-13 01:04:16 +02:00
}
else {
write(tokenString);
}
const tokenEndPos = tokenStartPos + tokenString.length;
emitPos(tokenEndPos);
return tokenEndPos;
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
}
}
function emitParenthesizedIf(node: Node, parenthesized: boolean) {
if (parenthesized) {
write("(");
}
emit(node);
if (parenthesized) {
write(")");
}
}
function emitLinePreservingList(parent: Node, nodes: NodeArray<Node>, allowTrailingComma: boolean, spacesBetweenBraces: boolean) {
Debug.assert(nodes.length > 0);
increaseIndent();
if (nodeStartPositionsAreOnSameLine(parent, nodes[0])) {
if (spacesBetweenBraces) {
write(" ");
}
}
else {
writeLine();
}
2015-03-13 20:34:12 +01:00
for (let i = 0, n = nodes.length; i < n; i++) {
if (i) {
if (nodeEndIsOnSameLineAsNodeStart(nodes[i - 1], nodes[i])) {
write(", ");
}
else {
write(",");
writeLine();
}
}
emit(nodes[i]);
}
if (nodes.hasTrailingComma && allowTrailingComma) {
write(",");
}
decreaseIndent();
if (nodeEndPositionsAreOnSameLine(parent, lastOrUndefined(nodes))) {
if (spacesBetweenBraces) {
write(" ");
}
}
else {
writeLine();
}
}
2015-04-03 00:22:53 +02:00
function emitList<TNode extends Node>(nodes: TNode[], start: number, count: number, multiLine: boolean, trailingComma: boolean, leadingComma?: boolean, noTrailingNewLine?: boolean, emitNode?: (node: TNode) => void): number {
if (!emitNode) {
emitNode = emit;
}
2015-03-13 20:34:12 +01:00
for (let i = 0; i < count; i++) {
if (multiLine) {
2015-04-03 00:22:53 +02:00
if (i || leadingComma) {
write(",");
}
writeLine();
}
else {
2015-04-03 00:22:53 +02:00
if (i || leadingComma) {
write(", ");
}
}
2015-11-04 23:02:33 +01:00
const node = nodes[start + i];
// This emitting is to make sure we emit following comment properly
2015-08-11 02:34:39 +02:00
// ...(x, /*comment1*/ y)...
// ^ => node.pos
2015-08-11 02:34:39 +02:00
// "comment1" is not considered leading comment for "y" but rather
// considered as trailing comment of the previous node.
emitTrailingCommentsOfPosition(node.pos);
emitNode(node);
2015-04-03 00:22:53 +02:00
leadingComma = true;
}
if (trailingComma) {
write(",");
}
2015-04-03 00:22:53 +02:00
if (multiLine && !noTrailingNewLine) {
writeLine();
}
2015-04-03 00:22:53 +02:00
return count;
}
function emitCommaList(nodes: Node[]) {
if (nodes) {
2015-11-14 02:43:53 +01:00
emitList(nodes, 0, nodes.length, /*multiLine*/ false, /*trailingComma*/ false);
}
}
function emitLines(nodes: Node[]) {
emitLinesStartingAt(nodes, /*startIndex*/ 0);
}
function emitLinesStartingAt(nodes: Node[], startIndex: number): void {
2015-03-13 20:34:12 +01:00
for (let i = startIndex; i < nodes.length; i++) {
writeLine();
emit(nodes[i]);
2014-07-13 01:04:16 +02:00
}
}
function isBinaryOrOctalIntegerLiteral(node: LiteralLikeNode, text: string): boolean {
if (node.kind === SyntaxKind.NumericLiteral && text.length > 1) {
switch (text.charCodeAt(1)) {
case CharacterCodes.b:
case CharacterCodes.B:
case CharacterCodes.o:
case CharacterCodes.O:
return true;
}
}
return false;
}
function emitLiteral(node: LiteralExpression | TemplateLiteralFragment) {
2015-11-04 23:02:33 +01:00
const text = getLiteralText(node);
2015-04-08 09:14:23 +02:00
if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
writer.writeLiteral(text);
}
// For versions below ES6, emit binary & octal literals in their canonical decimal form.
else if (languageVersion < ScriptTarget.ES6 && isBinaryOrOctalIntegerLiteral(node, text)) {
write(node.text);
}
else {
write(text);
}
}
function getLiteralText(node: LiteralExpression | TemplateLiteralFragment) {
// Any template literal or string literal with an extended escape
// (e.g. "\u{0067}") will need to be downleveled as a escaped string literal.
if (languageVersion < ScriptTarget.ES6 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) {
return getQuotedEscapedLiteralText('"', node.text, '"');
}
2015-06-26 01:24:41 +02:00
2015-03-03 20:56:03 +01:00
// If we don't need to downlevel and we can reach the original source text using
// the node's parent reference, then simply get the text as it was originally written.
if (node.parent) {
return getTextOfNodeFromSourceText(currentText, node);
}
2015-06-26 01:24:41 +02:00
2015-03-03 20:56:03 +01:00
// If we can't reach the original source text, use the canonical form if it's a number,
// or an escaped quoted form of the original text if it's string-like.
switch (node.kind) {
case SyntaxKind.StringLiteral:
return getQuotedEscapedLiteralText('"', node.text, '"');
case SyntaxKind.NoSubstitutionTemplateLiteral:
return getQuotedEscapedLiteralText("`", node.text, "`");
case SyntaxKind.TemplateHead:
return getQuotedEscapedLiteralText("`", node.text, "${");
case SyntaxKind.TemplateMiddle:
return getQuotedEscapedLiteralText("}", node.text, "${");
case SyntaxKind.TemplateTail:
return getQuotedEscapedLiteralText("}", node.text, "`");
case SyntaxKind.NumericLiteral:
return node.text;
}
Debug.fail(`Literal kind '${node.kind}' not accounted for.`);
}
function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) {
return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote;
}
2015-01-23 15:44:21 +01:00
function emitDownlevelRawTemplateLiteral(node: LiteralExpression) {
2015-02-22 10:07:32 +01:00
// Find original source text, since we need to emit the raw strings of the tagged template.
// The raw strings contain the (escaped) strings of what the user wrote.
// Examples: `\n` is converted to "\\n", a template string with a newline to "\n".
let text = getTextOfNodeFromSourceText(currentText, node);
2015-06-26 01:24:41 +02:00
// text contains the original source, it will also contain quotes ("`"), dollar signs and braces ("${" and "}"),
// thus we need to remove those characters.
// First template piece starts with "`", others with "}"
// Last template piece ends with "`", others with "${"
2015-11-04 23:02:33 +01:00
const isLast = node.kind === SyntaxKind.NoSubstitutionTemplateLiteral || node.kind === SyntaxKind.TemplateTail;
text = text.substring(1, text.length - (isLast ? 1 : 2));
2015-06-26 01:24:41 +02:00
2015-02-22 10:07:32 +01:00
// Newline normalization:
// ES6 Spec 11.8.6.1 - Static Semantics of TV's and TRV's
// <CR><LF> and <CR> LineTerminatorSequences are normalized to <LF> for both TV and TRV.
2015-01-23 15:44:21 +01:00
text = text.replace(/\r\n?/g, "\n");
text = escapeString(text);
write(`"${text}"`);
2015-01-19 11:45:48 +01:00
}
function emitDownlevelTaggedTemplateArray(node: TaggedTemplateExpression, literalEmitter: (literal: LiteralExpression | TemplateLiteralFragment) => void) {
2015-02-22 10:07:32 +01:00
write("[");
if (node.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral) {
2015-02-24 06:29:21 +01:00
literalEmitter(<LiteralExpression>node.template);
}
else {
2015-02-24 06:29:21 +01:00
literalEmitter((<TemplateExpression>node.template).head);
2015-01-23 15:44:21 +01:00
forEach((<TemplateExpression>node.template).templateSpans, (child) => {
write(", ");
2015-02-24 06:29:21 +01:00
literalEmitter(child.literal);
});
}
2015-02-22 10:07:32 +01:00
write("]");
}
2015-02-22 10:07:32 +01:00
function emitDownlevelTaggedTemplate(node: TaggedTemplateExpression) {
2015-11-04 23:02:33 +01:00
const tempVariable = createAndRecordTempVariable(TempFlags.Auto);
2015-02-22 10:07:32 +01:00
write("(");
emit(tempVariable);
write(" = ");
emitDownlevelTaggedTemplateArray(node, emit);
write(", ");
emit(tempVariable);
2015-02-22 10:07:32 +01:00
write(".raw = ");
emitDownlevelTaggedTemplateArray(node, emitDownlevelRawTemplateLiteral);
write(", ");
emitParenthesizedIf(node.tag, needsParenthesisForPropertyAccessOrInvocation(node.tag));
write("(");
emit(tempVariable);
2015-06-26 01:24:41 +02:00
2015-01-05 21:22:12 +01:00
// Now we emit the expressions
if (node.template.kind === SyntaxKind.TemplateExpression) {
2015-01-23 15:44:21 +01:00
forEach((<TemplateExpression>node.template).templateSpans, templateSpan => {
write(", ");
2015-11-04 23:02:33 +01:00
const needsParens = templateSpan.expression.kind === SyntaxKind.BinaryExpression
2015-02-24 07:13:38 +01:00
&& (<BinaryExpression>templateSpan.expression).operatorToken.kind === SyntaxKind.CommaToken;
emitParenthesizedIf(templateSpan.expression, needsParens);
});
}
write("))");
}
function emitTemplateExpression(node: TemplateExpression): void {
// In ES6 mode and above, we can simply emit each portion of a template in order, but in
// ES3 & ES5 we must convert the template expression into a series of string concatenations.
2015-01-16 16:15:31 +01:00
if (languageVersion >= ScriptTarget.ES6) {
forEachChild(node, emit);
return;
}
2015-11-04 23:02:33 +01:00
const emitOuterParens = isExpression(node.parent)
&& templateNeedsParens(node, <Expression>node.parent);
2014-07-13 01:04:16 +02:00
if (emitOuterParens) {
write("(");
}
2015-03-13 20:34:12 +01:00
let headEmitted = false;
if (shouldEmitTemplateHead()) {
emitLiteral(node.head);
headEmitted = true;
}
2015-03-13 17:41:54 +01:00
for (let i = 0, n = node.templateSpans.length; i < n; i++) {
2015-11-04 23:02:33 +01:00
const templateSpan = node.templateSpans[i];
// Check if the expression has operands and binds its operands less closely than binary '+'.
// If it does, we need to wrap the expression in parentheses. Otherwise, something like
// `abc${ 1 << 2 }`
// becomes
// "abc" + 1 << 2 + ""
// which is really
// ("abc" + 1) << (2 + "")
// rather than
// "abc" + (1 << 2) + ""
2015-11-04 23:02:33 +01:00
const needsParens = templateSpan.expression.kind !== SyntaxKind.ParenthesizedExpression
&& comparePrecedenceToBinaryPlus(templateSpan.expression) !== Comparison.GreaterThan;
if (i > 0 || headEmitted) {
// If this is the first span and the head was not emitted, then this templateSpan's
// expression will be the first to be emitted. Don't emit the preceding ' + ' in that
// case.
write(" + ");
}
emitParenthesizedIf(templateSpan.expression, needsParens);
// Only emit if the literal is non-empty.
// The binary '+' operator is left-associative, so the first string concatenation
// with the head will force the result up to this point to be a string.
// Emitting a '+ ""' has no semantic effect for middles and tails.
if (templateSpan.literal.text.length !== 0) {
2015-06-26 01:24:41 +02:00
write(" + ");
emitLiteral(templateSpan.literal);
}
}
2014-07-13 01:04:16 +02:00
if (emitOuterParens) {
write(")");
}
2014-07-13 01:04:16 +02:00
function shouldEmitTemplateHead() {
// If this expression has an empty head literal and the first template span has a non-empty
// literal, then emitting the empty head literal is not necessary.
// `${ foo } and ${ bar }`
// can be emitted as
// foo + " and " + bar
// This is because it is only required that one of the first two operands in the emit
// output must be a string literal, so that the other operand and all following operands
// are forced into strings.
//
// If the first template span has an empty literal, then the head must still be emitted.
// `${ foo }${ bar }`
// must still be emitted as
// "" + foo + bar
// There is always atleast one templateSpan in this code path, since
// NoSubstitutionTemplateLiterals are directly emitted via emitLiteral()
Debug.assert(node.templateSpans.length !== 0);
return node.head.text.length !== 0 || node.templateSpans[0].literal.text.length === 0;
}
function templateNeedsParens(template: TemplateExpression, parent: Expression) {
switch (parent.kind) {
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return (<CallExpression>parent).expression === template;
case SyntaxKind.TaggedTemplateExpression:
2014-11-30 00:58:55 +01:00
case SyntaxKind.ParenthesizedExpression:
return false;
default:
return comparePrecedenceToBinaryPlus(parent) !== Comparison.LessThan;
}
2014-11-12 20:45:43 +01:00
}
/**
* Returns whether the expression has lesser, greater,
* or equal precedence to the binary '+' operator
*/
function comparePrecedenceToBinaryPlus(expression: Expression): Comparison {
// All binary expressions have lower precedence than '+' apart from '*', '/', and '%'
// which have greater precedence and '-' which has equal precedence.
// All unary operators have a higher precedence apart from yield.
// Arrow functions and conditionals have a lower precedence,
// although we convert the former into regular function expressions in ES5 mode,
// and in ES6 mode this function won't get called anyway.
//
// TODO (drosen): Note that we need to account for the upcoming 'yield' and
// spread ('...') unary operators that are anticipated for ES6.
switch (expression.kind) {
case SyntaxKind.BinaryExpression:
switch ((<BinaryExpression>expression).operatorToken.kind) {
case SyntaxKind.AsteriskToken:
case SyntaxKind.SlashToken:
case SyntaxKind.PercentToken:
return Comparison.GreaterThan;
case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken:
return Comparison.EqualTo;
default:
return Comparison.LessThan;
}
case SyntaxKind.YieldExpression:
case SyntaxKind.ConditionalExpression:
return Comparison.LessThan;
default:
return Comparison.GreaterThan;
}
}
2014-07-13 01:04:16 +02:00
}
function emitTemplateSpan(span: TemplateSpan) {
emit(span.expression);
emit(span.literal);
}
function jsxEmitReact(node: JsxElement | JsxSelfClosingElement) {
2015-06-18 23:01:49 +02:00
/// Emit a tag name, which is either '"div"' for lower-cased names, or
/// 'Div' for upper-cased or dotted names
function emitTagName(name: LeftHandSideExpression) {
2015-06-26 23:18:51 +02:00
if (name.kind === SyntaxKind.Identifier && isIntrinsicJsxName((<Identifier>name).text)) {
write('"');
2015-06-26 23:18:51 +02:00
emit(name);
write('"');
2015-06-18 23:01:49 +02:00
}
else {
emit(name);
}
}
/// Emit an attribute name, which is quoted if it needs to be quoted. Because
/// these emit into an object literal property name, we don't need to be worried
/// about keywords, just non-identifier characters
function emitAttributeName(name: Identifier) {
2015-11-12 22:37:03 +01:00
if (/^[A-Za-z_]\w*$/.test(name.text)) {
2015-06-18 23:01:49 +02:00
emit(name);
}
else {
write('"');
2015-06-18 23:01:49 +02:00
emit(name);
write('"');
2015-06-18 23:01:49 +02:00
}
}
/// Emit an name/value pair for an attribute (e.g. "x: 3")
function emitJsxAttribute(node: JsxAttribute) {
emitAttributeName(node.name);
2015-06-29 18:07:12 +02:00
write(": ");
2015-06-19 03:37:32 +02:00
if (node.initializer) {
emit(node.initializer);
}
else {
2015-06-29 18:07:12 +02:00
write("true");
2015-06-19 03:37:32 +02:00
}
2015-06-18 23:01:49 +02:00
}
2015-06-22 22:55:09 +02:00
function emitJsxElement(openingNode: JsxOpeningLikeElement, children?: JsxChild[]) {
2015-11-04 23:02:33 +01:00
const syntheticReactRef = <Identifier>createSynthesizedNode(SyntaxKind.Identifier);
2016-01-07 22:59:25 +01:00
syntheticReactRef.text = compilerOptions.reactNamespace ? compilerOptions.reactNamespace : "React";
syntheticReactRef.parent = openingNode;
2015-06-18 23:01:49 +02:00
// Call React.createElement(tag, ...
emitLeadingComments(openingNode);
emitExpressionIdentifier(syntheticReactRef);
write(".createElement(");
2015-06-18 23:01:49 +02:00
emitTagName(openingNode.tagName);
2015-06-29 18:07:12 +02:00
write(", ");
2015-06-18 23:01:49 +02:00
// Attribute list
if (openingNode.attributes.length === 0) {
2015-06-29 18:07:12 +02:00
// When there are no attributes, React wants "null"
write("null");
2015-06-18 23:01:49 +02:00
}
else {
// Either emit one big object literal (no spread attribs), or
// a call to the __assign helper
2015-11-04 23:02:33 +01:00
const attrs = openingNode.attributes;
2015-06-29 18:37:40 +02:00
if (forEach(attrs, attr => attr.kind === SyntaxKind.JsxSpreadAttribute)) {
write("__assign(");
2015-06-18 23:01:49 +02:00
let haveOpenedObjectLiteral = false;
2015-06-22 22:55:09 +02:00
for (let i = 0; i < attrs.length; i++) {
2015-06-18 23:01:49 +02:00
if (attrs[i].kind === SyntaxKind.JsxSpreadAttribute) {
// If this is the first argument, we need to emit a {} as the first argument
2015-06-22 22:55:09 +02:00
if (i === 0) {
2015-06-29 18:07:12 +02:00
write("{}, ");
}
2015-06-18 23:01:49 +02:00
if (haveOpenedObjectLiteral) {
2015-06-29 18:07:12 +02:00
write("}");
2015-06-18 23:01:49 +02:00
haveOpenedObjectLiteral = false;
}
2015-06-22 22:55:09 +02:00
if (i > 0) {
2015-06-29 18:07:12 +02:00
write(", ");
2015-06-22 22:55:09 +02:00
}
2015-06-18 23:01:49 +02:00
emit((<JsxSpreadAttribute>attrs[i]).expression);
}
else {
Debug.assert(attrs[i].kind === SyntaxKind.JsxAttribute);
if (haveOpenedObjectLiteral) {
2015-06-29 18:07:12 +02:00
write(", ");
2015-06-18 23:01:49 +02:00
}
else {
haveOpenedObjectLiteral = true;
2015-06-22 22:55:09 +02:00
if (i > 0) {
2015-06-29 18:07:12 +02:00
write(", ");
2015-06-22 22:55:09 +02:00
}
2015-06-29 18:07:12 +02:00
write("{");
2015-06-18 23:01:49 +02:00
}
emitJsxAttribute(<JsxAttribute>attrs[i]);
}
}
2015-06-29 18:07:12 +02:00
if (haveOpenedObjectLiteral) write("}");
2015-06-18 23:01:49 +02:00
2015-06-29 18:07:12 +02:00
write(")"); // closing paren to React.__spread(
2015-06-18 23:01:49 +02:00
}
else {
// One object literal with all the attributes in them
2015-06-29 18:07:12 +02:00
write("{");
2016-01-16 23:05:46 +01:00
for (let i = 0, n = attrs.length; i < n; i++) {
2015-06-22 22:55:09 +02:00
if (i > 0) {
2015-06-29 18:07:12 +02:00
write(", ");
2015-06-22 22:55:09 +02:00
}
2015-06-18 23:01:49 +02:00
emitJsxAttribute(<JsxAttribute>attrs[i]);
}
2015-06-29 18:07:12 +02:00
write("}");
2015-06-18 23:01:49 +02:00
}
}
// Children
if (children) {
2016-01-29 08:33:14 +01:00
let firstChild: JsxChild;
let multipleEmittableChildren = false;
2015-06-18 23:01:49 +02:00
2016-01-29 08:33:14 +01:00
for (let i = 0, n = children.length; i < n; i++) {
const jsxChild = children[i];
if (isJsxChildEmittable(jsxChild)) {
// we need to decide whether to emit in single line or multiple lines as indented list
// store firstChild reference, if we see another emittable child, then emit accordingly
if (!firstChild) {
write(", ");
firstChild = jsxChild;
2015-06-26 23:18:51 +02:00
}
else {
// more than one emittable child, emit indented list
2016-01-29 08:33:14 +01:00
if (!multipleEmittableChildren) {
multipleEmittableChildren = true;
increaseIndent();
writeLine();
emit(firstChild);
}
2015-06-18 23:01:49 +02:00
write(", ");
writeLine();
emit(jsxChild);
2015-06-26 23:18:51 +02:00
}
}
}
if (multipleEmittableChildren) {
decreaseIndent();
}
else if (firstChild) {
if (firstChild.kind !== SyntaxKind.JsxElement && firstChild.kind !== SyntaxKind.JsxSelfClosingElement) {
emit(firstChild);
2015-06-26 23:18:51 +02:00
}
else {
// If the only child is jsx element, put it on a new indented line
2016-01-27 07:59:34 +01:00
increaseIndent();
writeLine();
emit(firstChild);
writeLine();
2016-01-27 07:59:34 +01:00
decreaseIndent();
2015-06-22 22:55:09 +02:00
}
2015-06-18 23:01:49 +02:00
}
}
// Closing paren
2015-06-29 18:07:12 +02:00
write(")"); // closes "React.createElement("
2015-06-18 23:01:49 +02:00
emitTrailingComments(openingNode);
}
if (node.kind === SyntaxKind.JsxElement) {
emitJsxElement((<JsxElement>node).openingElement, (<JsxElement>node).children);
}
else {
Debug.assert(node.kind === SyntaxKind.JsxSelfClosingElement);
emitJsxElement(<JsxSelfClosingElement>node);
}
}
function jsxEmitPreserve(node: JsxElement | JsxSelfClosingElement) {
2015-06-18 23:01:49 +02:00
function emitJsxAttribute(node: JsxAttribute) {
emit(node.name);
if (node.initializer) {
write("=");
emit(node.initializer);
}
2015-06-18 23:01:49 +02:00
}
function emitJsxSpreadAttribute(node: JsxSpreadAttribute) {
2015-06-29 18:07:12 +02:00
write("{...");
2015-06-18 23:01:49 +02:00
emit(node.expression);
2015-06-29 18:07:12 +02:00
write("}");
2015-06-18 23:01:49 +02:00
}
function emitAttributes(attribs: NodeArray<JsxAttribute | JsxSpreadAttribute>) {
2015-06-22 22:55:09 +02:00
for (let i = 0, n = attribs.length; i < n; i++) {
if (i > 0) {
2015-06-29 18:07:12 +02:00
write(" ");
2015-06-22 22:55:09 +02:00
}
2015-06-18 23:01:49 +02:00
if (attribs[i].kind === SyntaxKind.JsxSpreadAttribute) {
emitJsxSpreadAttribute(<JsxSpreadAttribute>attribs[i]);
}
else {
Debug.assert(attribs[i].kind === SyntaxKind.JsxAttribute);
emitJsxAttribute(<JsxAttribute>attribs[i]);
}
}
}
function emitJsxOpeningOrSelfClosingElement(node: JsxOpeningElement | JsxSelfClosingElement) {
2015-06-29 18:07:12 +02:00
write("<");
2015-06-18 23:01:49 +02:00
emit(node.tagName);
if (node.attributes.length > 0 || (node.kind === SyntaxKind.JsxSelfClosingElement)) {
2015-06-29 18:07:12 +02:00
write(" ");
2015-06-18 23:01:49 +02:00
}
emitAttributes(node.attributes);
if (node.kind === SyntaxKind.JsxSelfClosingElement) {
2015-06-29 18:07:12 +02:00
write("/>");
2015-06-18 23:01:49 +02:00
}
else {
2015-06-29 18:07:12 +02:00
write(">");
2015-06-18 23:01:49 +02:00
}
}
function emitJsxClosingElement(node: JsxClosingElement) {
2015-06-29 18:07:12 +02:00
write("</");
2015-06-18 23:01:49 +02:00
emit(node.tagName);
2015-06-29 18:07:12 +02:00
write(">");
2015-06-18 23:01:49 +02:00
}
function emitJsxElement(node: JsxElement) {
emitJsxOpeningOrSelfClosingElement(node.openingElement);
2016-01-16 23:05:46 +01:00
for (let i = 0, n = node.children.length; i < n; i++) {
2015-06-18 23:01:49 +02:00
emit(node.children[i]);
}
emitJsxClosingElement(node.closingElement);
}
if (node.kind === SyntaxKind.JsxElement) {
emitJsxElement(<JsxElement>node);
}
else {
Debug.assert(node.kind === SyntaxKind.JsxSelfClosingElement);
emitJsxOpeningOrSelfClosingElement(<JsxSelfClosingElement>node);
}
}
// 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.
// For example, this is utilized when feeding in a result to Object.defineProperty.
function emitExpressionForPropertyName(node: DeclarationName) {
Debug.assert(node.kind !== SyntaxKind.BindingElement);
if (node.kind === SyntaxKind.StringLiteral) {
emitLiteral(<LiteralExpression>node);
}
2014-11-18 00:53:03 +01:00
else if (node.kind === SyntaxKind.ComputedPropertyName) {
// if this is a decorated computed property, we will need to capture the result
2015-07-09 23:44:47 +02:00
// of the property expression so that we can apply decorators later. This is to ensure
// we don't introduce unintended side effects:
//
// class C {
// [_a = x]() { }
// }
2015-03-24 22:16:52 +01:00
//
// The emit for the decorated computed property decorator is:
//
2015-09-11 01:27:35 +02:00
// __decorate([dec], C.prototype, _a, Object.getOwnPropertyDescriptor(C.prototype, _a));
//
if (nodeIsDecorated(node.parent)) {
if (!computedPropertyNamesToGeneratedNames) {
computedPropertyNamesToGeneratedNames = [];
}
2015-04-10 21:10:38 +02:00
2015-04-07 22:17:57 +02:00
let generatedName = computedPropertyNamesToGeneratedNames[getNodeId(node)];
if (generatedName) {
// we have already generated a variable for this node, write that value instead.
write(generatedName);
return;
}
2015-03-18 01:09:39 +01:00
generatedName = createAndRecordTempVariable(TempFlags.Auto).text;
2015-04-07 22:17:57 +02:00
computedPropertyNamesToGeneratedNames[getNodeId(node)] = generatedName;
write(generatedName);
write(" = ");
}
2014-11-18 00:53:03 +01:00
emit((<ComputedPropertyName>node).expression);
}
else {
write('"');
if (node.kind === SyntaxKind.NumericLiteral) {
write((<LiteralExpression>node).text);
}
else {
writeTextOfNode(currentText, node);
}
write('"');
}
2014-07-13 01:04:16 +02:00
}
function isExpressionIdentifier(node: Node): boolean {
2015-11-04 23:02:33 +01:00
const parent = node.parent;
switch (parent.kind) {
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.AsExpression:
case SyntaxKind.AwaitExpression:
case SyntaxKind.BinaryExpression:
case SyntaxKind.CallExpression:
case SyntaxKind.CaseClause:
case SyntaxKind.ComputedPropertyName:
case SyntaxKind.ConditionalExpression:
case SyntaxKind.Decorator:
case SyntaxKind.DeleteExpression:
case SyntaxKind.DoStatement:
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.ExportAssignment:
case SyntaxKind.ExpressionStatement:
case SyntaxKind.ExpressionWithTypeArguments:
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
case SyntaxKind.IfStatement:
2015-12-07 20:57:54 +01:00
case SyntaxKind.JsxClosingElement:
2015-06-18 23:01:49 +02:00
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.JsxOpeningElement:
2015-07-29 00:31:39 +02:00
case SyntaxKind.JsxSpreadAttribute:
case SyntaxKind.JsxExpression:
case SyntaxKind.NewExpression:
case SyntaxKind.NonNullExpression:
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.PostfixUnaryExpression:
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.ReturnStatement:
case SyntaxKind.ShorthandPropertyAssignment:
case SyntaxKind.SpreadElementExpression:
case SyntaxKind.SwitchStatement:
case SyntaxKind.TaggedTemplateExpression:
case SyntaxKind.TemplateSpan:
case SyntaxKind.ThrowStatement:
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.VoidExpression:
case SyntaxKind.WhileStatement:
case SyntaxKind.WithStatement:
case SyntaxKind.YieldExpression:
return true;
case SyntaxKind.BindingElement:
case SyntaxKind.EnumMember:
case SyntaxKind.Parameter:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.VariableDeclaration:
return (<BindingElement | EnumMember | ParameterDeclaration | PropertyAssignment | PropertyDeclaration | VariableDeclaration>parent).initializer === node;
case SyntaxKind.PropertyAccessExpression:
return (<ExpressionStatement>parent).expression === node;
case SyntaxKind.ArrowFunction:
case SyntaxKind.FunctionExpression:
return (<FunctionLikeDeclaration>parent).body === node;
case SyntaxKind.ImportEqualsDeclaration:
return (<ImportEqualsDeclaration>parent).moduleReference === node;
case SyntaxKind.QualifiedName:
return (<QualifiedName>parent).left === node;
}
return false;
2014-07-13 01:04:16 +02:00
}
function emitExpressionIdentifier(node: Identifier) {
2015-11-04 23:02:33 +01:00
const container = resolver.getReferencedExportContainer(node);
if (container) {
if (container.kind === SyntaxKind.SourceFile) {
// Identifier references module export
2015-09-17 22:26:04 +02:00
if (modulekind !== ModuleKind.ES6 && modulekind !== ModuleKind.System) {
write("exports.");
}
}
else {
// Identifier references namespace export
write(getGeneratedNameForNode(container));
write(".");
}
}
else {
if (modulekind !== ModuleKind.ES6) {
2015-11-04 23:02:33 +01:00
const declaration = resolver.getReferencedImportDeclaration(node);
if (declaration) {
if (declaration.kind === SyntaxKind.ImportClause) {
// Identifier references default import
write(getGeneratedNameForNode(<ImportDeclaration>declaration.parent));
write(languageVersion === ScriptTarget.ES3 ? '["default"]' : ".default");
return;
2015-09-17 22:03:47 +02:00
}
else if (declaration.kind === SyntaxKind.ImportSpecifier) {
// Identifier references named import
write(getGeneratedNameForNode(<ImportDeclaration>declaration.parent.parent.parent));
const name = (<ImportSpecifier>declaration).propertyName || (<ImportSpecifier>declaration).name;
const identifier = getTextOfNodeFromSourceText(currentText, name);
if (languageVersion === ScriptTarget.ES3 && identifier === "default") {
write('["default"]');
}
else {
write(".");
write(identifier);
}
return;
}
}
}
if (languageVersion < ScriptTarget.ES6) {
const declaration = resolver.getReferencedDeclarationWithCollidingName(node);
if (declaration) {
write(getGeneratedNameForNode(declaration.name));
return;
}
}
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) {
// Due to the emit for class decorators, any reference to the class from inside of the class body
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
// behavior of class names in ES6.
const declaration = resolver.getReferencedValueDeclaration(node);
2016-01-29 00:25:42 +01:00
if (declaration) {
const classAlias = decoratedClassAliases[getNodeId(declaration)];
if (classAlias !== undefined) {
write(classAlias);
return;
}
}
}
}
if (nodeIsSynthesized(node)) {
write(node.text);
}
else {
writeTextOfNode(currentText, node);
}
}
function isNameOfNestedBlockScopedRedeclarationOrCapturedBinding(node: Identifier) {
if (languageVersion < ScriptTarget.ES6) {
2015-11-04 23:02:33 +01:00
const parent = node.parent;
switch (parent.kind) {
case SyntaxKind.BindingElement:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.VariableDeclaration:
return (<Declaration>parent).name === node && resolver.isDeclarationWithCollidingName(<Declaration>parent);
}
}
return false;
}
function getClassExpressionInPropertyAccessInStaticPropertyDeclaration(node: Identifier) {
if (languageVersion >= ScriptTarget.ES6) {
let parent = node.parent;
if (parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>parent).expression === node) {
parent = parent.parent;
while (parent && parent.kind !== SyntaxKind.PropertyDeclaration) {
parent = parent.parent;
}
return parent && parent.kind === SyntaxKind.PropertyDeclaration && (parent.flags & NodeFlags.Static) !== 0 &&
parent.parent.kind === SyntaxKind.ClassExpression ? parent.parent : undefined;
}
}
return undefined;
}
function emitIdentifier(node: Identifier) {
if (convertedLoopState) {
if (node.text == "arguments" && resolver.isArgumentsLocalBinding(node)) {
// in converted loop body arguments cannot be used directly.
2015-11-04 23:02:33 +01:00
const name = convertedLoopState.argumentsName || (convertedLoopState.argumentsName = makeUniqueName("arguments"));
write(name);
return;
}
}
if (!node.parent) {
write(node.text);
2014-07-13 01:04:16 +02:00
}
else if (isExpressionIdentifier(node)) {
const classExpression = getClassExpressionInPropertyAccessInStaticPropertyDeclaration(node);
if (classExpression) {
const declaration = resolver.getReferencedValueDeclaration(node);
if (declaration === classExpression) {
write(getGeneratedNameForNode(declaration.name));
return;
}
}
emitExpressionIdentifier(node);
}
else if (isNameOfNestedBlockScopedRedeclarationOrCapturedBinding(node)) {
write(getGeneratedNameForNode(node));
}
else if (nodeIsSynthesized(node)) {
write(node.text);
}
else {
writeTextOfNode(currentText, node);
}
2014-07-13 01:04:16 +02:00
}
function emitThis(node: Node) {
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.LexicalThis) {
write("_this");
2014-07-13 01:04:16 +02:00
}
else if (convertedLoopState) {
write(convertedLoopState.thisName || (convertedLoopState.thisName = makeUniqueName("this")));
}
else {
write("this");
2014-07-13 01:04:16 +02:00
}
}
2014-07-13 01:04:16 +02:00
function emitSuper(node: Node) {
2015-03-13 00:34:06 +01:00
if (languageVersion >= ScriptTarget.ES6) {
write("super");
}
else {
2015-11-04 23:02:33 +01:00
const flags = resolver.getNodeCheckFlags(node);
2015-03-13 00:34:06 +01:00
if (flags & NodeCheckFlags.SuperInstance) {
write("_super.prototype");
}
else {
2015-03-16 00:29:41 +01:00
write("_super");
2015-03-13 00:34:06 +01:00
}
}
2014-07-13 01:04:16 +02:00
}
function emitObjectBindingPattern(node: BindingPattern) {
write("{ ");
2015-11-04 23:02:33 +01:00
const elements = node.elements;
emitList(elements, 0, elements.length, /*multiLine*/ false, /*trailingComma*/ elements.hasTrailingComma);
write(" }");
2014-07-13 01:04:16 +02:00
}
function emitArrayBindingPattern(node: BindingPattern) {
write("[");
2015-11-04 23:02:33 +01:00
const elements = node.elements;
emitList(elements, 0, elements.length, /*multiLine*/ false, /*trailingComma*/ elements.hasTrailingComma);
write("]");
2014-07-13 01:04:16 +02:00
}
function emitBindingElement(node: BindingElement) {
if (node.propertyName) {
emit(node.propertyName);
write(": ");
}
if (node.dotDotDotToken) {
write("...");
}
if (isBindingPattern(node.name)) {
emit(node.name);
}
else {
emitModuleMemberName(node);
}
emitOptional(" = ", node.initializer);
}
function emitSpreadElementExpression(node: SpreadElementExpression) {
write("...");
emit((<SpreadElementExpression>node).expression);
}
function emitYieldExpression(node: YieldExpression) {
write(tokenToString(SyntaxKind.YieldKeyword));
if (node.asteriskToken) {
write("*");
}
if (node.expression) {
write(" ");
emit(node.expression);
}
}
2015-05-07 02:33:58 +02:00
function emitAwaitExpression(node: AwaitExpression) {
2015-11-04 23:02:33 +01:00
const needsParenthesis = needsParenthesisForAwaitExpressionAsYield(node);
2015-05-07 02:33:58 +02:00
if (needsParenthesis) {
write("(");
}
write(tokenToString(SyntaxKind.YieldKeyword));
write(" ");
emit(node.expression);
if (needsParenthesis) {
write(")");
}
}
function needsParenthesisForAwaitExpressionAsYield(node: AwaitExpression) {
if (node.parent.kind === SyntaxKind.BinaryExpression && !isAssignmentOperator((<BinaryExpression>node.parent).operatorToken.kind)) {
return true;
2015-05-07 02:33:58 +02:00
}
else if (node.parent.kind === SyntaxKind.ConditionalExpression && (<ConditionalExpression>node.parent).condition === node) {
return true;
}
Merge master 08/22 (#10487) * Add test case for #8229 * Do not report errors during contextual typecheck Fixes #8229 * Handle the scenario when let [a=undefined]=[] * Instantiate contextual this parameters if needed * Test that contextually typed generic this parameters are instantiated * Don't allow `.ts` to appear in an import * Add specific error message for unwanted '.ts' extension * Allow `await` in a simple unary expression * More tests * Forbid `await await` * Allow `await await` * Improve error message * Don't allow ".d.ts" extension in an import either. * Rename 'find' functions * Assign and instantiate contextual this type if not present * JSDoc supports null, undefined and never types * Update baselines in jsDocParsing unit tests * Return non-JsDocComment children ... to make syntactic classification work * Move supportedTypescriptExtensionsWithDtsFirst next to supportedTypeScriptExtensions and rename * Fix comment * Treat special property access symbol differently ... when retriving documentation * Fix tests * Update shim version to be 2.1 (#10424) * Check return code paths on getters (#10102) * Check return paths on getters * Remove TODO comment * Remove extraneous arguments from harness's runBaseline (#10419) * Remove extraneous arguments from runBaseline * Address comments from @yuit * Remove needless call to basename * Refactor baseliners out of compiler runner (#10440) * CR feedback * fix broken tests * Pass in baselineOpts into types baselines so that RWC baselines can be written to internal folder (#10443) * Add more test for 10426 * routine update of dom libs * Add test for jsdoc syntactic classification for function declaration * Simplify implementation * Tolerate certain errors in tsconfig.json * Add test for configFile error tolerance * Use TS parser to tolerate more errors in tsconfig.json * Implement tuple types as type references to synthesized generic types * Add comments + minor changes * Accept new baselines * Add .types extension * Properly guard for undefined in getTypeReferenceArity * Add jsdoc nullable union test case to fourslash * Fix class/interface merging issue + lint error * Allow "typings" in a package.json to be missing its extension (but also allow it to have an extension) * Contextually type this in getDeclFromSig, not checkThisExpr * Update parser comment with es7 grammar (#10459) * Use ES7 term of ExponentiationExpression * Update timeout for mac OS * Address PR: add space * allowSyntheticDefaultImports resolves to modules instead of variables Fixes #10429 by improving the fix in #10096 * Rename getContextuallyTypedThisParameter to getContextualThisParameter * Fix 10472: Invalid emitted code for await expression (#10483) * Properly emit await expression with yield expression * Add tests and update baselines * Move parsing await expression into parse unary-expression * Update incorrect comment
2016-08-23 02:04:49 +02:00
else if (node.parent.kind === SyntaxKind.PrefixUnaryExpression || node.parent.kind === SyntaxKind.DeleteExpression ||
node.parent.kind === SyntaxKind.TypeOfExpression || node.parent.kind === SyntaxKind.VoidExpression) {
return true;
}
2015-05-07 02:33:58 +02:00
return false;
}
function needsParenthesisForPropertyAccessOrInvocation(node: Expression) {
switch (node.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.CallExpression:
case SyntaxKind.ParenthesizedExpression:
2014-12-17 00:47:22 +01:00
// This list is not exhaustive and only includes those cases that are relevant
// to the check in emitArrayLiteral. More cases can be added as needed.
return false;
}
return true;
}
2015-05-16 10:42:49 +02:00
function emitListWithSpread(elements: Expression[], needsUniqueCopy: boolean, multiLine: boolean, trailingComma: boolean, useConcat: boolean) {
2015-03-13 20:34:12 +01:00
let pos = 0;
let group = 0;
2015-11-04 23:02:33 +01:00
const length = elements.length;
while (pos < length) {
// Emit using the pattern <group0>.concat(<group1>, <group2>, ...)
2015-05-17 03:27:20 +02:00
if (group === 1 && useConcat) {
write(".concat(");
}
2015-05-17 03:27:20 +02:00
else if (group > 0) {
write(", ");
}
2015-03-13 20:34:12 +01:00
let e = elements[pos];
if (e.kind === SyntaxKind.SpreadElementExpression) {
e = (<SpreadElementExpression>e).expression;
emitParenthesizedIf(e, /*parenthesized*/ group === 0 && needsParenthesisForPropertyAccessOrInvocation(e));
pos++;
2015-05-15 03:18:24 +02:00
if (pos === length && group === 0 && needsUniqueCopy && e.kind !== SyntaxKind.ArrayLiteralExpression) {
write(".slice()");
}
}
else {
2015-03-13 20:34:12 +01:00
let i = pos;
while (i < length && elements[i].kind !== SyntaxKind.SpreadElementExpression) {
i++;
}
write("[");
if (multiLine) {
increaseIndent();
}
2015-02-06 16:39:11 +01:00
emitList(elements, pos, i - pos, multiLine, trailingComma && i === length);
if (multiLine) {
decreaseIndent();
}
write("]");
pos = i;
}
group++;
}
if (group > 1) {
2015-06-26 01:24:41 +02:00
if (useConcat) {
2015-05-16 10:42:49 +02:00
write(")");
}
}
}
function isSpreadElementExpression(node: Node) {
return node.kind === SyntaxKind.SpreadElementExpression;
}
function emitArrayLiteral(node: ArrayLiteralExpression) {
2015-11-04 23:02:33 +01:00
const elements = node.elements;
if (elements.length === 0) {
write("[]");
}
else if (languageVersion >= ScriptTarget.ES6 || !forEach(elements, isSpreadElementExpression)) {
write("[");
2015-11-14 02:43:53 +01:00
emitLinePreservingList(node, node.elements, elements.hasTrailingComma, /*spacesBetweenBraces*/ false);
write("]");
}
2015-02-06 16:39:11 +01:00
else {
emitListWithSpread(elements, /*needsUniqueCopy*/ true, /*multiLine*/ node.multiLine,
2015-05-16 10:42:49 +02:00
/*trailingComma*/ elements.hasTrailingComma, /*useConcat*/ true);
2015-02-06 16:39:11 +01:00
}
}
function emitObjectLiteralBody(node: ObjectLiteralExpression, numElements: number): void {
if (numElements === 0) {
write("{}");
return;
}
write("{");
if (numElements > 0) {
2015-11-04 23:02:33 +01:00
const properties = node.properties;
// If we are not doing a downlevel transformation for object literals,
// then try to preserve the original shape of the object literal.
// Otherwise just try to preserve the formatting.
if (numElements === properties.length) {
2015-11-14 02:43:53 +01:00
emitLinePreservingList(node, properties, /*allowTrailingComma*/ languageVersion >= ScriptTarget.ES5, /*spacesBetweenBraces*/ true);
}
else {
const multiLine = node.multiLine;
if (!multiLine) {
write(" ");
}
else {
increaseIndent();
}
emitList(properties, 0, numElements, /*multiLine*/ multiLine, /*trailingComma*/ false);
if (!multiLine) {
write(" ");
}
else {
decreaseIndent();
}
}
}
write("}");
}
function emitDownlevelObjectLiteralWithComputedProperties(node: ObjectLiteralExpression, firstComputedPropertyIndex: number) {
const multiLine = node.multiLine;
2015-11-04 23:02:33 +01:00
const properties = node.properties;
write("(");
if (multiLine) {
increaseIndent();
}
// For computed properties, we need to create a unique handle to the object
// literal so we can modify it without risking internal assignments tainting the object.
2015-11-04 23:02:33 +01:00
const tempVar = createAndRecordTempVariable(TempFlags.Auto);
// Write out the first non-computed properties
// (or all properties if none of them are computed),
// then emit the rest through indexing on the temp variable.
2015-06-26 01:24:41 +02:00
emit(tempVar);
write(" = ");
emitObjectLiteralBody(node, firstComputedPropertyIndex);
Merge branch 'master' into unrewrite Conflicts: src/compiler/emitter.ts tests/baselines/reference/ES5SymbolProperty1.js tests/baselines/reference/FunctionDeclaration8_es6.js tests/baselines/reference/FunctionDeclaration9_es6.js tests/baselines/reference/FunctionPropertyAssignments5_es6.js tests/baselines/reference/computedPropertyNames10_ES5.js tests/baselines/reference/computedPropertyNames11_ES5.js tests/baselines/reference/computedPropertyNames18_ES5.js tests/baselines/reference/computedPropertyNames19_ES5.js tests/baselines/reference/computedPropertyNames1_ES5.js tests/baselines/reference/computedPropertyNames20_ES5.js tests/baselines/reference/computedPropertyNames22_ES5.js tests/baselines/reference/computedPropertyNames23_ES5.js tests/baselines/reference/computedPropertyNames25_ES5.js tests/baselines/reference/computedPropertyNames26_ES5.js tests/baselines/reference/computedPropertyNames28_ES5.js tests/baselines/reference/computedPropertyNames29_ES5.js tests/baselines/reference/computedPropertyNames30_ES5.js tests/baselines/reference/computedPropertyNames31_ES5.js tests/baselines/reference/computedPropertyNames33_ES5.js tests/baselines/reference/computedPropertyNames34_ES5.js tests/baselines/reference/computedPropertyNames46_ES5.js tests/baselines/reference/computedPropertyNames47_ES5.js tests/baselines/reference/computedPropertyNames48_ES5.js tests/baselines/reference/computedPropertyNames49_ES5.js tests/baselines/reference/computedPropertyNames4_ES5.js tests/baselines/reference/computedPropertyNames50_ES5.js tests/baselines/reference/computedPropertyNames5_ES5.js tests/baselines/reference/computedPropertyNames6_ES5.js tests/baselines/reference/computedPropertyNames7_ES5.js tests/baselines/reference/computedPropertyNames8_ES5.js tests/baselines/reference/computedPropertyNames9_ES5.js tests/baselines/reference/computedPropertyNamesContextualType10_ES5.js tests/baselines/reference/computedPropertyNamesContextualType1_ES5.js tests/baselines/reference/computedPropertyNamesContextualType2_ES5.js tests/baselines/reference/computedPropertyNamesContextualType3_ES5.js tests/baselines/reference/computedPropertyNamesContextualType4_ES5.js tests/baselines/reference/computedPropertyNamesContextualType5_ES5.js tests/baselines/reference/computedPropertyNamesContextualType6_ES5.js tests/baselines/reference/computedPropertyNamesContextualType7_ES5.js tests/baselines/reference/computedPropertyNamesContextualType8_ES5.js tests/baselines/reference/computedPropertyNamesContextualType9_ES5.js tests/baselines/reference/computedPropertyNamesDeclarationEmit5_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js.map tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.sourcemap.txt tests/baselines/reference/parserES5ComputedPropertyName2.js tests/baselines/reference/parserES5ComputedPropertyName3.js tests/baselines/reference/parserES5ComputedPropertyName4.js tests/baselines/reference/privateIndexer2.js
2015-04-06 23:19:28 +02:00
for (let i = firstComputedPropertyIndex, n = properties.length; i < n; i++) {
writeComma();
2015-11-04 23:02:33 +01:00
const property = properties[i];
2015-06-26 01:24:41 +02:00
emitStart(property);
if (property.kind === SyntaxKind.GetAccessor || property.kind === SyntaxKind.SetAccessor) {
// TODO (drosen): Reconcile with 'emitMemberFunctions'.
2015-11-04 23:02:33 +01:00
const accessors = getAllAccessorDeclarations(node.properties, <AccessorDeclaration>property);
if (property !== accessors.firstAccessor) {
continue;
}
write("Object.defineProperty(");
emit(tempVar);
write(", ");
emitStart(property.name);
emitExpressionForPropertyName(property.name);
emitEnd(property.name);
write(", {");
increaseIndent();
if (accessors.getAccessor) {
2015-06-26 01:24:41 +02:00
writeLine();
emitLeadingComments(accessors.getAccessor);
write("get: ");
emitStart(accessors.getAccessor);
write("function ");
emitSignatureAndBody(accessors.getAccessor);
emitEnd(accessors.getAccessor);
emitTrailingComments(accessors.getAccessor);
write(",");
}
if (accessors.setAccessor) {
writeLine();
emitLeadingComments(accessors.setAccessor);
write("set: ");
emitStart(accessors.setAccessor);
write("function ");
emitSignatureAndBody(accessors.setAccessor);
emitEnd(accessors.setAccessor);
emitTrailingComments(accessors.setAccessor);
write(",");
}
writeLine();
write("enumerable: true,");
writeLine();
write("configurable: true");
decreaseIndent();
writeLine();
write("})");
emitEnd(property);
}
else {
emitLeadingComments(property);
emitStart(property.name);
emit(tempVar);
emitMemberAccessForPropertyName(property.name);
emitEnd(property.name);
write(" = ");
if (property.kind === SyntaxKind.PropertyAssignment) {
emit((<PropertyAssignment>property).initializer);
}
else if (property.kind === SyntaxKind.ShorthandPropertyAssignment) {
emitExpressionIdentifier((<ShorthandPropertyAssignment>property).name);
}
else if (property.kind === SyntaxKind.MethodDeclaration) {
emitFunctionDeclaration(<MethodDeclaration>property);
}
else {
Debug.fail("ObjectLiteralElement type not accounted for: " + property.kind);
}
}
emitEnd(property);
}
writeComma();
emit(tempVar);
2015-02-21 03:02:14 +01:00
if (multiLine) {
decreaseIndent();
writeLine();
}
write(")");
function writeComma() {
if (multiLine) {
write(",");
writeLine();
}
else {
write(", ");
}
}
}
2015-02-18 21:01:24 +01:00
function emitObjectLiteral(node: ObjectLiteralExpression): void {
2015-11-04 23:02:33 +01:00
const properties = node.properties;
2015-02-18 21:01:24 +01:00
if (languageVersion < ScriptTarget.ES6) {
2015-11-04 23:02:33 +01:00
const numProperties = properties.length;
2015-02-18 21:01:24 +01:00
// Find the first computed property.
// Everything until that point can be emitted as part of the initial object literal.
2015-03-13 20:34:12 +01:00
let numInitialNonComputedProperties = numProperties;
for (let i = 0, n = properties.length; i < n; i++) {
2015-02-18 21:01:24 +01:00
if (properties[i].name.kind === SyntaxKind.ComputedPropertyName) {
numInitialNonComputedProperties = i;
break;
}
}
2015-11-04 23:02:33 +01:00
const hasComputedProperty = numInitialNonComputedProperties !== properties.length;
2015-02-18 21:01:24 +01:00
if (hasComputedProperty) {
emitDownlevelObjectLiteralWithComputedProperties(node, numInitialNonComputedProperties);
return;
}
}
2015-02-18 21:01:24 +01:00
// Ordinary case: either the object has no computed properties
// or we're compiling with an ES6+ target.
emitObjectLiteralBody(node, properties.length);
}
Merge branch 'master' into unrewrite Conflicts: src/compiler/emitter.ts tests/baselines/reference/ES5SymbolProperty1.js tests/baselines/reference/FunctionDeclaration8_es6.js tests/baselines/reference/FunctionDeclaration9_es6.js tests/baselines/reference/FunctionPropertyAssignments5_es6.js tests/baselines/reference/computedPropertyNames10_ES5.js tests/baselines/reference/computedPropertyNames11_ES5.js tests/baselines/reference/computedPropertyNames18_ES5.js tests/baselines/reference/computedPropertyNames19_ES5.js tests/baselines/reference/computedPropertyNames1_ES5.js tests/baselines/reference/computedPropertyNames20_ES5.js tests/baselines/reference/computedPropertyNames22_ES5.js tests/baselines/reference/computedPropertyNames23_ES5.js tests/baselines/reference/computedPropertyNames25_ES5.js tests/baselines/reference/computedPropertyNames26_ES5.js tests/baselines/reference/computedPropertyNames28_ES5.js tests/baselines/reference/computedPropertyNames29_ES5.js tests/baselines/reference/computedPropertyNames30_ES5.js tests/baselines/reference/computedPropertyNames31_ES5.js tests/baselines/reference/computedPropertyNames33_ES5.js tests/baselines/reference/computedPropertyNames34_ES5.js tests/baselines/reference/computedPropertyNames46_ES5.js tests/baselines/reference/computedPropertyNames47_ES5.js tests/baselines/reference/computedPropertyNames48_ES5.js tests/baselines/reference/computedPropertyNames49_ES5.js tests/baselines/reference/computedPropertyNames4_ES5.js tests/baselines/reference/computedPropertyNames50_ES5.js tests/baselines/reference/computedPropertyNames5_ES5.js tests/baselines/reference/computedPropertyNames6_ES5.js tests/baselines/reference/computedPropertyNames7_ES5.js tests/baselines/reference/computedPropertyNames8_ES5.js tests/baselines/reference/computedPropertyNames9_ES5.js tests/baselines/reference/computedPropertyNamesContextualType10_ES5.js tests/baselines/reference/computedPropertyNamesContextualType1_ES5.js tests/baselines/reference/computedPropertyNamesContextualType2_ES5.js tests/baselines/reference/computedPropertyNamesContextualType3_ES5.js tests/baselines/reference/computedPropertyNamesContextualType4_ES5.js tests/baselines/reference/computedPropertyNamesContextualType5_ES5.js tests/baselines/reference/computedPropertyNamesContextualType6_ES5.js tests/baselines/reference/computedPropertyNamesContextualType7_ES5.js tests/baselines/reference/computedPropertyNamesContextualType8_ES5.js tests/baselines/reference/computedPropertyNamesContextualType9_ES5.js tests/baselines/reference/computedPropertyNamesDeclarationEmit5_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js.map tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.sourcemap.txt tests/baselines/reference/parserES5ComputedPropertyName2.js tests/baselines/reference/parserES5ComputedPropertyName3.js tests/baselines/reference/parserES5ComputedPropertyName4.js tests/baselines/reference/privateIndexer2.js
2015-04-06 23:19:28 +02:00
function createBinaryExpression(left: Expression, operator: SyntaxKind, right: Expression, startsOnNewLine?: boolean): BinaryExpression {
2015-11-04 23:02:33 +01:00
const result = <BinaryExpression>createSynthesizedNode(SyntaxKind.BinaryExpression, startsOnNewLine);
Merge branch 'master' into unrewrite Conflicts: src/compiler/emitter.ts tests/baselines/reference/ES5SymbolProperty1.js tests/baselines/reference/FunctionDeclaration8_es6.js tests/baselines/reference/FunctionDeclaration9_es6.js tests/baselines/reference/FunctionPropertyAssignments5_es6.js tests/baselines/reference/computedPropertyNames10_ES5.js tests/baselines/reference/computedPropertyNames11_ES5.js tests/baselines/reference/computedPropertyNames18_ES5.js tests/baselines/reference/computedPropertyNames19_ES5.js tests/baselines/reference/computedPropertyNames1_ES5.js tests/baselines/reference/computedPropertyNames20_ES5.js tests/baselines/reference/computedPropertyNames22_ES5.js tests/baselines/reference/computedPropertyNames23_ES5.js tests/baselines/reference/computedPropertyNames25_ES5.js tests/baselines/reference/computedPropertyNames26_ES5.js tests/baselines/reference/computedPropertyNames28_ES5.js tests/baselines/reference/computedPropertyNames29_ES5.js tests/baselines/reference/computedPropertyNames30_ES5.js tests/baselines/reference/computedPropertyNames31_ES5.js tests/baselines/reference/computedPropertyNames33_ES5.js tests/baselines/reference/computedPropertyNames34_ES5.js tests/baselines/reference/computedPropertyNames46_ES5.js tests/baselines/reference/computedPropertyNames47_ES5.js tests/baselines/reference/computedPropertyNames48_ES5.js tests/baselines/reference/computedPropertyNames49_ES5.js tests/baselines/reference/computedPropertyNames4_ES5.js tests/baselines/reference/computedPropertyNames50_ES5.js tests/baselines/reference/computedPropertyNames5_ES5.js tests/baselines/reference/computedPropertyNames6_ES5.js tests/baselines/reference/computedPropertyNames7_ES5.js tests/baselines/reference/computedPropertyNames8_ES5.js tests/baselines/reference/computedPropertyNames9_ES5.js tests/baselines/reference/computedPropertyNamesContextualType10_ES5.js tests/baselines/reference/computedPropertyNamesContextualType1_ES5.js tests/baselines/reference/computedPropertyNamesContextualType2_ES5.js tests/baselines/reference/computedPropertyNamesContextualType3_ES5.js tests/baselines/reference/computedPropertyNamesContextualType4_ES5.js tests/baselines/reference/computedPropertyNamesContextualType5_ES5.js tests/baselines/reference/computedPropertyNamesContextualType6_ES5.js tests/baselines/reference/computedPropertyNamesContextualType7_ES5.js tests/baselines/reference/computedPropertyNamesContextualType8_ES5.js tests/baselines/reference/computedPropertyNamesContextualType9_ES5.js tests/baselines/reference/computedPropertyNamesDeclarationEmit5_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js.map tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.sourcemap.txt tests/baselines/reference/parserES5ComputedPropertyName2.js tests/baselines/reference/parserES5ComputedPropertyName3.js tests/baselines/reference/parserES5ComputedPropertyName4.js tests/baselines/reference/privateIndexer2.js
2015-04-06 23:19:28 +02:00
result.operatorToken = createSynthesizedNode(operator);
result.left = left;
result.right = right;
Merge branch 'master' into unrewrite Conflicts: src/compiler/emitter.ts tests/baselines/reference/ES5SymbolProperty1.js tests/baselines/reference/FunctionDeclaration8_es6.js tests/baselines/reference/FunctionDeclaration9_es6.js tests/baselines/reference/FunctionPropertyAssignments5_es6.js tests/baselines/reference/computedPropertyNames10_ES5.js tests/baselines/reference/computedPropertyNames11_ES5.js tests/baselines/reference/computedPropertyNames18_ES5.js tests/baselines/reference/computedPropertyNames19_ES5.js tests/baselines/reference/computedPropertyNames1_ES5.js tests/baselines/reference/computedPropertyNames20_ES5.js tests/baselines/reference/computedPropertyNames22_ES5.js tests/baselines/reference/computedPropertyNames23_ES5.js tests/baselines/reference/computedPropertyNames25_ES5.js tests/baselines/reference/computedPropertyNames26_ES5.js tests/baselines/reference/computedPropertyNames28_ES5.js tests/baselines/reference/computedPropertyNames29_ES5.js tests/baselines/reference/computedPropertyNames30_ES5.js tests/baselines/reference/computedPropertyNames31_ES5.js tests/baselines/reference/computedPropertyNames33_ES5.js tests/baselines/reference/computedPropertyNames34_ES5.js tests/baselines/reference/computedPropertyNames46_ES5.js tests/baselines/reference/computedPropertyNames47_ES5.js tests/baselines/reference/computedPropertyNames48_ES5.js tests/baselines/reference/computedPropertyNames49_ES5.js tests/baselines/reference/computedPropertyNames4_ES5.js tests/baselines/reference/computedPropertyNames50_ES5.js tests/baselines/reference/computedPropertyNames5_ES5.js tests/baselines/reference/computedPropertyNames6_ES5.js tests/baselines/reference/computedPropertyNames7_ES5.js tests/baselines/reference/computedPropertyNames8_ES5.js tests/baselines/reference/computedPropertyNames9_ES5.js tests/baselines/reference/computedPropertyNamesContextualType10_ES5.js tests/baselines/reference/computedPropertyNamesContextualType1_ES5.js tests/baselines/reference/computedPropertyNamesContextualType2_ES5.js tests/baselines/reference/computedPropertyNamesContextualType3_ES5.js tests/baselines/reference/computedPropertyNamesContextualType4_ES5.js tests/baselines/reference/computedPropertyNamesContextualType5_ES5.js tests/baselines/reference/computedPropertyNamesContextualType6_ES5.js tests/baselines/reference/computedPropertyNamesContextualType7_ES5.js tests/baselines/reference/computedPropertyNamesContextualType8_ES5.js tests/baselines/reference/computedPropertyNamesContextualType9_ES5.js tests/baselines/reference/computedPropertyNamesDeclarationEmit5_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js.map tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.sourcemap.txt tests/baselines/reference/parserES5ComputedPropertyName2.js tests/baselines/reference/parserES5ComputedPropertyName3.js tests/baselines/reference/parserES5ComputedPropertyName4.js tests/baselines/reference/privateIndexer2.js
2015-04-06 23:19:28 +02:00
return result;
}
2015-04-11 03:56:11 +02:00
function createPropertyAccessExpression(expression: Expression, name: Identifier): PropertyAccessExpression {
2015-11-04 23:02:33 +01:00
const result = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression);
2015-04-11 03:56:11 +02:00
result.expression = parenthesizeForAccess(expression);
Merge branch 'master' into unrewrite Conflicts: src/compiler/emitter.ts tests/baselines/reference/ES5SymbolProperty1.js tests/baselines/reference/FunctionDeclaration8_es6.js tests/baselines/reference/FunctionDeclaration9_es6.js tests/baselines/reference/FunctionPropertyAssignments5_es6.js tests/baselines/reference/computedPropertyNames10_ES5.js tests/baselines/reference/computedPropertyNames11_ES5.js tests/baselines/reference/computedPropertyNames18_ES5.js tests/baselines/reference/computedPropertyNames19_ES5.js tests/baselines/reference/computedPropertyNames1_ES5.js tests/baselines/reference/computedPropertyNames20_ES5.js tests/baselines/reference/computedPropertyNames22_ES5.js tests/baselines/reference/computedPropertyNames23_ES5.js tests/baselines/reference/computedPropertyNames25_ES5.js tests/baselines/reference/computedPropertyNames26_ES5.js tests/baselines/reference/computedPropertyNames28_ES5.js tests/baselines/reference/computedPropertyNames29_ES5.js tests/baselines/reference/computedPropertyNames30_ES5.js tests/baselines/reference/computedPropertyNames31_ES5.js tests/baselines/reference/computedPropertyNames33_ES5.js tests/baselines/reference/computedPropertyNames34_ES5.js tests/baselines/reference/computedPropertyNames46_ES5.js tests/baselines/reference/computedPropertyNames47_ES5.js tests/baselines/reference/computedPropertyNames48_ES5.js tests/baselines/reference/computedPropertyNames49_ES5.js tests/baselines/reference/computedPropertyNames4_ES5.js tests/baselines/reference/computedPropertyNames50_ES5.js tests/baselines/reference/computedPropertyNames5_ES5.js tests/baselines/reference/computedPropertyNames6_ES5.js tests/baselines/reference/computedPropertyNames7_ES5.js tests/baselines/reference/computedPropertyNames8_ES5.js tests/baselines/reference/computedPropertyNames9_ES5.js tests/baselines/reference/computedPropertyNamesContextualType10_ES5.js tests/baselines/reference/computedPropertyNamesContextualType1_ES5.js tests/baselines/reference/computedPropertyNamesContextualType2_ES5.js tests/baselines/reference/computedPropertyNamesContextualType3_ES5.js tests/baselines/reference/computedPropertyNamesContextualType4_ES5.js tests/baselines/reference/computedPropertyNamesContextualType5_ES5.js tests/baselines/reference/computedPropertyNamesContextualType6_ES5.js tests/baselines/reference/computedPropertyNamesContextualType7_ES5.js tests/baselines/reference/computedPropertyNamesContextualType8_ES5.js tests/baselines/reference/computedPropertyNamesContextualType9_ES5.js tests/baselines/reference/computedPropertyNamesDeclarationEmit5_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js.map tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.sourcemap.txt tests/baselines/reference/parserES5ComputedPropertyName2.js tests/baselines/reference/parserES5ComputedPropertyName3.js tests/baselines/reference/parserES5ComputedPropertyName4.js tests/baselines/reference/privateIndexer2.js
2015-04-06 23:19:28 +02:00
result.name = name;
return result;
2015-04-11 03:56:11 +02:00
}
Merge branch 'master' into unrewrite Conflicts: src/compiler/emitter.ts tests/baselines/reference/ES5SymbolProperty1.js tests/baselines/reference/FunctionDeclaration8_es6.js tests/baselines/reference/FunctionDeclaration9_es6.js tests/baselines/reference/FunctionPropertyAssignments5_es6.js tests/baselines/reference/computedPropertyNames10_ES5.js tests/baselines/reference/computedPropertyNames11_ES5.js tests/baselines/reference/computedPropertyNames18_ES5.js tests/baselines/reference/computedPropertyNames19_ES5.js tests/baselines/reference/computedPropertyNames1_ES5.js tests/baselines/reference/computedPropertyNames20_ES5.js tests/baselines/reference/computedPropertyNames22_ES5.js tests/baselines/reference/computedPropertyNames23_ES5.js tests/baselines/reference/computedPropertyNames25_ES5.js tests/baselines/reference/computedPropertyNames26_ES5.js tests/baselines/reference/computedPropertyNames28_ES5.js tests/baselines/reference/computedPropertyNames29_ES5.js tests/baselines/reference/computedPropertyNames30_ES5.js tests/baselines/reference/computedPropertyNames31_ES5.js tests/baselines/reference/computedPropertyNames33_ES5.js tests/baselines/reference/computedPropertyNames34_ES5.js tests/baselines/reference/computedPropertyNames46_ES5.js tests/baselines/reference/computedPropertyNames47_ES5.js tests/baselines/reference/computedPropertyNames48_ES5.js tests/baselines/reference/computedPropertyNames49_ES5.js tests/baselines/reference/computedPropertyNames4_ES5.js tests/baselines/reference/computedPropertyNames50_ES5.js tests/baselines/reference/computedPropertyNames5_ES5.js tests/baselines/reference/computedPropertyNames6_ES5.js tests/baselines/reference/computedPropertyNames7_ES5.js tests/baselines/reference/computedPropertyNames8_ES5.js tests/baselines/reference/computedPropertyNames9_ES5.js tests/baselines/reference/computedPropertyNamesContextualType10_ES5.js tests/baselines/reference/computedPropertyNamesContextualType1_ES5.js tests/baselines/reference/computedPropertyNamesContextualType2_ES5.js tests/baselines/reference/computedPropertyNamesContextualType3_ES5.js tests/baselines/reference/computedPropertyNamesContextualType4_ES5.js tests/baselines/reference/computedPropertyNamesContextualType5_ES5.js tests/baselines/reference/computedPropertyNamesContextualType6_ES5.js tests/baselines/reference/computedPropertyNamesContextualType7_ES5.js tests/baselines/reference/computedPropertyNamesContextualType8_ES5.js tests/baselines/reference/computedPropertyNamesContextualType9_ES5.js tests/baselines/reference/computedPropertyNamesDeclarationEmit5_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js.map tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.sourcemap.txt tests/baselines/reference/parserES5ComputedPropertyName2.js tests/baselines/reference/parserES5ComputedPropertyName3.js tests/baselines/reference/parserES5ComputedPropertyName4.js tests/baselines/reference/privateIndexer2.js
2015-04-06 23:19:28 +02:00
2015-04-11 03:56:11 +02:00
function createElementAccessExpression(expression: Expression, argumentExpression: Expression): ElementAccessExpression {
2015-11-04 23:02:33 +01:00
const result = <ElementAccessExpression>createSynthesizedNode(SyntaxKind.ElementAccessExpression);
2015-04-11 03:56:11 +02:00
result.expression = parenthesizeForAccess(expression);
Merge branch 'master' into unrewrite Conflicts: src/compiler/emitter.ts tests/baselines/reference/ES5SymbolProperty1.js tests/baselines/reference/FunctionDeclaration8_es6.js tests/baselines/reference/FunctionDeclaration9_es6.js tests/baselines/reference/FunctionPropertyAssignments5_es6.js tests/baselines/reference/computedPropertyNames10_ES5.js tests/baselines/reference/computedPropertyNames11_ES5.js tests/baselines/reference/computedPropertyNames18_ES5.js tests/baselines/reference/computedPropertyNames19_ES5.js tests/baselines/reference/computedPropertyNames1_ES5.js tests/baselines/reference/computedPropertyNames20_ES5.js tests/baselines/reference/computedPropertyNames22_ES5.js tests/baselines/reference/computedPropertyNames23_ES5.js tests/baselines/reference/computedPropertyNames25_ES5.js tests/baselines/reference/computedPropertyNames26_ES5.js tests/baselines/reference/computedPropertyNames28_ES5.js tests/baselines/reference/computedPropertyNames29_ES5.js tests/baselines/reference/computedPropertyNames30_ES5.js tests/baselines/reference/computedPropertyNames31_ES5.js tests/baselines/reference/computedPropertyNames33_ES5.js tests/baselines/reference/computedPropertyNames34_ES5.js tests/baselines/reference/computedPropertyNames46_ES5.js tests/baselines/reference/computedPropertyNames47_ES5.js tests/baselines/reference/computedPropertyNames48_ES5.js tests/baselines/reference/computedPropertyNames49_ES5.js tests/baselines/reference/computedPropertyNames4_ES5.js tests/baselines/reference/computedPropertyNames50_ES5.js tests/baselines/reference/computedPropertyNames5_ES5.js tests/baselines/reference/computedPropertyNames6_ES5.js tests/baselines/reference/computedPropertyNames7_ES5.js tests/baselines/reference/computedPropertyNames8_ES5.js tests/baselines/reference/computedPropertyNames9_ES5.js tests/baselines/reference/computedPropertyNamesContextualType10_ES5.js tests/baselines/reference/computedPropertyNamesContextualType1_ES5.js tests/baselines/reference/computedPropertyNamesContextualType2_ES5.js tests/baselines/reference/computedPropertyNamesContextualType3_ES5.js tests/baselines/reference/computedPropertyNamesContextualType4_ES5.js tests/baselines/reference/computedPropertyNamesContextualType5_ES5.js tests/baselines/reference/computedPropertyNamesContextualType6_ES5.js tests/baselines/reference/computedPropertyNamesContextualType7_ES5.js tests/baselines/reference/computedPropertyNamesContextualType8_ES5.js tests/baselines/reference/computedPropertyNamesContextualType9_ES5.js tests/baselines/reference/computedPropertyNamesDeclarationEmit5_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.js.map tests/baselines/reference/computedPropertyNamesSourceMap2_ES5.sourcemap.txt tests/baselines/reference/parserES5ComputedPropertyName2.js tests/baselines/reference/parserES5ComputedPropertyName3.js tests/baselines/reference/parserES5ComputedPropertyName4.js tests/baselines/reference/privateIndexer2.js
2015-04-06 23:19:28 +02:00
result.argumentExpression = argumentExpression;
return result;
}
2015-04-11 03:56:11 +02:00
function parenthesizeForAccess(expr: Expression): LeftHandSideExpression {
// When diagnosing whether the expression needs parentheses, the decision should be based
// on the innermost expression in a chain of nested type assertions.
while (expr.kind === SyntaxKind.TypeAssertionExpression ||
expr.kind === SyntaxKind.AsExpression ||
expr.kind === SyntaxKind.NonNullExpression) {
expr = (<AssertionExpression | NonNullExpression>expr).expression;
}
// isLeftHandSideExpression is almost the correct criterion for when it is not necessary
// to parenthesize the expression before a dot. The known exceptions are:
//
// NewExpression:
// new C.x -> not the same as (new C).x
// NumberLiteral
// 1.x -> not the same as (1).x
//
if (isLeftHandSideExpression(expr) &&
expr.kind !== SyntaxKind.NewExpression &&
expr.kind !== SyntaxKind.NumericLiteral) {
2015-04-11 03:56:11 +02:00
return <LeftHandSideExpression>expr;
}
2015-11-04 23:02:33 +01:00
const node = <ParenthesizedExpression>createSynthesizedNode(SyntaxKind.ParenthesizedExpression);
2015-04-11 03:56:11 +02:00
node.expression = expr;
return node;
}
2014-11-18 00:53:03 +01:00
function emitComputedPropertyName(node: ComputedPropertyName) {
write("[");
emitExpressionForPropertyName(node);
2014-11-18 00:53:03 +01:00
write("]");
}
function emitMethod(node: MethodDeclaration) {
if (languageVersion >= ScriptTarget.ES6 && node.asteriskToken) {
write("*");
}
emit(node.name);
2015-01-16 16:15:31 +01:00
if (languageVersion < ScriptTarget.ES6) {
write(": function ");
}
emitSignatureAndBody(node);
}
function emitPropertyAssignment(node: PropertyDeclaration) {
emit(node.name);
write(": ");
// This is to ensure that we emit comment in the following case:
// For example:
// obj = {
2015-08-11 02:34:39 +02:00
// id: /*comment1*/ ()=>void
// }
// "comment1" is not considered to be leading comment for node.initializer
// but rather a trailing comment on the previous node.
emitTrailingCommentsOfPosition(node.initializer.pos);
emit(node.initializer);
}
// Return true if identifier resolves to an exported member of a namespace
function isExportReference(node: Identifier) {
2015-11-04 23:02:33 +01:00
const container = resolver.getReferencedExportContainer(node);
return !!container;
}
// Return true if identifier resolves to an imported identifier
function isImportedReference(node: Identifier) {
const declaration = resolver.getReferencedImportDeclaration(node);
return declaration && (declaration.kind === SyntaxKind.ImportClause || declaration.kind === SyntaxKind.ImportSpecifier);
}
function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) {
// The name property of a short-hand property assignment is considered an expression position, so here
// we manually emit the identifier to avoid rewriting.
writeTextOfNode(currentText, node.name);
// If emitting pre-ES6 code, or if the name requires rewriting when resolved as an expression identifier,
// we emit a normal property assignment. For example:
// module m {
// export let y;
// }
// module m {
// let obj = { y };
// }
// Here we need to emit obj = { y : m.y } regardless of the output target.
// The same rules apply for imported identifiers when targeting module formats with indirect access to
// the imported identifiers. For example, when targeting CommonJS:
//
// import {foo} from './foo';
// export const baz = { foo };
//
// Must be transformed into:
//
// const foo_1 = require('./foo');
// exports.baz = { foo: foo_1.foo };
//
if (languageVersion < ScriptTarget.ES6 || (modulekind !== ModuleKind.ES6 && isImportedReference(node.name)) || isExportReference(node.name)) {
// Emit identifier as an identifier
write(": ");
emitExpressionIdentifier(node.name);
}
if (languageVersion >= ScriptTarget.ES6 && node.objectAssignmentInitializer) {
write(" = ");
emit(node.objectAssignmentInitializer);
}
}
2014-11-30 00:58:55 +01:00
function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean {
2015-11-04 23:02:33 +01:00
const constantValue = tryGetConstEnumValue(node);
if (constantValue !== undefined) {
write(constantValue.toString());
if (!compilerOptions.removeComments) {
2015-11-04 23:02:33 +01:00
const propertyName: string = node.kind === SyntaxKind.PropertyAccessExpression ? declarationNameToString((<PropertyAccessExpression>node).name) : getTextOfNode((<ElementAccessExpression>node).argumentExpression);
write(" /* " + propertyName + " */");
}
return true;
}
return false;
}
function tryGetConstEnumValue(node: Node): number {
if (compilerOptions.isolatedModules) {
return undefined;
}
return node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression
? resolver.getConstantValue(<PropertyAccessExpression | ElementAccessExpression>node)
: undefined;
}
2015-07-09 23:44:47 +02:00
// Returns 'true' if the code was actually indented, false otherwise.
// If the code is not indented, an optional valueToWriteWhenNotIndenting will be
// emitted instead.
function indentIfOnDifferentLines(parent: Node, node1: TextRange, node2: TextRange, valueToWriteWhenNotIndenting?: string): boolean {
2015-11-04 23:02:33 +01:00
const realNodesAreOnDifferentLines = !nodeIsSynthesized(parent) && !nodeEndIsOnSameLineAsNodeStart(node1, node2);
2015-03-07 22:33:02 +01:00
// Always use a newline for synthesized code if the synthesizer desires it.
const synthesizedNodeIsOnDifferentLine = synthesizedNodeStartsOnNewLine(node2 as Node);
2015-03-07 22:33:02 +01:00
if (realNodesAreOnDifferentLines || synthesizedNodeIsOnDifferentLine) {
increaseIndent();
writeLine();
return true;
}
else {
2015-03-09 09:07:02 +01:00
if (valueToWriteWhenNotIndenting) {
write(valueToWriteWhenNotIndenting);
}
return false;
}
2015-03-07 22:33:02 +01:00
}
2014-11-30 00:58:55 +01:00
function emitPropertyAccess(node: PropertyAccessExpression) {
if (tryEmitConstantValue(node)) {
return;
}
if (languageVersion === ScriptTarget.ES6 &&
node.expression.kind === SyntaxKind.SuperKeyword &&
isInAsyncMethodWithSuperInES6(node)) {
const name = <StringLiteral>createSynthesizedNode(SyntaxKind.StringLiteral);
name.text = node.name.text;
emitSuperAccessInAsyncMethod(node.expression, name);
return;
}
emit(node.expression);
const dotRangeStart = nodeIsSynthesized(node.expression) ? -1 : node.expression.end;
const dotRangeEnd = nodeIsSynthesized(node.expression) ? -1 : skipTrivia(currentText, node.expression.end) + 1;
const dotToken = <TextRange>{ pos: dotRangeStart, end: dotRangeEnd };
const indentedBeforeDot = indentIfOnDifferentLines(node, node.expression, dotToken);
2015-06-26 01:24:41 +02:00
2015-07-09 23:44:47 +02:00
// 1 .toString is a valid property access, emit a space after the literal
// Also emit a space if expression is a integer const enum value - it will appear in generated code as numeric literal
let shouldEmitSpace = false;
if (!indentedBeforeDot) {
if (node.expression.kind === SyntaxKind.NumericLiteral) {
// check if numeric literal was originally written with a dot
const text = getTextOfNodeFromSourceText(currentText, node.expression);
shouldEmitSpace = text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0;
}
else {
// check if constant enum value is integer
2015-11-04 23:02:33 +01:00
const constantValue = tryGetConstEnumValue(node.expression);
// isFinite handles cases when constantValue is undefined
shouldEmitSpace = isFinite(constantValue) && Math.floor(constantValue) === constantValue;
}
}
if (shouldEmitSpace) {
2015-06-26 01:24:41 +02:00
write(" .");
}
else {
write(".");
}
const indentedAfterDot = indentIfOnDifferentLines(node, dotToken, node.name);
emit(node.name);
2015-03-09 09:07:02 +01:00
decreaseIndentIf(indentedBeforeDot, indentedAfterDot);
2014-07-13 01:04:16 +02:00
}
2015-07-27 13:52:57 +02:00
function emitQualifiedName(node: QualifiedName) {
emit(node.left);
write(".");
emit(node.right);
2014-07-13 01:04:16 +02:00
}
function emitQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean) {
if (node.left.kind === SyntaxKind.Identifier) {
emitEntityNameAsExpression(node.left, useFallback);
}
else if (useFallback) {
2015-11-04 23:02:33 +01:00
const temp = createAndRecordTempVariable(TempFlags.Auto);
write("(");
emitNodeWithoutSourceMap(temp);
write(" = ");
emitEntityNameAsExpression(node.left, /*useFallback*/ true);
write(") && ");
emitNodeWithoutSourceMap(temp);
}
else {
emitEntityNameAsExpression(node.left, /*useFallback*/ false);
}
2015-07-27 13:52:57 +02:00
write(".");
emit(node.right);
}
2015-07-27 13:52:57 +02:00
2015-12-01 21:12:31 +01:00
function emitEntityNameAsExpression(node: EntityName | Expression, useFallback: boolean) {
switch (node.kind) {
case SyntaxKind.Identifier:
if (useFallback) {
write("typeof ");
emitExpressionIdentifier(<Identifier>node);
write(" !== 'undefined' && ");
}
2015-07-27 13:52:57 +02:00
emitExpressionIdentifier(<Identifier>node);
break;
2015-07-27 13:52:57 +02:00
case SyntaxKind.QualifiedName:
emitQualifiedNameAsExpression(<QualifiedName>node, useFallback);
break;
2015-12-01 21:12:31 +01:00
default:
emitNodeWithoutSourceMap(node);
break;
}
}
function emitIndexedAccess(node: ElementAccessExpression) {
if (tryEmitConstantValue(node)) {
return;
}
if (languageVersion === ScriptTarget.ES6 &&
node.expression.kind === SyntaxKind.SuperKeyword &&
isInAsyncMethodWithSuperInES6(node)) {
emitSuperAccessInAsyncMethod(node.expression, node.argumentExpression);
return;
}
emit(node.expression);
write("[");
emit(node.argumentExpression);
write("]");
2014-07-13 01:04:16 +02:00
}
function hasSpreadElement(elements: Expression[]) {
return forEach(elements, e => e.kind === SyntaxKind.SpreadElementExpression);
}
function skipParentheses(node: Expression): Expression {
while (node.kind === SyntaxKind.ParenthesizedExpression ||
node.kind === SyntaxKind.TypeAssertionExpression ||
node.kind === SyntaxKind.AsExpression ||
node.kind === SyntaxKind.NonNullExpression) {
node = (<ParenthesizedExpression | AssertionExpression | NonNullExpression>node).expression;
}
return node;
}
2015-02-06 16:39:11 +01:00
function emitCallTarget(node: Expression): Expression {
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword) {
emit(node);
return node;
}
2015-11-04 23:02:33 +01:00
const temp = createAndRecordTempVariable(TempFlags.Auto);
write("(");
emit(temp);
write(" = ");
emit(node);
write(")");
return temp;
}
function emitCallWithSpread(node: CallExpression) {
2015-03-13 20:34:12 +01:00
let target: Expression;
2015-11-04 23:02:33 +01:00
const expr = skipParentheses(node.expression);
if (expr.kind === SyntaxKind.PropertyAccessExpression) {
2015-02-06 16:39:11 +01:00
// Target will be emitted as "this" argument
target = emitCallTarget((<PropertyAccessExpression>expr).expression);
write(".");
emit((<PropertyAccessExpression>expr).name);
}
else if (expr.kind === SyntaxKind.ElementAccessExpression) {
2015-02-06 16:39:11 +01:00
// Target will be emitted as "this" argument
target = emitCallTarget((<PropertyAccessExpression>expr).expression);
write("[");
emit((<ElementAccessExpression>expr).argumentExpression);
write("]");
}
else if (expr.kind === SyntaxKind.SuperKeyword) {
target = expr;
write("_super");
}
else {
emit(node.expression);
}
write(".apply(");
if (target) {
if (target.kind === SyntaxKind.SuperKeyword) {
2015-02-06 16:39:11 +01:00
// Calls of form super(...) and super.foo(...)
emitThis(target);
}
else {
2015-02-06 16:39:11 +01:00
// Calls of form obj.foo(...)
emit(target);
}
}
else {
2015-02-06 16:39:11 +01:00
// Calls of form foo(...)
write("void 0");
}
write(", ");
2015-05-16 10:42:49 +02:00
emitListWithSpread(node.arguments, /*needsUniqueCopy*/ false, /*multiLine*/ false, /*trailingComma*/ false, /*useConcat*/ true);
write(")");
}
function isInAsyncMethodWithSuperInES6(node: Node) {
if (languageVersion === ScriptTarget.ES6) {
const container = getSuperContainer(node, /*includeFunctions*/ false);
if (container && resolver.getNodeCheckFlags(container) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding)) {
return true;
}
}
return false;
}
function emitSuperAccessInAsyncMethod(superNode: Node, argumentExpression: Expression) {
const container = getSuperContainer(superNode, /*includeFunctions*/ false);
const isSuperBinding = resolver.getNodeCheckFlags(container) & NodeCheckFlags.AsyncMethodWithSuperBinding;
write("_super(");
emit(argumentExpression);
write(isSuperBinding ? ").value" : ")");
}
function emitCallExpression(node: CallExpression) {
if (languageVersion < ScriptTarget.ES6 && hasSpreadElement(node.arguments)) {
emitCallWithSpread(node);
return;
}
const expression = node.expression;
2015-03-13 20:34:12 +01:00
let superCall = false;
let isAsyncMethodWithSuper = false;
if (expression.kind === SyntaxKind.SuperKeyword) {
emitSuper(expression);
superCall = true;
}
else {
superCall = isSuperPropertyOrElementAccess(expression);
isAsyncMethodWithSuper = superCall && isInAsyncMethodWithSuperInES6(node);
emit(expression);
}
if (superCall && (languageVersion < ScriptTarget.ES6 || isAsyncMethodWithSuper)) {
write(".call(");
emitThis(expression);
if (node.arguments.length) {
write(", ");
emitCommaList(node.arguments);
}
write(")");
2014-07-13 01:04:16 +02:00
}
else {
write("(");
emitCommaList(node.arguments);
write(")");
}
}
function emitNewExpression(node: NewExpression) {
write("new ");
2015-06-09 23:58:56 +02:00
// Spread operator logic is supported in new expressions in ES5 using a combination
// of Function.prototype.bind() and Function.prototype.apply().
//
// Example:
//
2015-06-09 23:58:56 +02:00
// var args = [1, 2, 3, 4, 5];
// new Array(...args);
//
2015-06-09 23:58:56 +02:00
// is compiled into the following ES5:
//
2015-06-09 23:58:56 +02:00
// var args = [1, 2, 3, 4, 5];
// new (Array.bind.apply(Array, [void 0].concat(args)));
//
2015-06-09 23:58:56 +02:00
// The 'thisArg' to 'bind' is ignored when invoking the result of 'bind' with 'new',
// Thus, we set it to undefined ('void 0').
if (languageVersion === ScriptTarget.ES5 &&
node.arguments &&
hasSpreadElement(node.arguments)) {
write("(");
2015-11-04 23:02:33 +01:00
const target = emitCallTarget(node.expression);
2015-05-14 06:33:15 +02:00
write(".bind.apply(");
emit(target);
write(", [void 0].concat(");
2015-11-14 02:43:53 +01:00
emitListWithSpread(node.arguments, /*needsUniqueCopy*/ false, /*multiLine*/ false, /*trailingComma*/ false, /*useConcat*/ false);
write(")))");
write("()");
}
else {
emit(node.expression);
if (node.arguments) {
write("(");
emitCommaList(node.arguments);
write(")");
}
}
2014-07-13 01:04:16 +02:00
}
function emitTaggedTemplateExpression(node: TaggedTemplateExpression): void {
if (languageVersion >= ScriptTarget.ES6) {
emit(node.tag);
write(" ");
emit(node.template);
}
else {
emitDownlevelTaggedTemplate(node);
}
}
2014-11-30 00:58:55 +01:00
function emitParenExpression(node: ParenthesizedExpression) {
// If the node is synthesized, it means the emitter put the parentheses there,
// not the user. If we didn't want them, the emitter would not have put them
// there.
if (!nodeIsSynthesized(node) && node.parent.kind !== SyntaxKind.ArrowFunction) {
if (node.expression.kind === SyntaxKind.TypeAssertionExpression ||
node.expression.kind === SyntaxKind.AsExpression ||
node.expression.kind === SyntaxKind.NonNullExpression) {
let operand = (<TypeAssertion | NonNullExpression>node.expression).expression;
2015-03-07 11:08:36 +01:00
// Make sure we consider all nested cast expressions, e.g.:
// (<any><number><any>-A).x;
while (operand.kind === SyntaxKind.TypeAssertionExpression ||
operand.kind === SyntaxKind.AsExpression ||
operand.kind === SyntaxKind.NonNullExpression) {
operand = (<TypeAssertion | NonNullExpression>operand).expression;
2015-03-07 11:08:36 +01:00
}
// We have an expression of the form: (<Type>SubExpr) or (SubExpr as Type)
2015-03-07 11:08:36 +01:00
// 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.PrefixUnaryExpression &&
operand.kind !== SyntaxKind.VoidExpression &&
operand.kind !== SyntaxKind.TypeOfExpression &&
operand.kind !== SyntaxKind.DeleteExpression &&
operand.kind !== SyntaxKind.PostfixUnaryExpression &&
operand.kind !== SyntaxKind.NewExpression &&
!(operand.kind === SyntaxKind.BinaryExpression && node.expression.kind === SyntaxKind.AsExpression) &&
2015-03-07 11:08:36 +01:00
!(operand.kind === SyntaxKind.CallExpression && node.parent.kind === SyntaxKind.NewExpression) &&
2015-09-04 15:47:28 +02:00
!(operand.kind === SyntaxKind.FunctionExpression && node.parent.kind === SyntaxKind.CallExpression) &&
!(operand.kind === SyntaxKind.NumericLiteral && node.parent.kind === SyntaxKind.PropertyAccessExpression)) {
2015-03-07 11:08:36 +01:00
emit(operand);
return;
}
}
}
2015-03-07 11:08:36 +01:00
write("(");
emit(node.expression);
write(")");
}
2014-07-13 01:04:16 +02:00
function emitDeleteExpression(node: DeleteExpression) {
write(tokenToString(SyntaxKind.DeleteKeyword));
write(" ");
emit(node.expression);
}
function emitVoidExpression(node: VoidExpression) {
write(tokenToString(SyntaxKind.VoidKeyword));
write(" ");
emit(node.expression);
}
function emitTypeOfExpression(node: TypeOfExpression) {
write(tokenToString(SyntaxKind.TypeOfKeyword));
write(" ");
emit(node.expression);
}
2015-04-13 10:32:16 +02:00
function isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node: Node): boolean {
if (!isCurrentFileSystemExternalModule() || node.kind !== SyntaxKind.Identifier || nodeIsSynthesized(node)) {
return false;
}
2015-04-13 10:32:16 +02:00
const isVariableDeclarationOrBindingElement =
node.parent && (node.parent.kind === SyntaxKind.VariableDeclaration || node.parent.kind === SyntaxKind.BindingElement);
2015-04-21 01:56:36 +02:00
const targetDeclaration =
isVariableDeclarationOrBindingElement
? <Declaration>node.parent
: resolver.getReferencedValueDeclaration(<Identifier>node);
2015-04-13 10:32:16 +02:00
2015-04-24 06:14:03 +02:00
return isSourceFileLevelDeclarationInSystemJsModule(targetDeclaration, /*isExported*/ true);
}
function isNameOfExportedDeclarationInNonES6Module(node: Node): boolean {
if (modulekind === ModuleKind.System || node.kind !== SyntaxKind.Identifier || nodeIsSynthesized(node)) {
return false;
}
2016-08-15 21:03:39 +02:00
return !exportEquals && exportSpecifiers && (<Identifier>node).text in exportSpecifiers;
}
function emitPrefixUnaryExpression(node: PrefixUnaryExpression) {
const isPlusPlusOrMinusMinus = (node.operator === SyntaxKind.PlusPlusToken
|| node.operator === SyntaxKind.MinusMinusToken);
const externalExportChanged = isPlusPlusOrMinusMinus &&
isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand);
if (externalExportChanged) {
2015-04-21 01:56:36 +02:00
// emit
// ++x
// as
// exports('x', ++x)
write(`${exportFunctionForFile}("`);
emitNodeWithoutSourceMap(node.operand);
write(`", `);
}
const internalExportChanged = isPlusPlusOrMinusMinus &&
isNameOfExportedDeclarationInNonES6Module(node.operand);
if (internalExportChanged) {
emitAliasEqual(<Identifier>node.operand);
}
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 identifier, 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.
2014-12-01 06:22:23 +01:00
if (node.operand.kind === SyntaxKind.PrefixUnaryExpression) {
2015-11-04 23:02:33 +01:00
const operand = <PrefixUnaryExpression>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(" ");
}
2014-07-13 01:04:16 +02:00
}
emit(node.operand);
if (externalExportChanged) {
write(")");
}
}
2014-07-13 01:04:16 +02:00
function emitPostfixUnaryExpression(node: PostfixUnaryExpression) {
const externalExportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand);
const internalExportChanged = isNameOfExportedDeclarationInNonES6Module(node.operand);
if (externalExportChanged) {
2015-04-21 01:56:36 +02:00
// export function returns the value that was passes as the second argument
// however for postfix unary expressions result value should be the value before modification.
// emit 'x++' as '(export('x', ++x) - 1)' and 'x--' as '(export('x', --x) + 1)'
write(`(${exportFunctionForFile}("`);
emitNodeWithoutSourceMap(node.operand);
write(`", `);
write(tokenToString(node.operator));
emit(node.operand);
if (node.operator === SyntaxKind.PlusPlusToken) {
write(") - 1)");
}
else {
write(") + 1)");
}
}
else if (internalExportChanged) {
emitAliasEqual(<Identifier>node.operand);
emit(node.operand);
if (node.operator === SyntaxKind.PlusPlusToken) {
write(" += 1");
}
else {
write(" -= 1");
}
}
else {
emit(node.operand);
write(tokenToString(node.operator));
}
}
2015-04-24 06:14:03 +02:00
function shouldHoistDeclarationInSystemJsModule(node: Node): boolean {
return isSourceFileLevelDeclarationInSystemJsModule(node, /*isExported*/ false);
}
2015-07-09 23:44:47 +02:00
/*
2015-04-21 01:56:36 +02:00
* Checks if given node is a source file level declaration (not nested in module/function).
* If 'isExported' is true - then declaration must also be exported.
* This function is used in two cases:
2015-07-09 23:44:47 +02:00
* - check if node is a exported source file level value to determine
2015-04-21 01:56:36 +02:00
* if we should also export the value after its it changed
2015-07-09 23:44:47 +02:00
* - check if node is a source level declaration to emit it differently,
* i.e non-exported variable statement 'var x = 1' is hoisted so
* when we emit variable statement 'var' should be dropped.
2015-04-21 01:56:36 +02:00
*/
2015-04-24 06:14:03 +02:00
function isSourceFileLevelDeclarationInSystemJsModule(node: Node, isExported: boolean): boolean {
2015-10-22 23:24:04 +02:00
if (!node || !isCurrentFileSystemExternalModule()) {
return false;
}
2015-04-13 10:32:16 +02:00
let current = getRootDeclaration(node).parent;
2015-04-13 10:32:16 +02:00
while (current) {
if (current.kind === SyntaxKind.SourceFile) {
2015-06-26 01:24:41 +02:00
return !isExported || ((getCombinedNodeFlags(node) & NodeFlags.Export) !== 0);
2015-04-13 10:32:16 +02:00
}
else if (isDeclaration(current)) {
2015-04-13 10:32:16 +02:00
return false;
}
else {
current = current.parent;
}
}
}
2014-07-13 01:04:16 +02:00
2015-10-03 03:50:45 +02:00
/**
* Emit ES7 exponentiation operator downlevel using Math.pow
* @param node a binary expression node containing exponentiationOperator (**, **=)
2015-10-03 03:50:45 +02:00
*/
function emitExponentiationOperator(node: BinaryExpression) {
2015-11-04 23:02:33 +01:00
const leftHandSideExpression = node.left;
2015-10-03 03:50:45 +02:00
if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) {
let synthesizedLHS: ElementAccessExpression | PropertyAccessExpression;
let shouldEmitParentheses = false;
2015-10-03 03:50:45 +02:00
if (isElementAccessExpression(leftHandSideExpression)) {
shouldEmitParentheses = true;
write("(");
2015-10-03 03:50:45 +02:00
synthesizedLHS = <ElementAccessExpression>createSynthesizedNode(SyntaxKind.ElementAccessExpression, /*startsOnNewLine*/ false);
2015-11-14 02:43:53 +01:00
const identifier = emitTempVariableAssignment(leftHandSideExpression.expression, /*canDefineTempVariablesInPlace*/ false, /*shouldEmitCommaBeforeAssignment*/ false);
synthesizedLHS.expression = identifier;
2015-10-03 03:50:45 +02:00
if (leftHandSideExpression.argumentExpression.kind !== SyntaxKind.NumericLiteral &&
leftHandSideExpression.argumentExpression.kind !== SyntaxKind.StringLiteral) {
2015-11-04 23:02:33 +01:00
const tempArgumentExpression = createAndRecordTempVariable(TempFlags._i);
2015-10-03 03:50:45 +02:00
(<ElementAccessExpression>synthesizedLHS).argumentExpression = tempArgumentExpression;
2015-12-03 23:20:25 +01:00
emitAssignment(tempArgumentExpression, leftHandSideExpression.argumentExpression, /*shouldEmitCommaBeforeAssignment*/ true, leftHandSideExpression.expression);
2015-10-03 03:50:45 +02:00
}
else {
(<ElementAccessExpression>synthesizedLHS).argumentExpression = leftHandSideExpression.argumentExpression;
}
write(", ");
}
else if (isPropertyAccessExpression(leftHandSideExpression)) {
shouldEmitParentheses = true;
write("(");
synthesizedLHS = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression, /*startsOnNewLine*/ false);
2015-11-14 02:43:53 +01:00
const identifier = emitTempVariableAssignment(leftHandSideExpression.expression, /*canDefineTempVariablesInPlace*/ false, /*shouldEmitCommaBeforeAssignment*/ false);
synthesizedLHS.expression = identifier;
2015-10-03 03:50:45 +02:00
(<PropertyAccessExpression>synthesizedLHS).name = leftHandSideExpression.name;
write(", ");
}
emit(synthesizedLHS || leftHandSideExpression);
write(" = ");
write("Math.pow(");
emit(synthesizedLHS || leftHandSideExpression);
write(", ");
emit(node.right);
write(")");
if (shouldEmitParentheses) {
2015-10-03 03:50:45 +02:00
write(")");
}
}
else {
write("Math.pow(");
emit(leftHandSideExpression);
write(", ");
emit(node.right);
write(")");
}
}
function emitAliasEqual(name: Identifier): boolean {
for (const specifier of exportSpecifiers[name.text]) {
emitStart(specifier.name);
emitContainingModuleName(specifier);
2016-05-24 00:42:11 +02:00
if (languageVersion === ScriptTarget.ES3 && name.text === "default") {
write('["default"]');
}
2016-05-24 00:52:15 +02:00
else {
write(".");
emitNodeWithCommentsAndWithoutSourcemap(specifier.name);
}
emitEnd(specifier.name);
write(" = ");
}
return true;
}
function emitBinaryExpression(node: BinaryExpression) {
if (languageVersion < ScriptTarget.ES6 && node.operatorToken.kind === SyntaxKind.EqualsToken &&
(node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) {
2015-03-09 21:32:02 +01:00
emitDestructuring(node, node.parent.kind === SyntaxKind.ExpressionStatement);
}
else {
2016-05-23 23:21:43 +02:00
const isAssignment = isAssignmentOperator(node.operatorToken.kind);
const externalExportChanged = isAssignment &&
2015-04-13 10:32:16 +02:00
isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.left);
if (externalExportChanged) {
2015-04-21 15:49:31 +02:00
// emit assignment 'x <op> y' as 'exports("x", x <op> y)'
write(`${exportFunctionForFile}("`);
emitNodeWithoutSourceMap(node.left);
write(`", `);
}
const internalExportChanged = isAssignment &&
isNameOfExportedDeclarationInNonES6Module(node.left);
if (internalExportChanged) {
// export { foo }
// emit foo = 2 as exports.foo = foo = 2
emitAliasEqual(<Identifier>node.left);
}
if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken || node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) {
// Downleveled emit exponentiation operator using Math.pow
2015-10-03 03:50:45 +02:00
emitExponentiationOperator(node);
}
else {
emit(node.left);
2015-10-09 08:25:05 +02:00
// Add indentation before emit the operator if the operator is on different line
// For example:
// 3
// + 2;
// emitted as
// 3
// + 2;
2015-11-04 23:02:33 +01:00
const indentedBeforeOperator = indentIfOnDifferentLines(node, node.left, node.operatorToken, node.operatorToken.kind !== SyntaxKind.CommaToken ? " " : undefined);
write(tokenToString(node.operatorToken.kind));
2015-11-04 23:02:33 +01:00
const indentedAfterOperator = indentIfOnDifferentLines(node, node.operatorToken, node.right, " ");
emit(node.right);
decreaseIndentIf(indentedBeforeOperator, indentedAfterOperator);
}
if (externalExportChanged) {
write(")");
}
}
2014-07-13 01:04:16 +02:00
}
function synthesizedNodeStartsOnNewLine(node: Node) {
2015-02-27 20:28:14 +01:00
return nodeIsSynthesized(node) && (<SynthesizedNode>node).startsOnNewLine;
}
function emitConditionalExpression(node: ConditionalExpression) {
emit(node.condition);
2015-11-04 23:02:33 +01:00
const indentedBeforeQuestion = indentIfOnDifferentLines(node, node.condition, node.questionToken, " ");
write("?");
2015-11-04 23:02:33 +01:00
const indentedAfterQuestion = indentIfOnDifferentLines(node, node.questionToken, node.whenTrue, " ");
emit(node.whenTrue);
2015-03-09 09:07:02 +01:00
decreaseIndentIf(indentedBeforeQuestion, indentedAfterQuestion);
2015-11-04 23:02:33 +01:00
const indentedBeforeColon = indentIfOnDifferentLines(node, node.whenTrue, node.colonToken, " ");
write(":");
2015-11-04 23:02:33 +01:00
const indentedAfterColon = indentIfOnDifferentLines(node, node.colonToken, node.whenFalse, " ");
emit(node.whenFalse);
2015-03-09 09:07:02 +01:00
decreaseIndentIf(indentedBeforeColon, indentedAfterColon);
}
2015-07-09 23:44:47 +02:00
// Helper function to decrease the indent if we previously indented. Allows multiple
// previous indent values to be considered at a time. This also allows caller to just
// call this once, passing in all their appropriate indent values, instead of needing
// to call this helper function multiple times.
2015-03-09 09:07:02 +01:00
function decreaseIndentIf(value1: boolean, value2?: boolean) {
if (value1) {
decreaseIndent();
}
2015-03-09 09:07:02 +01:00
if (value2) {
decreaseIndent();
}
}
function isSingleLineEmptyBlock(node: Node) {
if (node && node.kind === SyntaxKind.Block) {
2015-11-04 23:02:33 +01:00
const block = <Block>node;
return block.statements.length === 0 && nodeEndIsOnSameLineAsNodeStart(block, block);
}
}
function emitBlock(node: Block) {
if (isSingleLineEmptyBlock(node)) {
emitToken(SyntaxKind.OpenBraceToken, node.pos);
write(" ");
emitToken(SyntaxKind.CloseBraceToken, node.statements.end);
return;
}
emitToken(SyntaxKind.OpenBraceToken, node.pos);
2014-07-13 01:04:16 +02:00
increaseIndent();
if (node.kind === SyntaxKind.ModuleBlock) {
Debug.assert(node.parent.kind === SyntaxKind.ModuleDeclaration);
emitCaptureThisForNodeIfNecessary(node.parent);
}
emitLines(node.statements);
if (node.kind === SyntaxKind.ModuleBlock) {
emitTempDeclarations(/*newLine*/ true);
}
2014-07-13 01:04:16 +02:00
decreaseIndent();
writeLine();
emitToken(SyntaxKind.CloseBraceToken, node.statements.end);
}
function emitEmbeddedStatement(node: Node) {
if (node.kind === SyntaxKind.Block) {
write(" ");
emit(<Block>node);
}
else {
increaseIndent();
writeLine();
emit(node);
decreaseIndent();
}
}
function emitExpressionStatement(node: ExpressionStatement) {
emitParenthesizedIf(node.expression, /*parenthesized*/ node.expression.kind === SyntaxKind.ArrowFunction);
write(";");
}
function emitIfStatement(node: IfStatement) {
2015-03-13 20:34:12 +01:00
let 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);
}
}
}
function emitDoStatement(node: DoStatement) {
emitLoop(node, emitDoStatementWorker);
}
function emitDoStatementWorker(node: DoStatement, loop: ConvertedLoop) {
write("do");
if (loop) {
2015-11-14 02:43:53 +01:00
emitConvertedLoopCall(loop, /*emitAsBlock*/ true);
}
else {
2015-11-14 02:43:53 +01:00
emitNormalLoopBody(node, /*emitAsEmbeddedStatement*/ true);
}
if (node.statement.kind === SyntaxKind.Block) {
write(" ");
}
else {
writeLine();
}
write("while (");
emit(node.expression);
write(");");
}
function emitWhileStatement(node: WhileStatement) {
emitLoop(node, emitWhileStatementWorker);
}
function emitWhileStatementWorker(node: WhileStatement, loop: ConvertedLoop) {
write("while (");
emit(node.expression);
write(")");
if (loop) {
2015-11-14 02:43:53 +01:00
emitConvertedLoopCall(loop, /*emitAsBlock*/ true);
}
else {
2015-11-14 02:43:53 +01:00
emitNormalLoopBody(node, /*emitAsEmbeddedStatement*/ true);
}
}
/**
* Returns true if start of variable declaration list was emitted.
* Returns false if nothing was written - this can happen for source file level variable declarations
* in system modules where such variable declarations are hoisted.
2015-04-22 07:27:33 +02:00
*/
function tryEmitStartOfVariableDeclarationList(decl: VariableDeclarationList): boolean {
if (shouldHoistVariable(decl, /*checkIfSourceFileLevelDecl*/ true)) {
// variables in variable declaration list were already hoisted
return false;
2015-04-10 21:10:38 +02:00
}
2015-04-13 10:32:16 +02:00
if (convertedLoopState && (getCombinedNodeFlags(decl) & NodeFlags.BlockScoped) === 0) {
// we are inside a converted loop - this can only happen in downlevel scenarios
// record names for all variable declarations
for (const varDecl of decl.declarations) {
hoistVariableDeclarationFromLoop(convertedLoopState, varDecl);
}
return false;
}
emitStart(decl);
if (decl && languageVersion >= ScriptTarget.ES6) {
if (isLet(decl)) {
write("let ");
}
else if (isConst(decl)) {
write("const ");
}
else {
write("var ");
}
}
else {
write("var ");
}
// Note here we specifically dont emit end so that if we are going to emit binding pattern
// we can alter the source map correctly
return true;
}
function emitVariableDeclarationListSkippingUninitializedEntries(list: VariableDeclarationList): boolean {
let started = false;
2015-11-04 23:02:33 +01:00
for (const decl of list.declarations) {
if (!decl.initializer) {
continue;
}
if (!started) {
started = true;
}
else {
write(", ");
}
emit(decl);
}
return started;
}
interface ConvertedLoop {
functionName: string;
paramList: string;
state: ConvertedLoopState;
}
function shouldConvertLoopBody(node: IterationStatement): boolean {
return languageVersion < ScriptTarget.ES6 &&
(resolver.getNodeCheckFlags(node) & NodeCheckFlags.LoopWithCapturedBlockScopedBinding) !== 0;
}
function emitLoop(node: IterationStatement, loopEmitter: (n: IterationStatement, convertedLoop: ConvertedLoop) => void): void {
const shouldConvert = shouldConvertLoopBody(node);
if (!shouldConvert) {
loopEmitter(node, /* convertedLoop*/ undefined);
}
else {
const loop = convertLoopBody(node);
if (node.parent.kind === SyntaxKind.LabeledStatement) {
// if parent of the loop was labeled statement - attach the label to loop skipping converted loop body
emitLabelAndColon(<LabeledStatement>node.parent);
}
loopEmitter(node, loop);
}
}
function convertLoopBody(node: IterationStatement): ConvertedLoop {
const functionName = makeUniqueName("_loop");
let loopInitializer: VariableDeclarationList;
switch (node.kind) {
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
const initializer = (<ForStatement | ForInStatement | ForOfStatement>node).initializer;
if (initializer && initializer.kind === SyntaxKind.VariableDeclarationList) {
loopInitializer = <VariableDeclarationList>(<ForStatement | ForInStatement | ForOfStatement>node).initializer;
}
break;
}
let loopParameters: string[];
let loopOutParameters: LoopOutParameter[];
if (loopInitializer && (getCombinedNodeFlags(loopInitializer) & NodeFlags.BlockScoped)) {
// if loop initializer contains block scoped variables - they should be passed to converted loop body as parameters
loopParameters = [];
for (const varDeclaration of loopInitializer.declarations) {
processVariableDeclaration(varDeclaration.name);
}
}
const bodyIsBlock = node.statement.kind === SyntaxKind.Block;
const paramList = loopParameters ? loopParameters.join(", ") : "";
writeLine();
write(`var ${functionName} = function(${paramList})`);
const convertedOuterLoopState = convertedLoopState;
convertedLoopState = { loopOutParameters };
if (convertedOuterLoopState) {
2015-10-19 23:10:27 +02:00
// convertedOuterLoopState !== undefined means that this converted loop is nested in another converted loop.
// if outer converted loop has already accumulated some state - pass it through
if (convertedOuterLoopState.argumentsName) {
// outer loop has already used 'arguments' so we've already have some name to alias it
// use the same name in all nested loops
convertedLoopState.argumentsName = convertedOuterLoopState.argumentsName;
}
if (convertedOuterLoopState.thisName) {
// outer loop has already used 'this' so we've already have some name to alias it
// use the same name in all nested loops
convertedLoopState.thisName = convertedOuterLoopState.thisName;
}
if (convertedOuterLoopState.hoistedLocalVariables) {
// we've already collected some non-block scoped variable declarations in enclosing loop
// use the same storage in nested loop
convertedLoopState.hoistedLocalVariables = convertedOuterLoopState.hoistedLocalVariables;
}
}
write(" {");
writeLine();
increaseIndent();
if (bodyIsBlock) {
emitLines((<Block>node.statement).statements);
}
else {
emit(node.statement);
}
writeLine();
// end of loop body -> copy out parameter
copyLoopOutParameters(convertedLoopState, CopyDirection.ToOutParameter, /*emitAsStatements*/true);
decreaseIndent();
writeLine();
write("};");
writeLine();
if (loopOutParameters) {
// declare variables to hold out params for loop body
write(`var `);
for (let i = 0; i < loopOutParameters.length; i++) {
if (i !== 0) {
write(", ");
}
write(loopOutParameters[i].outParamName);
}
write(";");
writeLine();
}
if (convertedLoopState.argumentsName) {
// if alias for arguments is set
if (convertedOuterLoopState) {
// pass it to outer converted loop
convertedOuterLoopState.argumentsName = convertedLoopState.argumentsName;
}
else {
2015-10-19 23:10:27 +02:00
// this is top level converted loop and we need to create an alias for 'arguments' object
write(`var ${convertedLoopState.argumentsName} = arguments;`);
writeLine();
}
}
if (convertedLoopState.thisName) {
// if alias for this is set
if (convertedOuterLoopState) {
// pass it to outer converted loop
convertedOuterLoopState.thisName = convertedLoopState.thisName;
}
else {
// this is top level converted loop so we need to create an alias for 'this' here
2015-12-01 21:12:31 +01:00
// NOTE:
// if converted loops were all nested in arrow function then we'll always emit '_this' so convertedLoopState.thisName will not be set.
// If it is set this means that all nested loops are not nested in arrow function and it is safe to capture 'this'.
write(`var ${convertedLoopState.thisName} = this;`);
writeLine();
}
}
if (convertedLoopState.hoistedLocalVariables) {
// if hoistedLocalVariables !== undefined this means that we've possibly collected some variable declarations to be hoisted later
if (convertedOuterLoopState) {
// pass them to outer converted loop
convertedOuterLoopState.hoistedLocalVariables = convertedLoopState.hoistedLocalVariables;
}
else {
// deduplicate and hoist collected variable declarations
write("var ");
let seen: Map<string>;
for (const id of convertedLoopState.hoistedLocalVariables) {
// Don't initialize seen unless we have at least one element.
// Emit a comma to separate for all but the first element.
if (!seen) {
2016-08-11 01:47:06 +02:00
seen = createMap<string>();
}
else {
write(", ");
}
2015-10-19 23:10:27 +02:00
2016-08-15 21:03:39 +02:00
if (!(id.text in seen)) {
emit(id);
seen[id.text] = id.text;
}
}
write(";");
writeLine();
}
}
const currentLoopState = convertedLoopState;
convertedLoopState = convertedOuterLoopState;
return { functionName, paramList, state: currentLoopState };
function processVariableDeclaration(name: Identifier | BindingPattern): void {
if (name.kind === SyntaxKind.Identifier) {
const nameText = isNameOfNestedBlockScopedRedeclarationOrCapturedBinding(<Identifier>name)
? getGeneratedNameForNode(name)
: (<Identifier>name).text;
loopParameters.push(nameText);
if (resolver.getNodeCheckFlags(name.parent) & NodeCheckFlags.NeedsLoopOutParameter) {
const reassignedVariable = { originalName: <Identifier>name, outParamName: makeUniqueName(`out_${nameText}`) };
(loopOutParameters || (loopOutParameters = [])).push(reassignedVariable);
}
}
else {
for (const element of (<BindingPattern>name).elements) {
processVariableDeclaration(element.name);
}
}
}
}
function emitNormalLoopBody(node: IterationStatement, emitAsEmbeddedStatement: boolean): void {
let saveAllowedNonLabeledJumps: Jump;
if (convertedLoopState) {
// we get here if we are trying to emit normal loop loop inside converted loop
// set allowedNonLabeledJumps to Break | Continue to mark that break\continue inside the loop should be emitted as is
saveAllowedNonLabeledJumps = convertedLoopState.allowedNonLabeledJumps;
convertedLoopState.allowedNonLabeledJumps = Jump.Break | Jump.Continue;
}
if (emitAsEmbeddedStatement) {
emitEmbeddedStatement(node.statement);
}
else if (node.statement.kind === SyntaxKind.Block) {
emitLines((<Block>node.statement).statements);
}
else {
writeLine();
emit(node.statement);
}
if (convertedLoopState) {
convertedLoopState.allowedNonLabeledJumps = saveAllowedNonLabeledJumps;
}
}
function copyLoopOutParameters(state: ConvertedLoopState, copyDirection: CopyDirection, emitAsStatements: boolean) {
if (state.loopOutParameters) {
for (const outParam of state.loopOutParameters) {
if (copyDirection === CopyDirection.ToOriginal) {
emitIdentifier(outParam.originalName);
write(` = ${outParam.outParamName}`);
}
else {
write(`${outParam.outParamName} = `);
emitIdentifier(outParam.originalName);
}
if (emitAsStatements) {
write(";");
writeLine();
}
else {
write(", ");
}
}
}
}
function emitConvertedLoopCall(loop: ConvertedLoop, emitAsBlock: boolean): void {
if (emitAsBlock) {
write(" {");
writeLine();
increaseIndent();
}
// loop is considered simple if it does not have any return statements or break\continue that transfer control outside of the loop
// simple loops are emitted as just 'loop()';
// NOTE: if loop uses only 'continue' it still will be emitted as simple loop
const isSimpleLoop =
!(loop.state.nonLocalJumps & ~Jump.Continue) &&
!loop.state.labeledNonLocalBreaks &&
!loop.state.labeledNonLocalContinues;
const loopResult = makeUniqueName("state");
if (!isSimpleLoop) {
write(`var ${loopResult} = `);
}
write(`${loop.functionName}(${loop.paramList});`);
writeLine();
copyLoopOutParameters(loop.state, CopyDirection.ToOriginal, /*emitAsStatements*/ true);
if (!isSimpleLoop) {
// for non simple loops we need to store result returned from converted loop function and use it to do dispatching
// converted loop function can return:
// - object - used when body of the converted loop contains return statement. Property "value" of this object stores retuned value
// - string - used to dispatch jumps. "break" and "continue" are used to non-labeled jumps, other values are used to transfer control to
// different labels
writeLine();
if (loop.state.nonLocalJumps & Jump.Return) {
write(`if (typeof ${loopResult} === "object") `);
if (convertedLoopState) {
// we are currently nested in another converted loop - return unwrapped result
write(`return ${loopResult};`);
// propagate 'hasReturn' flag to outer loop
convertedLoopState.nonLocalJumps |= Jump.Return;
}
else {
// top level converted loop - return unwrapped value
write(`return ${loopResult}.value;`);
}
writeLine();
}
if (loop.state.nonLocalJumps & Jump.Break) {
write(`if (${loopResult} === "break") break;`);
writeLine();
}
// in case of labeled breaks emit code that either breaks to some known label inside outer loop or delegates jump decision to outer loop
emitDispatchTableForLabeledJumps(loopResult, loop.state, convertedLoopState);
// in case of 'continue' we'll just fallthough here
}
if (emitAsBlock) {
writeLine();
decreaseIndent();
write("}");
}
function emitDispatchTableForLabeledJumps(loopResultVariable: string, currentLoop: ConvertedLoopState, outerLoop: ConvertedLoopState) {
if (!currentLoop.labeledNonLocalBreaks && !currentLoop.labeledNonLocalContinues) {
return;
}
write(`switch(${loopResultVariable}) {`);
increaseIndent();
2015-11-14 02:43:53 +01:00
emitDispatchEntriesForLabeledJumps(currentLoop.labeledNonLocalBreaks, /*isBreak*/ true, loopResultVariable, outerLoop);
emitDispatchEntriesForLabeledJumps(currentLoop.labeledNonLocalContinues, /*isBreak*/ false, loopResultVariable, outerLoop);
decreaseIndent();
writeLine();
write("}");
}
function emitDispatchEntriesForLabeledJumps(table: Map<string>, isBreak: boolean, loopResultVariable: string, outerLoop: ConvertedLoopState): void {
if (!table) {
return;
}
2015-11-04 23:02:33 +01:00
for (const labelText in table) {
const labelMarker = table[labelText];
writeLine();
write(`case "${labelMarker}": `);
// if there are no outer converted loop or outer label in question is located inside outer converted loop
// then emit labeled break\continue
// otherwise propagate pair 'label -> marker' to outer converted loop and emit 'return labelMarker' so outer loop can later decide what to do
if (!outerLoop || (outerLoop.labels && outerLoop.labels[labelText])) {
if (isBreak) {
write("break ");
}
else {
write("continue ");
}
write(`${labelText};`);
}
else {
setLabeledJump(outerLoop, isBreak, labelText, labelMarker);
write(`return ${loopResultVariable};`);
}
}
}
}
function emitForStatement(node: ForStatement) {
emitLoop(node, emitForStatementWorker);
}
function emitForStatementWorker(node: ForStatement, loop: ConvertedLoop) {
2015-03-13 20:34:12 +01:00
let endPos = emitToken(SyntaxKind.ForKeyword, node.pos);
write(" ");
endPos = emitToken(SyntaxKind.OpenParenToken, endPos);
if (node.initializer && node.initializer.kind === SyntaxKind.VariableDeclarationList) {
2015-11-04 23:02:33 +01:00
const variableDeclarationList = <VariableDeclarationList>node.initializer;
const startIsEmitted = tryEmitStartOfVariableDeclarationList(variableDeclarationList);
if (startIsEmitted) {
emitCommaList(variableDeclarationList.declarations);
}
else {
emitVariableDeclarationListSkippingUninitializedEntries(variableDeclarationList);
}
}
else if (node.initializer) {
emit(node.initializer);
}
write(";");
emitOptional(" ", node.condition);
write(";");
emitOptional(" ", node.incrementor);
write(")");
if (loop) {
2015-11-14 02:43:53 +01:00
emitConvertedLoopCall(loop, /*emitAsBlock*/ true);
}
else {
2015-11-14 02:43:53 +01:00
emitNormalLoopBody(node, /*emitAsEmbeddedStatement*/ true);
}
}
function emitForInOrForOfStatement(node: ForInStatement | ForOfStatement) {
2015-03-03 20:43:32 +01:00
if (languageVersion < ScriptTarget.ES6 && node.kind === SyntaxKind.ForOfStatement) {
emitLoop(node, emitDownLevelForOfStatementWorker);
2015-03-03 20:43:32 +01:00
}
else {
emitLoop(node, emitForInOrForOfStatementWorker);
2015-03-03 20:43:32 +01:00
}
}
function emitForInOrForOfStatementWorker(node: ForInStatement | ForOfStatement, loop: ConvertedLoop) {
2015-03-13 20:34:12 +01:00
let endPos = emitToken(SyntaxKind.ForKeyword, node.pos);
write(" ");
endPos = emitToken(SyntaxKind.OpenParenToken, endPos);
if (node.initializer.kind === SyntaxKind.VariableDeclarationList) {
2015-11-04 23:02:33 +01:00
const variableDeclarationList = <VariableDeclarationList>node.initializer;
if (variableDeclarationList.declarations.length >= 1) {
tryEmitStartOfVariableDeclarationList(variableDeclarationList);
emit(variableDeclarationList.declarations[0]);
}
}
else {
emit(node.initializer);
}
if (node.kind === SyntaxKind.ForInStatement) {
write(" in ");
}
else {
write(" of ");
}
emit(node.expression);
emitToken(SyntaxKind.CloseParenToken, node.expression.end);
if (loop) {
2015-11-14 02:43:53 +01:00
emitConvertedLoopCall(loop, /*emitAsBlock*/ true);
}
else {
2015-11-14 02:43:53 +01:00
emitNormalLoopBody(node, /*emitAsEmbeddedStatement*/ true);
}
}
function emitDownLevelForOfStatementWorker(node: ForOfStatement, loop: ConvertedLoop) {
2015-03-03 20:43:32 +01:00
// The following ES6 code:
//
2015-03-13 20:34:12 +01:00
// for (let v of expr) { }
2015-03-03 20:43:32 +01:00
//
// should be emitted as
//
2015-03-13 20:34:12 +01:00
// for (let _i = 0, _a = expr; _i < _a.length; _i++) {
// let v = _a[_i];
2015-03-03 20:43:32 +01:00
// }
//
// where _a and _i are temps emitted to capture the RHS and the counter,
// respectively.
2015-03-13 20:34:12 +01:00
// When the left hand side is an expression instead of a let declaration,
// the "let v" is not emitted.
2015-03-03 20:43:32 +01:00
// When the left hand side is a let/const, the v is renamed if there is
// another v in scope.
// Note that all assignments to the LHS are emitted in the body, including
// all destructuring.
// Note also that because an extra statement is needed to assign to the LHS,
// for-of bodies are always emitted as blocks.
2015-06-26 01:24:41 +02:00
2015-03-13 20:34:12 +01:00
let endPos = emitToken(SyntaxKind.ForKeyword, node.pos);
2015-03-03 20:43:32 +01:00
write(" ");
endPos = emitToken(SyntaxKind.OpenParenToken, endPos);
2015-06-26 01:24:41 +02:00
2015-03-13 20:34:12 +01:00
// Do not emit the LHS let declaration yet, because it might contain destructuring.
2015-06-26 01:24:41 +02:00
2015-03-09 21:32:02 +01:00
// Do not call recordTempDeclaration because we are declaring the temps
2015-03-03 20:43:32 +01:00
// right here. Recording means they will be declared later.
// In the case where the user wrote an identifier as the RHS, like this:
//
2015-03-13 20:34:12 +01:00
// for (let v of arr) { }
//
// we can't reuse 'arr' because it might be modified within the body of the loop.
2015-11-04 23:02:33 +01:00
const counter = createTempVariable(TempFlags._i);
const rhsReference = createSynthesizedNode(SyntaxKind.Identifier) as Identifier;
rhsReference.text = node.expression.kind === SyntaxKind.Identifier ?
makeUniqueName((<Identifier>node.expression).text) :
makeTempVariableName(TempFlags.Auto);
2015-03-13 20:34:12 +01:00
// This is the let keyword for the counter and rhsReference. The let keyword for
// the LHS will be emitted inside the body.
emitStart(node.expression);
write("var ");
2015-06-26 01:24:41 +02:00
// _i = 0
emitNodeWithoutSourceMap(counter);
write(" = 0");
emitEnd(node.expression);
// , _a = expr
write(", ");
emitStart(node.expression);
emitNodeWithoutSourceMap(rhsReference);
write(" = ");
emitNodeWithoutSourceMap(node.expression);
emitEnd(node.expression);
2015-03-03 20:43:32 +01:00
write("; ");
2015-06-26 01:24:41 +02:00
2015-03-03 20:43:32 +01:00
// _i < _a.length;
emitStart(node.expression);
emitNodeWithoutSourceMap(counter);
2015-03-03 20:43:32 +01:00
write(" < ");
emitNodeWithCommentsAndWithoutSourcemap(rhsReference);
write(".length");
emitEnd(node.expression);
write("; ");
2015-06-26 01:24:41 +02:00
2015-03-03 20:43:32 +01:00
// _i++)
emitStart(node.expression);
emitNodeWithoutSourceMap(counter);
2015-03-03 20:43:32 +01:00
write("++");
emitEnd(node.expression);
2015-03-03 20:43:32 +01:00
emitToken(SyntaxKind.CloseParenToken, node.expression.end);
2015-06-26 01:24:41 +02:00
2015-03-03 20:43:32 +01:00
// Body
write(" {");
writeLine();
increaseIndent();
2015-06-26 01:24:41 +02:00
2015-03-03 20:43:32 +01:00
// Initialize LHS
2015-03-13 20:34:12 +01:00
// let v = _a[_i];
2015-11-04 23:02:33 +01:00
const rhsIterationValue = createElementAccessExpression(rhsReference, counter);
emitStart(node.initializer);
if (node.initializer.kind === SyntaxKind.VariableDeclarationList) {
2015-03-03 23:45:16 +01:00
write("var ");
2015-11-04 23:02:33 +01:00
const variableDeclarationList = <VariableDeclarationList>node.initializer;
2015-03-09 21:32:02 +01:00
if (variableDeclarationList.declarations.length > 0) {
2015-11-04 23:02:33 +01:00
const declaration = variableDeclarationList.declarations[0];
if (isBindingPattern(declaration.name)) {
// This works whether the declaration is a var, let, or const.
// It will use rhsIterationValue _a[_i] as the initializer.
2015-03-09 21:32:02 +01:00
emitDestructuring(declaration, /*isAssignmentExpressionStatement*/ false, rhsIterationValue);
}
else {
// The following call does not include the initializer, so we have
// to emit it separately.
emitNodeWithCommentsAndWithoutSourcemap(declaration);
write(" = ");
emitNodeWithoutSourceMap(rhsIterationValue);
}
}
else {
// It's an empty declaration list. This can only happen in an error case, if the user wrote
2015-03-13 20:34:12 +01:00
// for (let of []) {}
emitNodeWithoutSourceMap(createTempVariable(TempFlags.Auto));
write(" = ");
emitNodeWithoutSourceMap(rhsIterationValue);
}
2015-03-03 23:45:16 +01:00
}
else {
// Initializer is an expression. Emit the expression in the body, so that it's
// evaluated on every iteration.
2015-11-04 23:02:33 +01:00
const assignmentExpression = createBinaryExpression(<Expression>node.initializer, SyntaxKind.EqualsToken, rhsIterationValue, /*startsOnNewLine*/ false);
if (node.initializer.kind === SyntaxKind.ArrayLiteralExpression || node.initializer.kind === SyntaxKind.ObjectLiteralExpression) {
// This is a destructuring pattern, so call emitDestructuring instead of emit. Calling emit will not work, because it will cause
// the BinaryExpression to be passed in instead of the expression statement, which will cause emitDestructuring to crash.
emitDestructuring(assignmentExpression, /*isAssignmentExpressionStatement*/ true, /*value*/ undefined);
}
else {
emitNodeWithCommentsAndWithoutSourcemap(assignmentExpression);
}
2015-03-03 23:45:16 +01:00
}
emitEnd(node.initializer);
write(";");
if (loop) {
writeLine();
2015-11-14 02:43:53 +01:00
emitConvertedLoopCall(loop, /*emitAsBlock*/ false);
2015-03-03 20:43:32 +01:00
}
else {
2015-11-14 02:43:53 +01:00
emitNormalLoopBody(node, /*emitAsEmbeddedStatement*/ false);
2015-03-03 20:43:32 +01:00
}
2015-03-03 20:43:32 +01:00
writeLine();
decreaseIndent();
write("}");
}
function emitBreakOrContinueStatement(node: BreakOrContinueStatement) {
if (convertedLoopState) {
// check if we can emit break\continue as is
// it is possible if either
// - break\continue is statement labeled and label is located inside the converted loop
// - break\continue is non-labeled and located in non-converted loop\switch statement
const jump = node.kind === SyntaxKind.BreakStatement ? Jump.Break : Jump.Continue;
const canUseBreakOrContinue =
(node.label && convertedLoopState.labels && convertedLoopState.labels[node.label.text]) ||
(!node.label && (convertedLoopState.allowedNonLabeledJumps & jump));
if (!canUseBreakOrContinue) {
write ("return ");
// explicit exit from loop -> copy out parameters
copyLoopOutParameters(convertedLoopState, CopyDirection.ToOutParameter, /*emitAsStatements*/ false);
if (!node.label) {
if (node.kind === SyntaxKind.BreakStatement) {
convertedLoopState.nonLocalJumps |= Jump.Break;
write(`"break";`);
}
else {
convertedLoopState.nonLocalJumps |= Jump.Continue;
// note: return value is emitted only to simplify debugging, call to converted loop body does not do any dispatching on it.
write(`"continue";`);
}
}
else {
let labelMarker: string;
if (node.kind === SyntaxKind.BreakStatement) {
labelMarker = `break-${node.label.text}`;
2015-11-14 02:43:53 +01:00
setLabeledJump(convertedLoopState, /*isBreak*/ true, node.label.text, labelMarker);
}
else {
labelMarker = `continue-${node.label.text}`;
2015-11-14 02:43:53 +01:00
setLabeledJump(convertedLoopState, /*isBreak*/ false, node.label.text, labelMarker);
}
write(`"${labelMarker}";`);
}
return;
}
}
emitToken(node.kind === SyntaxKind.BreakStatement ? SyntaxKind.BreakKeyword : SyntaxKind.ContinueKeyword, node.pos);
emitOptional(" ", node.label);
write(";");
}
function emitReturnStatement(node: ReturnStatement) {
if (convertedLoopState) {
convertedLoopState.nonLocalJumps |= Jump.Return;
2015-10-19 23:10:27 +02:00
write("return { value: ");
if (node.expression) {
emit(node.expression);
}
else {
2015-10-19 23:10:27 +02:00
write("void 0");
}
write(" };");
return;
}
emitToken(SyntaxKind.ReturnKeyword, node.pos);
emitOptional(" ", node.expression);
write(";");
}
function emitWithStatement(node: WithStatement) {
write("with (");
emit(node.expression);
write(")");
emitEmbeddedStatement(node.statement);
}
function emitSwitchStatement(node: SwitchStatement) {
2015-03-13 20:34:12 +01:00
let endPos = emitToken(SyntaxKind.SwitchKeyword, node.pos);
write(" ");
emitToken(SyntaxKind.OpenParenToken, endPos);
emit(node.expression);
endPos = emitToken(SyntaxKind.CloseParenToken, node.expression.end);
write(" ");
let saveAllowedNonLabeledJumps: Jump;
if (convertedLoopState) {
saveAllowedNonLabeledJumps = convertedLoopState.allowedNonLabeledJumps;
// for switch statement allow only non-labeled break
convertedLoopState.allowedNonLabeledJumps |= Jump.Break;
}
2015-06-26 01:24:41 +02:00
emitCaseBlock(node.caseBlock, endPos);
if (convertedLoopState) {
convertedLoopState.allowedNonLabeledJumps = saveAllowedNonLabeledJumps;
}
2015-03-11 02:17:52 +01:00
}
function emitCaseBlock(node: CaseBlock, startPos: number): void {
emitToken(SyntaxKind.OpenBraceToken, startPos);
increaseIndent();
emitLines(node.clauses);
decreaseIndent();
writeLine();
emitToken(SyntaxKind.CloseBraceToken, node.clauses.end);
}
function nodeStartPositionsAreOnSameLine(node1: Node, node2: Node) {
return getLineOfLocalPositionFromLineMap(currentLineMap, skipTrivia(currentText, node1.pos)) ===
getLineOfLocalPositionFromLineMap(currentLineMap, skipTrivia(currentText, node2.pos));
}
function nodeEndPositionsAreOnSameLine(node1: Node, node2: Node) {
return getLineOfLocalPositionFromLineMap(currentLineMap, node1.end) ===
getLineOfLocalPositionFromLineMap(currentLineMap, node2.end);
}
function nodeEndIsOnSameLineAsNodeStart(node1: TextRange, node2: TextRange) {
return getLineOfLocalPositionFromLineMap(currentLineMap, node1.end) ===
getLineOfLocalPositionFromLineMap(currentLineMap, skipTrivia(currentText, node2.pos));
}
function emitCaseOrDefaultClause(node: CaseOrDefaultClause) {
if (node.kind === SyntaxKind.CaseClause) {
write("case ");
2014-12-02 07:50:03 +01:00
emit((<CaseClause>node).expression);
write(":");
}
else {
write("default:");
}
if (node.statements.length === 1 && nodeStartPositionsAreOnSameLine(node, node.statements[0])) {
write(" ");
emit(node.statements[0]);
}
else {
increaseIndent();
emitLines(node.statements);
decreaseIndent();
}
}
function emitThrowStatement(node: ThrowStatement) {
write("throw ");
emit(node.expression);
write(";");
}
function emitTryStatement(node: TryStatement) {
write("try ");
emit(node.tryBlock);
emit(node.catchClause);
if (node.finallyBlock) {
writeLine();
write("finally ");
emit(node.finallyBlock);
}
}
function emitCatchClause(node: CatchClause) {
writeLine();
2015-11-04 23:02:33 +01:00
const endPos = emitToken(SyntaxKind.CatchKeyword, node.pos);
write(" ");
emitToken(SyntaxKind.OpenParenToken, endPos);
emit(node.variableDeclaration);
emitToken(SyntaxKind.CloseParenToken, node.variableDeclaration ? node.variableDeclaration.end : endPos);
write(" ");
emitBlock(node.block);
}
function emitDebuggerStatement(node: Node) {
emitToken(SyntaxKind.DebuggerKeyword, node.pos);
write(";");
2014-07-13 01:04:16 +02:00
}
function emitLabelAndColon(node: LabeledStatement): void {
emit(node.label);
write(": ");
}
2015-10-20 00:51:20 +02:00
function emitLabeledStatement(node: LabeledStatement) {
if (!isIterationStatement(node.statement, /* lookInLabeledStatements */ false) || !shouldConvertLoopBody(<IterationStatement>node.statement)) {
emitLabelAndColon(node);
}
if (convertedLoopState) {
if (!convertedLoopState.labels) {
2016-08-11 01:47:06 +02:00
convertedLoopState.labels = createMap<string>();
}
convertedLoopState.labels[node.label.text] = node.label.text;
}
emit(node.statement);
if (convertedLoopState) {
convertedLoopState.labels[node.label.text] = undefined;
}
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
}
2015-02-15 17:25:24 +01:00
function emitContainingModuleName(node: Node) {
2015-11-04 23:02:33 +01:00
const container = getContainingModule(node);
2015-03-24 00:16:29 +01:00
write(container ? getGeneratedNameForNode(container) : "exports");
2015-02-15 17:25:24 +01:00
}
function emitModuleMemberName(node: Declaration) {
emitStart(node.name);
2014-12-17 03:50:34 +01:00
if (getCombinedNodeFlags(node) & NodeFlags.Export) {
2015-11-04 23:02:33 +01:00
const container = getContainingModule(node);
if (container) {
2015-03-24 00:16:29 +01:00
write(getGeneratedNameForNode(container));
write(".");
}
2015-09-17 22:26:04 +02:00
else if (modulekind !== ModuleKind.ES6 && modulekind !== ModuleKind.System) {
write("exports.");
}
}
emitNodeWithCommentsAndWithoutSourcemap(node.name);
emitEnd(node.name);
}
function createVoidZero(): Expression {
2015-11-04 23:02:33 +01:00
const zero = <LiteralExpression>createSynthesizedNode(SyntaxKind.NumericLiteral);
zero.text = "0";
2015-11-04 23:02:33 +01:00
const result = <VoidExpression>createSynthesizedNode(SyntaxKind.VoidExpression);
result.expression = zero;
return result;
}
2015-07-11 13:55:27 +02:00
function emitEs6ExportDefaultCompat(node: Node) {
2015-07-15 20:05:10 +02:00
if (node.parent.kind === SyntaxKind.SourceFile) {
Debug.assert(!!(node.flags & NodeFlags.Default) || node.kind === SyntaxKind.ExportAssignment);
2015-07-11 13:55:27 +02:00
// only allow export default at a source file level
2015-09-17 22:26:04 +02:00
if (modulekind === ModuleKind.CommonJS || modulekind === ModuleKind.AMD || modulekind === ModuleKind.UMD) {
if (!isEs6Module) {
if (languageVersion !== ScriptTarget.ES3) {
2015-07-27 13:52:57 +02:00
// default value of configurable, enumerable, writable are `false`.
write('Object.defineProperty(exports, "__esModule", { value: true });');
2015-07-11 13:55:27 +02:00
writeLine();
}
else {
2015-07-11 13:55:27 +02:00
write("exports.__esModule = true;");
writeLine();
}
2015-06-24 19:45:46 +02:00
}
}
}
}
function emitExportMemberAssignment(node: FunctionLikeDeclaration | ClassDeclaration) {
if (node.flags & NodeFlags.Export) {
writeLine();
emitStart(node);
2015-04-20 22:40:13 +02:00
2015-05-11 09:09:06 +02:00
// emit call to exporter only for top level nodes
2015-09-17 22:26:04 +02:00
if (modulekind === ModuleKind.System && node.parent === currentSourceFile) {
2015-04-20 22:40:13 +02:00
// emit export default <smth> as
// export("default", <smth>)
2015-04-10 21:10:38 +02:00
write(`${exportFunctionForFile}("`);
if (node.flags & NodeFlags.Default) {
write("default");
}
2015-04-10 21:10:38 +02:00
else {
emitNodeWithCommentsAndWithoutSourcemap(node.name);
2015-04-10 21:10:38 +02:00
}
write(`", `);
emitDeclarationName(node);
2015-06-26 01:24:41 +02:00
write(")");
}
else {
2015-04-10 21:10:38 +02:00
if (node.flags & NodeFlags.Default) {
2015-07-11 13:55:27 +02:00
emitEs6ExportDefaultCompat(node);
2015-04-20 22:40:13 +02:00
if (languageVersion === ScriptTarget.ES3) {
write('exports["default"]');
2015-06-24 19:45:46 +02:00
}
else {
2015-04-20 22:40:13 +02:00
write("exports.default");
}
2015-04-10 21:10:38 +02:00
}
else {
emitModuleMemberName(node);
}
write(" = ");
emitDeclarationName(node);
}
emitEnd(node);
write(";");
}
}
function emitExportMemberAssignments(name: Identifier) {
2015-09-17 22:26:04 +02:00
if (modulekind === ModuleKind.System) {
return;
}
2016-08-15 21:03:39 +02:00
if (!exportEquals && exportSpecifiers && name.text in exportSpecifiers) {
2015-11-04 23:02:33 +01:00
for (const specifier of exportSpecifiers[name.text]) {
writeLine();
emitStart(specifier.name);
emitContainingModuleName(specifier);
write(".");
2015-08-17 20:26:49 +02:00
emitNodeWithCommentsAndWithoutSourcemap(specifier.name);
emitEnd(specifier.name);
write(" = ");
emitExpressionIdentifier(name);
write(";");
}
}
}
function emitExportSpecifierInSystemModule(specifier: ExportSpecifier): void {
2015-09-17 22:26:04 +02:00
Debug.assert(modulekind === ModuleKind.System);
if (!resolver.getReferencedValueDeclaration(specifier.propertyName || specifier.name) && !resolver.isValueAliasDeclaration(specifier) ) {
return;
}
writeLine();
emitStart(specifier.name);
write(`${exportFunctionForFile}("`);
2015-08-17 20:26:49 +02:00
emitNodeWithCommentsAndWithoutSourcemap(specifier.name);
write(`", `);
emitExpressionIdentifier(specifier.propertyName || specifier.name);
write(")");
emitEnd(specifier.name);
write(";");
}
/**
* Emit an assignment to a given identifier, 'name', with a given expression, 'value'.
* @param name an identifier as a left-hand-side operand of the assignment
* @param value an expression as a right-hand-side operand of the assignment
* @param shouldEmitCommaBeforeAssignment a boolean indicating whether to prefix an assignment with comma
*/
function emitAssignment(name: Identifier, value: Expression, shouldEmitCommaBeforeAssignment: boolean, nodeForSourceMap: Node) {
2015-10-03 03:50:45 +02:00
if (shouldEmitCommaBeforeAssignment) {
write(", ");
}
2015-11-04 23:02:33 +01:00
const exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(name);
2015-10-03 03:50:45 +02:00
if (exportChanged) {
write(`${exportFunctionForFile}("`);
emitNodeWithCommentsAndWithoutSourcemap(name);
write(`", `);
}
2015-10-08 23:38:58 +02:00
const isVariableDeclarationOrBindingElement =
name.parent && (name.parent.kind === SyntaxKind.VariableDeclaration || name.parent.kind === SyntaxKind.BindingElement);
// If this is first var declaration, we need to start at var/let/const keyword instead
// otherwise use nodeForSourceMap as the start position
emitStart(isFirstVariableDeclaration(nodeForSourceMap) ? nodeForSourceMap.parent : nodeForSourceMap);
2015-12-03 23:20:25 +01:00
withTemporaryNoSourceMap(() => {
if (isVariableDeclarationOrBindingElement) {
emitModuleMemberName(<Declaration>name.parent);
}
else {
emit(name);
}
2015-10-03 03:50:45 +02:00
2015-12-03 23:20:25 +01:00
write(" = ");
emit(value);
});
emitEnd(nodeForSourceMap, /*stopOverridingSpan*/true);
2015-10-03 03:50:45 +02:00
if (exportChanged) {
write(")");
}
}
/**
* Create temporary variable, emit an assignment of the variable the given expression
* @param expression an expression to assign to the newly created temporary variable
* @param canDefineTempVariablesInPlace a boolean indicating whether you can define the temporary variable at an assignment location
* @param shouldEmitCommaBeforeAssignment a boolean indicating whether an assignment should prefix with comma
*/
function emitTempVariableAssignment(expression: Expression, canDefineTempVariablesInPlace: boolean, shouldEmitCommaBeforeAssignment: boolean, sourceMapNode?: Node): Identifier {
2015-11-04 23:02:33 +01:00
const identifier = createTempVariable(TempFlags.Auto);
if (!canDefineTempVariablesInPlace) {
recordTempDeclaration(identifier);
}
emitAssignment(identifier, expression, shouldEmitCommaBeforeAssignment, sourceMapNode || expression.parent);
return identifier;
2015-10-12 20:48:38 +02:00
}
function isFirstVariableDeclaration(root: Node) {
return root.kind === SyntaxKind.VariableDeclaration &&
root.parent.kind === SyntaxKind.VariableDeclarationList &&
(<VariableDeclarationList>root.parent).declarations[0] === root;
}
function emitDestructuring(root: BinaryExpression | VariableDeclaration | ParameterDeclaration, isAssignmentExpressionStatement: boolean, value?: Expression) {
2015-03-13 20:34:12 +01:00
let emitCount = 0;
2015-04-13 10:32:16 +02:00
2014-12-05 02:43:15 +01:00
// An exported declaration is actually emitted as an assignment (to a property on the module object), so
// temporary variables in an exported declaration need to have real declarations elsewhere
2015-04-10 21:10:38 +02:00
// Also temporary variables should be explicitly allocated for source level declarations when module target is system
// because actual variable declarations are hoisted
2015-04-22 07:27:33 +02:00
let canDefineTempVariablesInPlace = false;
2015-04-10 21:10:38 +02:00
if (root.kind === SyntaxKind.VariableDeclaration) {
2015-11-04 23:02:33 +01:00
const isExported = getCombinedNodeFlags(root) & NodeFlags.Export;
const isSourceLevelForSystemModuleKind = shouldHoistDeclarationInSystemJsModule(root);
2015-04-22 07:27:33 +02:00
canDefineTempVariablesInPlace = !isExported && !isSourceLevelForSystemModuleKind;
2015-04-10 21:10:38 +02:00
}
else if (root.kind === SyntaxKind.Parameter) {
2015-04-22 07:27:33 +02:00
canDefineTempVariablesInPlace = true;
2015-04-10 21:10:38 +02:00
}
2015-03-09 21:32:02 +01:00
if (root.kind === SyntaxKind.BinaryExpression) {
emitAssignmentExpression(<BinaryExpression>root);
}
else {
2015-03-09 21:32:02 +01:00
Debug.assert(!isAssignmentExpressionStatement);
// If first variable declaration of variable statement correct the start location
if (isFirstVariableDeclaration(root)) {
// Use emit location of "var " as next emit start entry
sourceMap.changeEmitSourcePos();
}
emitBindingElement(<BindingElement>root, value);
}
/**
* Ensures that there exists a declared identifier whose value holds the given expression.
* This function is useful to ensure that the expression's value can be read from in subsequent expressions.
* Unless 'reuseIdentifierExpressions' is false, 'expr' will be returned if it is just an identifier.
*
* @param expr the expression whose value needs to be bound.
* @param reuseIdentifierExpressions true if identifier expressions can simply be returned;
* false if it is necessary to always emit an identifier.
*/
function ensureIdentifier(expr: Expression, reuseIdentifierExpressions: boolean, sourceMapNode: Node): Expression {
if (expr.kind === SyntaxKind.Identifier && reuseIdentifierExpressions) {
return expr;
}
const identifier = emitTempVariableAssignment(expr, canDefineTempVariablesInPlace, emitCount > 0, sourceMapNode);
emitCount++;
return identifier;
}
function createDefaultValueCheck(value: Expression, defaultValue: Expression, sourceMapNode: Node): Expression {
// The value expression will be evaluated twice, so for anything but a simple identifier
// we need to generate a temporary variable
// If the temporary variable needs to be emitted use the source Map node for assignment of that statement
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, sourceMapNode);
// Return the expression 'value === void 0 ? defaultValue : value'
2015-11-04 23:02:33 +01:00
const equals = <BinaryExpression>createSynthesizedNode(SyntaxKind.BinaryExpression);
equals.left = value;
equals.operatorToken = createSynthesizedNode(SyntaxKind.EqualsEqualsEqualsToken);
equals.right = createVoidZero();
return createConditionalExpression(equals, defaultValue, value);
}
function createConditionalExpression(condition: Expression, whenTrue: Expression, whenFalse: Expression) {
2015-11-04 23:02:33 +01:00
const cond = <ConditionalExpression>createSynthesizedNode(SyntaxKind.ConditionalExpression);
cond.condition = condition;
cond.questionToken = createSynthesizedNode(SyntaxKind.QuestionToken);
cond.whenTrue = whenTrue;
cond.colonToken = createSynthesizedNode(SyntaxKind.ColonToken);
cond.whenFalse = whenFalse;
return cond;
}
function createNumericLiteral(value: number) {
2015-11-04 23:02:33 +01:00
const node = <LiteralExpression>createSynthesizedNode(SyntaxKind.NumericLiteral);
node.text = "" + value;
return node;
}
function createPropertyAccessForDestructuringProperty(object: Expression, propName: PropertyName): Expression {
let index: Expression;
const nameIsComputed = propName.kind === SyntaxKind.ComputedPropertyName;
if (nameIsComputed) {
// TODO to handle when we look into sourcemaps for computed properties, for now use propName
index = ensureIdentifier((<ComputedPropertyName>propName).expression, /*reuseIdentifierExpressions*/ false, propName);
}
else {
// We create a synthetic copy of the identifier in order to avoid the rewriting that might
// otherwise occur when the identifier is emitted.
index = <Identifier | LiteralExpression>createSynthesizedNode(propName.kind);
2016-01-22 01:16:52 +01:00
// We need to unescape identifier here because when parsing an identifier prefixing with "__"
2015-12-22 02:07:18 +01:00
// the parser need to append "_" in order to escape colliding with magic identifiers such as "__proto__"
// Therefore, in order to correctly emit identifiers that are written in original TypeScript file,
// we will unescapeIdentifier to remove additional underscore (if no underscore is added, the function will return original input string)
2015-12-22 01:23:51 +01:00
(<Identifier | LiteralExpression>index).text = unescapeIdentifier((<Identifier | LiteralExpression>propName).text);
}
return !nameIsComputed && index.kind === SyntaxKind.Identifier
? createPropertyAccessExpression(object, <Identifier>index)
: createElementAccessExpression(object, index);
}
function createSliceCall(value: Expression, sliceIndex: number): CallExpression {
2015-11-04 23:02:33 +01:00
const call = <CallExpression>createSynthesizedNode(SyntaxKind.CallExpression);
const sliceIdentifier = <Identifier>createSynthesizedNode(SyntaxKind.Identifier);
sliceIdentifier.text = "slice";
call.expression = createPropertyAccessExpression(value, sliceIdentifier);
call.arguments = <NodeArray<LiteralExpression>>createSynthesizedNodeArray();
call.arguments[0] = createNumericLiteral(sliceIndex);
return call;
}
function emitObjectLiteralAssignment(target: ObjectLiteralExpression, value: Expression, sourceMapNode: Node) {
2015-11-04 23:02:33 +01:00
const properties = target.properties;
if (properties.length !== 1) {
// For anything but a single element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once.
// When doing so we want to highlight the passed in source map node since thats the one needing this temp assignment
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, sourceMapNode);
}
2015-11-04 23:02:33 +01:00
for (const p of properties) {
if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) {
2015-11-04 23:02:33 +01:00
const propName = <Identifier | LiteralExpression>(<PropertyAssignment>p).name;
const target = p.kind === SyntaxKind.ShorthandPropertyAssignment ? <ShorthandPropertyAssignment>p : (<PropertyAssignment>p).initializer || propName;
// Assignment for target = value.propName should highlight whole property, hence use p as source map node
emitDestructuringAssignment(target, createPropertyAccessForDestructuringProperty(value, propName), p);
}
}
}
function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, sourceMapNode: Node) {
2015-11-04 23:02:33 +01:00
const elements = target.elements;
if (elements.length !== 1) {
// For anything but a single element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once.
// When doing so we want to highlight the passed in source map node since thats the one needing this temp assignment
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, sourceMapNode);
}
2015-03-13 20:34:12 +01:00
for (let i = 0; i < elements.length; i++) {
2015-11-04 23:02:33 +01:00
const e = elements[i];
if (e.kind !== SyntaxKind.OmittedExpression) {
// Assignment for target = value.propName should highlight whole property, hence use e as source map node
if (e.kind !== SyntaxKind.SpreadElementExpression) {
emitDestructuringAssignment(e, createElementAccessExpression(value, createNumericLiteral(i)), e);
}
2015-04-11 03:56:11 +02:00
else if (i === elements.length - 1) {
emitDestructuringAssignment((<SpreadElementExpression>e).expression, createSliceCall(value, i), e);
}
}
}
}
function emitDestructuringAssignment(target: Expression | ShorthandPropertyAssignment, value: Expression, sourceMapNode: Node) {
// When emitting target = value use source map node to highlight, including any temporary assignments needed for this
if (target.kind === SyntaxKind.ShorthandPropertyAssignment) {
if ((<ShorthandPropertyAssignment>target).objectAssignmentInitializer) {
value = createDefaultValueCheck(value, (<ShorthandPropertyAssignment>target).objectAssignmentInitializer, sourceMapNode);
}
target = (<ShorthandPropertyAssignment>target).name;
}
else if (target.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>target).operatorToken.kind === SyntaxKind.EqualsToken) {
value = createDefaultValueCheck(value, (<BinaryExpression>target).right, sourceMapNode);
target = (<BinaryExpression>target).left;
}
if (target.kind === SyntaxKind.ObjectLiteralExpression) {
emitObjectLiteralAssignment(<ObjectLiteralExpression>target, value, sourceMapNode);
}
else if (target.kind === SyntaxKind.ArrayLiteralExpression) {
emitArrayLiteralAssignment(<ArrayLiteralExpression>target, value, sourceMapNode);
}
else {
emitAssignment(<Identifier>target, value, /*shouldEmitCommaBeforeAssignment*/ emitCount > 0, sourceMapNode);
emitCount++;
}
}
2015-03-09 21:32:02 +01:00
function emitAssignmentExpression(root: BinaryExpression) {
2015-11-04 23:02:33 +01:00
const target = root.left;
2015-03-13 20:34:12 +01:00
let value = root.right;
if (isEmptyObjectLiteralOrArrayLiteral(target)) {
emit(value);
}
else if (isAssignmentExpressionStatement) {
// Source map node for root.left = root.right is root
// but if root is synthetic, which could be in below case, use the target which is { a }
// for ({a} of {a: string}) {
// }
emitDestructuringAssignment(target, value, nodeIsSynthesized(root) ? target : root);
2015-03-09 21:32:02 +01:00
}
else {
if (root.parent.kind !== SyntaxKind.ParenthesizedExpression) {
write("(");
}
// Temporary assignment needed to emit root should highlight whole binary expression
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, root);
// Source map node for root.left = root.right is root
emitDestructuringAssignment(target, value, root);
write(", ");
emit(value);
if (root.parent.kind !== SyntaxKind.ParenthesizedExpression) {
write(")");
}
}
}
function emitBindingElement(target: BindingElement | VariableDeclaration, value: Expression) {
// Any temporary assignments needed to emit target = value should point to target
if (target.initializer) {
// Combine value and initializer
value = value ? createDefaultValueCheck(value, target.initializer, target) : target.initializer;
}
else if (!value) {
// Use 'void 0' in absence of value and initializer
value = createVoidZero();
}
if (isBindingPattern(target.name)) {
const pattern = <BindingPattern>target.name;
const elements = pattern.elements;
const numElements = elements.length;
if (numElements !== 1) {
// For anything other than a single-element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once. Additionally, if we have zero elements
// we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
// so in that case, we'll intentionally create that temporary.
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ numElements !== 0, target);
}
for (let i = 0; i < numElements; i++) {
2015-11-04 23:02:33 +01:00
const element = elements[i];
if (pattern.kind === SyntaxKind.ObjectBindingPattern) {
// Rewrite element to a declaration with an initializer that fetches property
2015-11-04 23:02:33 +01:00
const propName = element.propertyName || <Identifier>element.name;
2015-04-11 03:56:11 +02:00
emitBindingElement(element, createPropertyAccessForDestructuringProperty(value, propName));
}
else if (element.kind !== SyntaxKind.OmittedExpression) {
if (!element.dotDotDotToken) {
// Rewrite element to a declaration that accesses array element at index i
2015-04-11 03:56:11 +02:00
emitBindingElement(element, createElementAccessExpression(value, createNumericLiteral(i)));
}
else if (i === numElements - 1) {
emitBindingElement(element, createSliceCall(value, i));
}
}
}
}
else {
emitAssignment(<Identifier>target.name, value, /*shouldEmitCommaBeforeAssignment*/ emitCount > 0, target);
emitCount++;
}
}
}
function emitVariableDeclaration(node: VariableDeclaration) {
if (isBindingPattern(node.name)) {
const isExported = getCombinedNodeFlags(node) & NodeFlags.Export;
if (languageVersion >= ScriptTarget.ES6 && (!isExported || modulekind === ModuleKind.ES6)) {
// emit ES6 destructuring only if target module is ES6 or variable is not exported
2016-03-17 15:05:33 +01:00
// exported variables in CJS/AMD are prefixed with 'exports.' so result javascript { exports.toString } = 1; is illegal
const isTopLevelDeclarationInSystemModule =
modulekind === ModuleKind.System &&
shouldHoistVariable(node, /*checkIfSourceFileLevelDecl*/true);
if (isTopLevelDeclarationInSystemModule) {
2016-03-17 15:05:33 +01:00
// In System modules top level variables are hoisted
// so variable declarations with destructuring are turned into destructuring assignments.
// As a result, they will need parentheses to disambiguate object binding assignments from blocks.
write("(");
}
emit(node.name);
emitOptional(" = ", node.initializer);
if (isTopLevelDeclarationInSystemModule) {
write(")");
}
}
else {
emitDestructuring(node, /*isAssignmentExpressionStatement*/ false);
}
}
else {
2015-03-13 20:34:12 +01:00
let initializer = node.initializer;
if (!initializer &&
languageVersion < ScriptTarget.ES6 &&
// for names - binding patterns that lack initializer there is no point to emit explicit initializer
// since downlevel codegen for destructuring will fail in the absence of initializer so all binding elements will say uninitialized
node.name.kind === SyntaxKind.Identifier) {
const container = getEnclosingBlockScopeContainer(node);
const flags = resolver.getNodeCheckFlags(node);
// nested let bindings might need to be initialized explicitly to preserve ES6 semantic
// { let x = 1; }
// { let x; } // x here should be undefined. not 1
// NOTES:
// Top level bindings never collide with anything and thus don't require explicit initialization.
// As for nested let bindings there are two cases:
// - nested let bindings that were not renamed definitely should be initialized explicitly
// { let x = 1; }
// { let x; if (some-condition) { x = 1}; if (x) { /*1*/ } }
// Without explicit initialization code in /*1*/ can be executed even if some-condition is evaluated to false
// - renaming introduces fresh name that should not collide with any existing names, however renamed bindings sometimes also should be
// explicitly initialized. One particular case: non-captured binding declared inside loop body (but not in loop initializer)
// let x;
// for (;;) {
// let x;
// }
// in downlevel codegen inner 'x' will be renamed so it won't collide with outer 'x' however it will should be reset on every iteration
// as if it was declared anew.
// * Why non-captured binding - because if loop contains block scoped binding captured in some function then loop body will be rewritten
// to have a fresh scope on every iteration so everything will just work.
// * Why loop initializer is excluded - since we've introduced a fresh name it already will be undefined.
const isCapturedInFunction = flags & NodeCheckFlags.CapturedBlockScopedBinding;
const isDeclaredInLoop = flags & NodeCheckFlags.BlockScopedBindingInLoop;
const emittedAsTopLevel =
isBlockScopedContainerTopLevel(container) ||
(isCapturedInFunction && isDeclaredInLoop && container.kind === SyntaxKind.Block && isIterationStatement(container.parent, /*lookInLabeledStatements*/ false));
const emittedAsNestedLetDeclaration =
getCombinedNodeFlags(node) & NodeFlags.Let &&
!emittedAsTopLevel;
const emitExplicitInitializer =
emittedAsNestedLetDeclaration &&
container.kind !== SyntaxKind.ForInStatement &&
container.kind !== SyntaxKind.ForOfStatement &&
(
!resolver.isDeclarationWithCollidingName(node) ||
(isDeclaredInLoop && !isCapturedInFunction && !isIterationStatement(container, /*lookInLabeledStatements*/ false))
);
if (emitExplicitInitializer) {
initializer = createVoidZero();
}
2015-02-18 01:26:32 +01:00
}
2015-11-04 23:02:33 +01:00
const exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.name);
2015-04-13 10:32:16 +02:00
if (exportChanged) {
write(`${exportFunctionForFile}("`);
emitNodeWithCommentsAndWithoutSourcemap(node.name);
write(`", `);
}
emitModuleMemberName(node);
emitOptional(" = ", initializer);
2015-04-13 10:32:16 +02:00
if (exportChanged) {
2015-06-26 01:24:41 +02:00
write(")");
}
}
}
function emitExportVariableAssignments(node: VariableDeclaration | BindingElement) {
if (node.kind === SyntaxKind.OmittedExpression) {
return;
}
2015-11-04 23:02:33 +01:00
const name = node.name;
if (name.kind === SyntaxKind.Identifier) {
emitExportMemberAssignments(<Identifier>name);
}
else if (isBindingPattern(name)) {
forEach((<BindingPattern>name).elements, emitExportVariableAssignments);
}
}
function isES6ExportedDeclaration(node: Node) {
return !!(node.flags & NodeFlags.Export) &&
2015-09-17 22:26:04 +02:00
modulekind === ModuleKind.ES6 &&
node.parent.kind === SyntaxKind.SourceFile;
}
function emitVariableStatement(node: VariableStatement) {
let startIsEmitted = false;
if (node.flags & NodeFlags.Export) {
if (isES6ExportedDeclaration(node)) {
// Exported ES6 module member
write("export ");
startIsEmitted = tryEmitStartOfVariableDeclarationList(node.declarationList);
}
}
else {
2015-04-22 07:27:33 +02:00
startIsEmitted = tryEmitStartOfVariableDeclarationList(node.declarationList);
}
if (startIsEmitted) {
emitCommaList(node.declarationList.declarations);
write(";");
}
else {
2015-11-04 23:02:33 +01:00
const atLeastOneItem = emitVariableDeclarationListSkippingUninitializedEntries(node.declarationList);
if (atLeastOneItem) {
write(";");
}
}
if (modulekind !== ModuleKind.ES6 && node.parent === currentSourceFile) {
forEach(node.declarationList.declarations, emitExportVariableAssignments);
}
}
function shouldEmitLeadingAndTrailingCommentsForVariableStatement(node: VariableStatement) {
// If we're not exporting the variables, there's nothing special here.
// Always emit comments for these nodes.
if (!(node.flags & NodeFlags.Export)) {
return true;
}
// If we are exporting, but it's a top-level ES6 module exports,
// we'll emit the declaration list verbatim, so emit comments too.
if (isES6ExportedDeclaration(node)) {
return true;
}
// Otherwise, only emit if we have at least one initializer present.
2015-11-04 23:02:33 +01:00
for (const declaration of node.declarationList.declarations) {
if (declaration.initializer) {
return true;
}
}
return false;
}
function emitParameter(node: ParameterDeclaration) {
2015-01-16 16:15:31 +01:00
if (languageVersion < ScriptTarget.ES6) {
if (isBindingPattern(node.name)) {
2015-11-04 23:02:33 +01:00
const name = createTempVariable(TempFlags.Auto);
if (!tempParameters) {
tempParameters = [];
}
tempParameters.push(name);
emit(name);
}
else {
emit(node.name);
}
}
else {
if (node.dotDotDotToken) {
write("...");
}
emit(node.name);
emitOptional(" = ", node.initializer);
}
}
function emitDefaultValueAssignments(node: FunctionLikeDeclaration) {
2015-01-16 16:15:31 +01:00
if (languageVersion < ScriptTarget.ES6) {
2015-03-13 20:34:12 +01:00
let tempIndex = 0;
2015-06-22 23:41:35 +02:00
forEach(node.parameters, parameter => {
// A rest parameter cannot have a binding pattern or an initializer,
// so let's just ignore it.
2015-06-22 23:41:35 +02:00
if (parameter.dotDotDotToken) {
return;
}
2015-11-04 23:02:33 +01:00
const { name: paramName, initializer } = parameter;
if (isBindingPattern(paramName)) {
2015-06-24 00:05:33 +02:00
// In cases where a binding pattern is simply '[]' or '{}',
// we usually don't want to emit a var declaration; however, in the presence
// of an initializer, we must emit that expression to preserve side effects.
2015-11-04 23:02:33 +01:00
const hasBindingElements = paramName.elements.length > 0;
2015-06-24 00:05:33 +02:00
if (hasBindingElements || initializer) {
writeLine();
write("var ");
2015-06-24 00:05:33 +02:00
if (hasBindingElements) {
emitDestructuring(parameter, /*isAssignmentExpressionStatement*/ false, tempParameters[tempIndex]);
}
else {
emit(tempParameters[tempIndex]);
write(" = ");
emit(initializer);
}
write(";");
}
// Regardless of whether we will emit a var declaration for the binding pattern, we generate the temporary
// variable for the parameter (see: emitParameter)
tempIndex++;
}
2015-06-24 00:05:33 +02:00
else if (initializer) {
writeLine();
2015-06-22 23:41:35 +02:00
emitStart(parameter);
write("if (");
2015-06-24 00:05:33 +02:00
emitNodeWithoutSourceMap(paramName);
write(" === void 0)");
2015-06-22 23:41:35 +02:00
emitEnd(parameter);
write(" { ");
2015-06-22 23:41:35 +02:00
emitStart(parameter);
emitNodeWithCommentsAndWithoutSourcemap(paramName);
write(" = ");
emitNodeWithCommentsAndWithoutSourcemap(initializer);
2015-06-22 23:41:35 +02:00
emitEnd(parameter);
write("; }");
}
});
}
}
function emitRestParameter(node: FunctionLikeDeclaration) {
if (languageVersion < ScriptTarget.ES6 && hasDeclaredRestParameter(node)) {
const restParam = node.parameters[node.parameters.length - 1];
// A rest parameter cannot have a binding pattern, so let's just ignore it if it does.
if (isBindingPattern(restParam.name)) {
return;
}
const skipThisCount = node.parameters.length && (<Identifier>node.parameters[0].name).originalKeywordKind === SyntaxKind.ThisKeyword ? 1 : 0;
const restIndex = node.parameters.length - 1 - skipThisCount;
2015-11-04 23:02:33 +01:00
const tempName = createTempVariable(TempFlags._i).text;
writeLine();
emitLeadingComments(restParam);
emitStart(restParam);
write("var ");
emitNodeWithCommentsAndWithoutSourcemap(restParam.name);
write(" = [];");
emitEnd(restParam);
emitTrailingComments(restParam);
writeLine();
write("for (");
emitStart(restParam);
write("var " + tempName + " = " + restIndex + ";");
emitEnd(restParam);
write(" ");
emitStart(restParam);
write(tempName + " < arguments.length;");
emitEnd(restParam);
write(" ");
emitStart(restParam);
write(tempName + "++");
emitEnd(restParam);
write(") {");
increaseIndent();
writeLine();
emitStart(restParam);
emitNodeWithCommentsAndWithoutSourcemap(restParam.name);
write("[" + tempName + " - " + restIndex + "] = arguments[" + tempName + "];");
emitEnd(restParam);
decreaseIndent();
writeLine();
write("}");
}
}
function emitAccessor(node: AccessorDeclaration) {
write(node.kind === SyntaxKind.GetAccessor ? "get " : "set ");
emit(node.name);
emitSignatureAndBody(node);
2014-07-13 01:04:16 +02:00
}
2015-01-29 02:34:38 +01:00
function shouldEmitAsArrowFunction(node: FunctionLikeDeclaration): boolean {
2015-01-31 02:48:07 +01:00
return node.kind === SyntaxKind.ArrowFunction && languageVersion >= ScriptTarget.ES6;
2014-07-13 01:04:16 +02:00
}
function emitDeclarationName(node: Declaration) {
if (node.name) {
emitNodeWithCommentsAndWithoutSourcemap(node.name);
}
else {
2015-03-24 00:16:29 +01:00
write(getGeneratedNameForNode(node));
}
}
function shouldEmitFunctionName(node: FunctionLikeDeclaration) {
2015-03-13 01:14:33 +01:00
if (node.kind === SyntaxKind.FunctionExpression) {
// Emit name if one is present
2015-03-13 01:14:33 +01:00
return !!node.name;
}
if (node.kind === SyntaxKind.FunctionDeclaration) {
// Emit name if one is present, or emit generated name in down-level case (for export default case)
return !!node.name || modulekind !== ModuleKind.ES6;
2015-03-13 01:14:33 +01:00
}
}
function emitFunctionDeclaration(node: FunctionLikeDeclaration) {
if (nodeIsMissing(node.body)) {
return emitCommentsOnNotEmittedNode(node);
2014-07-13 01:04:16 +02:00
}
2015-08-08 01:30:16 +02:00
// TODO (yuisu) : we should not have special cases to condition emitting comments
// but have one place to fix check for these conditions.
2015-12-21 22:40:59 +01:00
const { kind, parent } = node;
if (kind !== SyntaxKind.MethodDeclaration &&
kind !== SyntaxKind.MethodSignature &&
parent &&
parent.kind !== SyntaxKind.PropertyAssignment &&
parent.kind !== SyntaxKind.CallExpression &&
parent.kind !== SyntaxKind.ArrayLiteralExpression) {
// 1. Methods will emit comments at their assignment declaration sites.
//
// 2. If the function is a property of object literal, emitting leading-comments
2015-12-21 22:40:59 +01:00
// is done by emitNodeWithoutSourceMap which then call this function.
// In particular, we would like to avoid emit comments twice in following case:
//
// var obj = {
// id:
// /*comment*/ () => void
// }
2015-12-21 22:40:59 +01:00
//
// 3. If the function is an argument in call expression, emitting of comments will be
2015-12-21 22:40:59 +01:00
// taken care of in emit list of arguments inside of 'emitCallExpression'.
//
// 4. If the function is in an array literal, 'emitLinePreservingList' will take care
// of leading comments.
emitLeadingComments(node);
}
2015-01-07 03:18:37 +01:00
emitStart(node);
2015-01-07 20:54:12 +01:00
// For targeting below es6, emit functions-like declaration including arrow function using function keyword.
// When targeting ES6, emit arrow function natively in ES6 by omitting function keyword and using fat arrow instead
2015-01-29 02:34:38 +01:00
if (!shouldEmitAsArrowFunction(node)) {
if (isES6ExportedDeclaration(node)) {
write("export ");
2015-03-13 01:14:33 +01:00
if (node.flags & NodeFlags.Default) {
write("default ");
}
}
write("function");
if (languageVersion >= ScriptTarget.ES6 && node.asteriskToken) {
write("*");
}
write(" ");
2015-01-07 03:18:37 +01:00
}
2015-03-13 01:14:33 +01:00
if (shouldEmitFunctionName(node)) {
emitDeclarationName(node);
}
2015-03-13 01:14:33 +01:00
emitSignatureAndBody(node);
2015-12-21 22:40:59 +01:00
if (modulekind !== ModuleKind.ES6 && kind === SyntaxKind.FunctionDeclaration && parent === currentSourceFile && node.name) {
emitExportMemberAssignments((<FunctionDeclaration>node).name);
}
emitEnd(node);
2016-03-29 21:48:53 +02:00
if (kind !== SyntaxKind.MethodDeclaration &&
kind !== SyntaxKind.MethodSignature &&
kind !== SyntaxKind.ArrowFunction) {
emitTrailingComments(node);
}
}
function emitCaptureThisForNodeIfNecessary(node: Node): void {
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureThis) {
writeLine();
emitStart(node);
write("var _this = this;");
emitEnd(node);
}
}
function emitSignatureParameters(node: FunctionLikeDeclaration) {
increaseIndent();
write("(");
if (node) {
2015-11-04 23:02:33 +01:00
const parameters = node.parameters;
const skipCount = node.parameters.length && (<Identifier>node.parameters[0].name).originalKeywordKind === SyntaxKind.ThisKeyword ? 1 : 0;
const omitCount = languageVersion < ScriptTarget.ES6 && hasDeclaredRestParameter(node) ? 1 : 0;
emitList(parameters, skipCount, parameters.length - omitCount - skipCount, /*multiLine*/ false, /*trailingComma*/ false);
}
write(")");
decreaseIndent();
}
2015-01-29 01:48:53 +01:00
function emitSignatureParametersForArrow(node: FunctionLikeDeclaration) {
2015-01-29 02:34:38 +01:00
// Check whether the parameter list needs parentheses and preserve no-parenthesis
2015-01-30 23:09:10 +01:00
if (node.parameters.length === 1 && node.pos === node.parameters[0].pos) {
emit(node.parameters[0]);
2015-01-30 01:38:05 +01:00
return;
2015-01-29 01:48:53 +01:00
}
2015-07-09 23:44:47 +02:00
2015-01-30 01:07:55 +01:00
emitSignatureParameters(node);
2015-01-29 01:48:53 +01:00
}
function emitAsyncFunctionBodyForES6(node: FunctionLikeDeclaration) {
2015-11-04 23:02:33 +01:00
const promiseConstructor = getEntityNameFromTypeNode(node.type);
const isArrowFunction = node.kind === SyntaxKind.ArrowFunction;
const hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0;
// An async function is emit as an outer function that calls an inner
// generator function. To preserve lexical bindings, we pass the current
// `this` and `arguments` objects to `__awaiter`. The generator function
2015-07-09 23:44:47 +02:00
// passed to `__awaiter` is executed inside of the callback to the
// promise constructor.
//
// The emit for an async arrow without a lexical `arguments` binding might be:
//
// // input
// let a = async (b) => { await b; }
//
// // output
// let a = (b) => __awaiter(this, void 0, void 0, function* () {
// yield b;
// });
//
// The emit for an async arrow with a lexical `arguments` binding might be:
//
// // input
// let a = async (b) => { await arguments[0]; }
//
// // output
// let a = (b) => __awaiter(this, arguments, void 0, function* (arguments) {
// yield arguments[0];
2015-06-18 20:31:03 +02:00
// });
//
// The emit for an async function expression without a lexical `arguments` binding
// might be:
//
// // input
// let a = async function (b) {
// await b;
// }
//
// // output
// let a = function (b) {
// return __awaiter(this, void 0, void 0, function* () {
// yield b;
// });
// }
//
// The emit for an async function expression with a lexical `arguments` binding
// might be:
//
// // input
// let a = async function (b) {
// await arguments[0];
// }
//
// // output
// let a = function (b) {
// return __awaiter(this, arguments, void 0, function* (_arguments) {
// yield _arguments[0];
2015-06-18 20:31:03 +02:00
// });
// }
//
// The emit for an async function expression with a lexical `arguments` binding
// and a return type annotation might be:
//
// // input
// let a = async function (b): MyPromise<any> {
// await arguments[0];
// }
//
// // output
// let a = function (b) {
// return __awaiter(this, arguments, MyPromise, function* (_arguments) {
// yield _arguments[0];
2015-06-18 20:31:03 +02:00
// });
// }
//
2015-07-09 23:44:47 +02:00
// If this is not an async arrow, emit the opening brace of the function body
// and the start of the return statement.
if (!isArrowFunction) {
write(" {");
2015-05-07 02:33:58 +02:00
increaseIndent();
writeLine();
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) {
writeLines(`
const _super = (function (geti, seti) {
const cache = Object.create(null);
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
})(name => super[name], (name, value) => super[name] = value);`);
writeLine();
}
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) {
write(`const _super = name => super[name];`);
writeLine();
}
write("return");
2015-01-07 03:18:37 +01:00
}
2015-07-09 23:44:47 +02:00
write(" __awaiter(this");
if (hasLexicalArguments) {
2015-12-01 21:12:31 +01:00
write(", arguments, ");
}
else {
2015-12-01 21:12:31 +01:00
write(", void 0, ");
}
if (languageVersion >= ScriptTarget.ES6 || !promiseConstructor) {
write("void 0");
}
else {
emitEntityNameAsExpression(promiseConstructor, /*useFallback*/ false);
2015-06-18 20:31:03 +02:00
}
2015-07-09 23:44:47 +02:00
// Emit the call to __awaiter.
write(", function* ()");
2015-07-09 23:44:47 +02:00
// Emit the signature and body for the inner generator function.
2015-05-07 02:33:58 +02:00
emitFunctionBody(node);
write(")");
2015-07-09 23:44:47 +02:00
// If this is not an async arrow, emit the closing brace of the outer function body.
2015-05-07 02:33:58 +02:00
if (!isArrowFunction) {
write(";");
decreaseIndent();
writeLine();
write("}");
}
2015-05-07 02:33:58 +02:00
}
2015-07-09 23:44:47 +02:00
2015-05-07 02:33:58 +02:00
function emitFunctionBody(node: FunctionLikeDeclaration) {
if (!node.body) {
2015-07-09 23:44:47 +02:00
// There can be no body when there are parse errors. Just emit an empty block
// in that case.
write(" { }");
}
2015-05-07 02:33:58 +02:00
else {
if (node.body.kind === SyntaxKind.Block) {
emitBlockFunctionBody(node, <Block>node.body);
}
else {
emitExpressionFunctionBody(node, <Expression>node.body);
}
2015-07-09 23:44:47 +02:00
}
2015-05-07 02:33:58 +02:00
}
function emitSignatureAndBody(node: FunctionLikeDeclaration) {
const saveConvertedLoopState = convertedLoopState;
2015-11-04 23:02:33 +01:00
const saveTempFlags = tempFlags;
const saveTempVariables = tempVariables;
const saveTempParameters = tempParameters;
convertedLoopState = undefined;
tempFlags = 0;
tempVariables = undefined;
tempParameters = undefined;
2015-01-07 03:18:37 +01:00
// When targeting ES6, emit arrow function natively in ES6
2015-01-29 02:34:38 +01:00
if (shouldEmitAsArrowFunction(node)) {
2015-01-29 01:48:53 +01:00
emitSignatureParametersForArrow(node);
2015-01-29 02:34:38 +01:00
write(" =>");
}
else {
emitSignatureParameters(node);
2015-01-07 03:18:37 +01:00
}
2015-11-04 23:02:33 +01:00
const isAsync = isAsyncFunctionLike(node);
if (isAsync) {
emitAsyncFunctionBodyForES6(node);
}
else {
2015-05-07 02:33:58 +02:00
emitFunctionBody(node);
2014-07-13 01:04:16 +02:00
}
if (!isES6ExportedDeclaration(node)) {
emitExportMemberAssignment(node);
}
Debug.assert(convertedLoopState === undefined);
convertedLoopState = saveConvertedLoopState;
tempFlags = saveTempFlags;
tempVariables = saveTempVariables;
tempParameters = saveTempParameters;
}
// Returns true if any preamble code was emitted.
function emitFunctionBodyPreamble(node: FunctionLikeDeclaration): void {
emitCaptureThisForNodeIfNecessary(node);
emitDefaultValueAssignments(node);
emitRestParameter(node);
}
function emitExpressionFunctionBody(node: FunctionLikeDeclaration, body: Expression) {
2015-05-07 02:33:58 +02:00
if (languageVersion < ScriptTarget.ES6 || node.flags & NodeFlags.Async) {
emitDownLevelExpressionFunctionBody(node, body);
return;
}
2015-07-09 23:44:47 +02:00
// For es6 and higher we can emit the expression as is. However, in the case
2015-03-07 10:33:18 +01:00
// where the expression might end up looking like a block when emitted, we'll
// also wrap it in parentheses first. For example if you have: a => <foo>{}
// then we need to generate: a => ({})
write(" ");
// Unwrap all type assertions.
2015-03-13 20:34:12 +01:00
let current = body;
while (current.kind === SyntaxKind.TypeAssertionExpression) {
current = (<TypeAssertion>current).expression;
}
emitParenthesizedIf(body, current.kind === SyntaxKind.ObjectLiteralExpression);
}
function emitDownLevelExpressionFunctionBody(node: FunctionLikeDeclaration, body: Expression) {
write(" {");
increaseIndent();
2015-11-04 23:02:33 +01:00
const outPos = writer.getTextPos();
emitDetachedCommentsAndUpdateCommentsInfo(node.body);
emitFunctionBodyPreamble(node);
2015-11-04 23:02:33 +01:00
const preambleEmitted = writer.getTextPos() !== outPos;
decreaseIndent();
// If we didn't have to emit any preamble code, then attempt to keep the arrow
// function on one line.
if (!preambleEmitted && nodeStartPositionsAreOnSameLine(node, body)) {
write(" ");
emitStart(body);
write("return ");
emit(body);
emitEnd(body);
write(";");
emitTempDeclarations(/*newLine*/ false);
write(" ");
}
else {
increaseIndent();
writeLine();
emitLeadingComments(node.body);
emitStart(body);
write("return ");
emit(body);
emitEnd(body);
write(";");
emitTrailingComments(node.body);
emitTempDeclarations(/*newLine*/ true);
decreaseIndent();
writeLine();
}
emitStart(node.body);
write("}");
emitEnd(node.body);
}
function emitBlockFunctionBody(node: FunctionLikeDeclaration, body: Block) {
write(" {");
2015-11-04 23:02:33 +01:00
const initialTextPos = writer.getTextPos();
increaseIndent();
emitDetachedCommentsAndUpdateCommentsInfo(body.statements);
2015-02-25 23:23:51 +01:00
// Emit all the directive prologues (like "use strict"). These have to come before
// any other preamble code we write (like parameter initializers).
2015-11-04 23:02:33 +01:00
const startIndex = emitDirectivePrologues(body.statements, /*startWithNewLine*/ true);
emitFunctionBodyPreamble(node);
decreaseIndent();
2015-11-04 23:02:33 +01:00
const preambleEmitted = writer.getTextPos() !== initialTextPos;
if (!preambleEmitted && nodeEndIsOnSameLineAsNodeStart(body, body)) {
2015-11-04 23:02:33 +01:00
for (const statement of body.statements) {
write(" ");
2015-03-13 17:08:27 +01:00
emit(statement);
}
emitTempDeclarations(/*newLine*/ false);
write(" ");
emitLeadingCommentsOfPosition(body.statements.end);
}
else {
increaseIndent();
emitLinesStartingAt(body.statements, startIndex);
emitTempDeclarations(/*newLine*/ true);
writeLine();
emitLeadingCommentsOfPosition(body.statements.end);
decreaseIndent();
}
emitToken(SyntaxKind.CloseBraceToken, body.statements.end);
}
/**
* Return the statement at a given index if it is a super-call statement
* @param ctor a constructor declaration
* @param index an index to constructor's body to check
*/
function getSuperCallAtGivenIndex(ctor: ConstructorDeclaration, index: number): ExpressionStatement {
if (!ctor.body) {
return undefined;
}
const statements = ctor.body.statements;
if (!statements || index >= statements.length) {
return undefined;
}
const statement = statements[index];
if (statement.kind === SyntaxKind.ExpressionStatement) {
return isSuperCallExpression((<ExpressionStatement>statement).expression) ? <ExpressionStatement>statement : undefined;
}
}
function emitParameterPropertyAssignments(node: ConstructorDeclaration) {
forEach(node.parameters, param => {
if (param.flags & NodeFlags.ParameterPropertyModifier) {
writeLine();
emitStart(param);
emitStart(param.name);
write("this.");
emitNodeWithoutSourceMap(param.name);
emitEnd(param.name);
write(" = ");
emit(param.name);
write(";");
emitEnd(param);
}
});
}
2014-11-18 00:53:03 +01:00
function emitMemberAccessForPropertyName(memberName: DeclarationName) {
// This does not emit source map because it is emitted by caller as caller
// is aware how the property name changes to the property access
// eg. public x = 10; becomes this.x and static x = 10 becomes className.x
if (memberName.kind === SyntaxKind.StringLiteral || memberName.kind === SyntaxKind.NumericLiteral) {
write("[");
emitNodeWithCommentsAndWithoutSourcemap(memberName);
write("]");
}
2014-11-18 00:53:03 +01:00
else if (memberName.kind === SyntaxKind.ComputedPropertyName) {
emitComputedPropertyName(<ComputedPropertyName>memberName);
}
else {
write(".");
emitNodeWithCommentsAndWithoutSourcemap(memberName);
}
}
function getInitializedProperties(node: ClassLikeDeclaration, isStatic: boolean) {
2015-11-04 23:02:33 +01:00
const properties: PropertyDeclaration[] = [];
for (const member of node.members) {
if (member.kind === SyntaxKind.PropertyDeclaration && isStatic === ((member.flags & NodeFlags.Static) !== 0) && (<PropertyDeclaration>member).initializer) {
properties.push(<PropertyDeclaration>member);
}
}
return properties;
}
function emitPropertyDeclarations(node: ClassLikeDeclaration, properties: PropertyDeclaration[]) {
2015-11-04 23:02:33 +01:00
for (const property of properties) {
emitPropertyDeclaration(node, property);
}
}
function emitPropertyDeclaration(node: ClassLikeDeclaration, property: PropertyDeclaration, receiver?: string, isExpression?: boolean) {
writeLine();
emitLeadingComments(property);
emitStart(property);
emitStart(property.name);
if (receiver) {
write(receiver);
}
else {
if (property.flags & NodeFlags.Static) {
emitDeclarationName(node);
}
else {
write("this");
}
}
emitMemberAccessForPropertyName(property.name);
emitEnd(property.name);
write(" = ");
emit(property.initializer);
if (!isExpression) {
write(";");
}
emitEnd(property);
emitTrailingComments(property);
}
2014-07-13 01:04:16 +02:00
function emitMemberFunctionsForES5AndLower(node: ClassLikeDeclaration) {
forEach(node.members, member => {
if (member.kind === SyntaxKind.SemicolonClassElement) {
writeLine();
write(";");
}
else if (member.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) {
if (!(<MethodDeclaration>member).body) {
return emitCommentsOnNotEmittedNode(member);
}
2014-07-13 01:04:16 +02:00
writeLine();
emitLeadingComments(member);
emitStart(member);
emitStart((<MethodDeclaration>member).name);
2015-03-24 03:51:38 +01:00
emitClassMemberPrefix(node, member);
2014-11-18 00:53:03 +01:00
emitMemberAccessForPropertyName((<MethodDeclaration>member).name);
emitEnd((<MethodDeclaration>member).name);
write(" = ");
emitFunctionDeclaration(<MethodDeclaration>member);
emitEnd(member);
write(";");
emitTrailingComments(member);
}
else if (member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor) {
2015-11-04 23:02:33 +01:00
const accessors = getAllAccessorDeclarations(node.members, <AccessorDeclaration>member);
if (member === accessors.firstAccessor) {
writeLine();
emitStart(member);
write("Object.defineProperty(");
emitStart((<AccessorDeclaration>member).name);
2015-03-24 03:51:38 +01:00
emitClassMemberPrefix(node, member);
write(", ");
emitExpressionForPropertyName((<AccessorDeclaration>member).name);
emitEnd((<AccessorDeclaration>member).name);
write(", {");
increaseIndent();
if (accessors.getAccessor) {
writeLine();
emitLeadingComments(accessors.getAccessor);
write("get: ");
emitStart(accessors.getAccessor);
write("function ");
emitSignatureAndBody(accessors.getAccessor);
emitEnd(accessors.getAccessor);
emitTrailingComments(accessors.getAccessor);
write(",");
}
if (accessors.setAccessor) {
writeLine();
emitLeadingComments(accessors.setAccessor);
write("set: ");
emitStart(accessors.setAccessor);
write("function ");
emitSignatureAndBody(accessors.setAccessor);
emitEnd(accessors.setAccessor);
emitTrailingComments(accessors.setAccessor);
write(",");
}
writeLine();
write("enumerable: true,");
writeLine();
write("configurable: true");
decreaseIndent();
writeLine();
write("});");
emitEnd(member);
}
}
});
}
function emitMemberFunctionsForES6AndHigher(node: ClassLikeDeclaration) {
2015-11-04 23:02:33 +01:00
for (const member of node.members) {
if ((member.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) && !(<MethodDeclaration>member).body) {
emitCommentsOnNotEmittedNode(member);
2015-03-16 23:41:51 +01:00
}
else if (member.kind === SyntaxKind.MethodDeclaration ||
2015-06-26 01:24:41 +02:00
member.kind === SyntaxKind.GetAccessor ||
member.kind === SyntaxKind.SetAccessor) {
2015-03-10 23:41:41 +01:00
writeLine();
emitLeadingComments(member);
emitStart(member);
if (member.flags & NodeFlags.Static) {
write("static ");
}
2015-03-16 23:41:51 +01:00
if (member.kind === SyntaxKind.GetAccessor) {
write("get ");
}
else if (member.kind === SyntaxKind.SetAccessor) {
write("set ");
}
if ((<MethodDeclaration>member).asteriskToken) {
write("*");
}
2015-03-16 23:20:40 +01:00
emit((<MethodDeclaration>member).name);
2015-03-10 23:41:41 +01:00
emitSignatureAndBody(<MethodDeclaration>member);
emitEnd(member);
emitTrailingComments(member);
}
else if (member.kind === SyntaxKind.SemicolonClassElement) {
writeLine();
write(";");
}
}
2015-03-10 23:41:41 +01:00
}
function emitConstructor(node: ClassLikeDeclaration, baseTypeElement: ExpressionWithTypeArguments) {
const saveConvertedLoopState = convertedLoopState;
2015-11-04 23:02:33 +01:00
const saveTempFlags = tempFlags;
const saveTempVariables = tempVariables;
const saveTempParameters = tempParameters;
convertedLoopState = undefined;
tempFlags = 0;
tempVariables = undefined;
tempParameters = undefined;
emitConstructorWorker(node, baseTypeElement);
Debug.assert(convertedLoopState === undefined);
convertedLoopState = saveConvertedLoopState;
tempFlags = saveTempFlags;
tempVariables = saveTempVariables;
tempParameters = saveTempParameters;
}
function emitConstructorWorker(node: ClassLikeDeclaration, baseTypeElement: ExpressionWithTypeArguments) {
// Check if we have property assignment inside class declaration.
// If there is property assignment, we need to emit constructor whether users define it or not
// If there is no property assignment, we can omit constructor if users do not define it
2015-03-16 23:20:40 +01:00
let hasInstancePropertyWithInitializer = false;
// Emit the constructor overload pinned comments
forEach(node.members, member => {
if (member.kind === SyntaxKind.Constructor && !(<ConstructorDeclaration>member).body) {
emitCommentsOnNotEmittedNode(member);
}
2015-03-16 23:20:40 +01:00
// Check if there is any non-static property assignment
if (member.kind === SyntaxKind.PropertyDeclaration && (<PropertyDeclaration>member).initializer && (member.flags & NodeFlags.Static) === 0) {
hasInstancePropertyWithInitializer = true;
}
});
2015-11-04 23:02:33 +01:00
const ctor = getFirstConstructorWithBody(node);
// For target ES6 and above, if there is no user-defined constructor and there is no property assignment
// do not emit constructor in class declaration.
2015-03-16 23:20:40 +01:00
if (languageVersion >= ScriptTarget.ES6 && !ctor && !hasInstancePropertyWithInitializer) {
return;
}
if (ctor) {
emitLeadingComments(ctor);
}
2015-03-16 23:20:40 +01:00
emitStart(ctor || node);
if (languageVersion < ScriptTarget.ES6) {
write("function ");
emitDeclarationName(node);
emitSignatureParameters(ctor);
}
else {
write("constructor");
if (ctor) {
emitSignatureParameters(ctor);
}
else {
// The ES2015 spec specifies in 14.5.14. Runtime Semantics: ClassDefinitionEvaluation:
// If constructor is empty, then
// If ClassHeritag_eopt is present and protoParent is not null, then
// Let constructor be the result of parsing the source text
// constructor(...args) { super (...args);}
// using the syntactic grammar with the goal symbol MethodDefinition[~Yield].
// Else,
// Let constructor be the result of parsing the source text
// constructor( ){ }
// using the syntactic grammar with the goal symbol MethodDefinition[~Yield].
//
// While we could emit the '...args' rest parameter, certain later tools in the pipeline might
// downlevel the '...args' portion less efficiently by naively copying the contents of 'arguments' to an array.
// Instead, we'll avoid using a rest parameter and spread into the super call as
// 'super(...arguments)' instead of 'super(...args)', as you can see below.
Emit more efficient/concise "empty" ES6 ctor When there are property assignments in a the class body of an inheriting class, tsc current emit the following compilation: ```ts class Foo extends Bar { public foo = 1; } ``` ```js class Foo extends Bar { constructor(…args) { super(…args); this.foo = 1; } } ``` This introduces an unneeded local variable and might force a reification of the `arguments` object (or otherwise reify the arguments into an array). This is particularly bad when that output is fed into another transpiler like Babel. In Babel, you get something like this today: ```js var Foo = (function (_Bar) { _inherits(Foo, _Bar); function Foo() { _classCallCheck(this, Foo); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _Bar.call.apply(_Bar, [this].concat(args)); this.foo = 1; } return Foo; })(Bar); ``` This causes a lot of needless work/allocations and some very strange code (`.call.apply` o_0). Admittedly, this is not strictly tsc’s problem; it could have done a deeper analysis of the code and optimized out the extra dance. However, tsc could also have emitted this simpler, more concise and semantically equivalent code in the first place: ```js class Foo extends Bar { constructor() { super(…arguments); this.foo = 1; } } ``` Which compiles into the following in Babel: ```js var Foo = (function (_Bar) { _inherits(Foo, _Bar); function Foo() { _classCallCheck(this, Foo); _Bar.apply(this, arguments); this.foo = 1; } return Foo; })(Bar); ``` Which is well-optimized (today) in most engines and much less confusing to read. As far as I can tell, the proposed compilation has exactly the same semantics as before. Fixes #10175
2016-08-06 19:36:17 +02:00
write("()");
}
}
2015-07-16 01:43:13 +02:00
let startIndex = 0;
write(" {");
increaseIndent();
if (ctor) {
2015-07-14 16:21:47 +02:00
// Emit all the directive prologues (like "use strict"). These have to come before
// any other preamble code we write (like parameter initializers).
2015-07-27 13:52:57 +02:00
startIndex = emitDirectivePrologues(ctor.body.statements, /*startWithNewLine*/ true);
emitDetachedCommentsAndUpdateCommentsInfo(ctor.body.statements);
}
emitCaptureThisForNodeIfNecessary(node);
2015-07-24 00:18:48 +02:00
let superCall: ExpressionStatement;
if (ctor) {
emitDefaultValueAssignments(ctor);
emitRestParameter(ctor);
if (baseTypeElement) {
superCall = getSuperCallAtGivenIndex(ctor, startIndex);
if (superCall) {
writeLine();
emit(superCall);
}
}
emitParameterPropertyAssignments(ctor);
}
else {
if (baseTypeElement) {
writeLine();
emitStart(baseTypeElement);
if (languageVersion < ScriptTarget.ES6) {
write("_super.apply(this, arguments);");
}
else {
// See comment above on using '...arguments' instead of '...args'.
Emit more efficient/concise "empty" ES6 ctor When there are property assignments in a the class body of an inheriting class, tsc current emit the following compilation: ```ts class Foo extends Bar { public foo = 1; } ``` ```js class Foo extends Bar { constructor(…args) { super(…args); this.foo = 1; } } ``` This introduces an unneeded local variable and might force a reification of the `arguments` object (or otherwise reify the arguments into an array). This is particularly bad when that output is fed into another transpiler like Babel. In Babel, you get something like this today: ```js var Foo = (function (_Bar) { _inherits(Foo, _Bar); function Foo() { _classCallCheck(this, Foo); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _Bar.call.apply(_Bar, [this].concat(args)); this.foo = 1; } return Foo; })(Bar); ``` This causes a lot of needless work/allocations and some very strange code (`.call.apply` o_0). Admittedly, this is not strictly tsc’s problem; it could have done a deeper analysis of the code and optimized out the extra dance. However, tsc could also have emitted this simpler, more concise and semantically equivalent code in the first place: ```js class Foo extends Bar { constructor() { super(…arguments); this.foo = 1; } } ``` Which compiles into the following in Babel: ```js var Foo = (function (_Bar) { _inherits(Foo, _Bar); function Foo() { _classCallCheck(this, Foo); _Bar.apply(this, arguments); this.foo = 1; } return Foo; })(Bar); ``` Which is well-optimized (today) in most engines and much less confusing to read. As far as I can tell, the proposed compilation has exactly the same semantics as before. Fixes #10175
2016-08-06 19:36:17 +02:00
write("super(...arguments);");
}
emitEnd(baseTypeElement);
}
}
2015-11-14 02:43:53 +01:00
emitPropertyDeclarations(node, getInitializedProperties(node, /*isStatic*/ false));
if (ctor) {
2015-06-26 02:36:19 +02:00
let statements: Node[] = (<Block>ctor.body).statements;
2015-03-16 23:20:40 +01:00
if (superCall) {
statements = statements.slice(1);
}
2015-07-14 14:28:05 +02:00
emitLinesStartingAt(statements, startIndex);
}
emitTempDeclarations(/*newLine*/ true);
writeLine();
if (ctor) {
emitLeadingCommentsOfPosition((<Block>ctor.body).statements.end);
}
decreaseIndent();
emitToken(SyntaxKind.CloseBraceToken, ctor ? (<Block>ctor.body).statements.end : node.members.end);
emitEnd(<Node>ctor || node);
if (ctor) {
emitTrailingComments(ctor);
}
}
function emitClassExpression(node: ClassExpression) {
return emitClassLikeDeclaration(node);
}
function emitClassDeclaration(node: ClassDeclaration) {
return emitClassLikeDeclaration(node);
}
function emitClassLikeDeclaration(node: ClassLikeDeclaration) {
if (languageVersion < ScriptTarget.ES6) {
emitClassLikeDeclarationBelowES6(node);
}
else {
emitClassLikeDeclarationForES6AndHigher(node);
}
if (modulekind !== ModuleKind.ES6 && node.parent === currentSourceFile && node.name) {
emitExportMemberAssignments(node.name);
}
}
2015-06-18 23:01:49 +02:00
function emitClassLikeDeclarationForES6AndHigher(node: ClassLikeDeclaration) {
let decoratedClassAlias: string;
const isHoistedDeclarationInSystemModule = shouldHoistDeclarationInSystemJsModule(node);
const isDecorated = nodeIsDecorated(node);
const rewriteAsClassExpression = isDecorated || isHoistedDeclarationInSystemModule;
if (node.kind === SyntaxKind.ClassDeclaration) {
if (rewriteAsClassExpression) {
// When we emit an ES6 class that has a class decorator, we must tailor the
// emit to certain specific cases.
//
// In the simplest case, we emit the class declaration as a let declaration, and
// evaluate decorators after the close of the class body:
//
// TypeScript | Javascript
// --------------------------------|------------------------------------
// @dec | let C = class C {
// class C { | }
// } | C = __decorate([dec], C);
// --------------------------------|------------------------------------
// @dec | export let C = class C {
// export class C { | }
// } | C = __decorate([dec], C);
// ---------------------------------------------------------------------
// [Example 1]
//
// If a class declaration contains a reference to itself *inside* of the class body,
// this introduces two bindings to the class: One outside of the class body, and one
// inside of the class body. If we apply decorators as in [Example 1] above, there
// is the possibility that the decorator `dec` will return a new value for the
// constructor, which would result in the binding inside of the class no longer
// pointing to the same reference as the binding outside of the class.
//
// As a result, we must instead rewrite all references to the class *inside* of the
// class body to instead point to a local temporary alias for the class:
//
// TypeScript | Javascript
// --------------------------------|------------------------------------
// @dec | let C_1 = class C {
// class C { | static x() { return C_1.y; }
// static x() { return C.y; } | }
// static y = 1; | let C = C_1;
// } | C.y = 1;
// | C = C_1 = __decorate([dec], C);
// --------------------------------|------------------------------------
// @dec | let C_1 = class C {
// export class C { | static x() { return C_1.y; }
// static x() { return C.y; } | }
// static y = 1; | export let C = C_1;
// } | C.y = 1;
// | C = C_1 = __decorate([dec], C);
// ---------------------------------------------------------------------
// [Example 2]
//
// If a class declaration is the default export of a module, we instead emit
// the export after the decorated declaration:
//
// TypeScript | Javascript
// --------------------------------|------------------------------------
// @dec | let default_1 = class {
// export default class { | }
// } | default_1 = __decorate([dec], default_1);
// | export default default_1;
// --------------------------------|------------------------------------
// @dec | let C = class C {
// export default class { | }
// } | C = __decorate([dec], C);
// | export default C;
// ---------------------------------------------------------------------
// [Example 3]
//
// If the class declaration is the default export and a reference to itself
// inside of the class body, we must emit both an alias for the class *and*
// move the export after the declaration:
//
// TypeScript | Javascript
// --------------------------------|------------------------------------
// @dec | let C_1 = class C {
// export default class C { | static x() { return C_1.y; }
// static x() { return C.y; } | };
// static y = 1; | let C = C_1;
// } | C.y = 1;
// | C = C_1 = __decorate([dec], C);
// | export default C;
// ---------------------------------------------------------------------
// [Example 4]
//
// NOTE: we reuse the same rewriting logic for cases when targeting ES6 and module kind is System.
// Because of hoisting top level class declaration need to be emitted as class expressions.
// Double bind case is only required if node is decorated.
if (isDecorated && resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithBodyScopedClassBinding) {
decoratedClassAlias = unescapeIdentifier(makeUniqueName(node.name ? node.name.text : "default"));
decoratedClassAliases[getNodeId(node)] = decoratedClassAlias;
}
if (isES6ExportedDeclaration(node) && !(node.flags & NodeFlags.Default) && decoratedClassAlias === undefined) {
write("export ");
}
if (decoratedClassAlias !== undefined) {
write(`let ${decoratedClassAlias}`);
}
else {
if (!isHoistedDeclarationInSystemModule) {
write("let ");
}
emitDeclarationName(node);
}
write(" = ");
}
else if (isES6ExportedDeclaration(node)) {
write("export ");
if (node.flags & NodeFlags.Default) {
write("default ");
}
}
}
2015-03-16 23:20:40 +01:00
// If the class has static properties, and it's a class expression, then we'll need
2015-07-09 23:44:47 +02:00
// to specialize the emit a bit. for a class expression of the form:
//
// (class C { static a = 1; static b = 2; ... })
//
// We'll emit:
//
// ((C_1 = class C {
// // Normal class body
// },
// C_1.a = 1,
// C_1.b = 2,
// C_1));
// var C_1;
//
// This keeps the expression as an expression, while ensuring that the static parts
// of it have been initialized by the time it is used.
2015-11-14 02:43:53 +01:00
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
2015-11-04 23:02:33 +01:00
const isClassExpressionWithStaticProperties = staticProperties.length > 0 && node.kind === SyntaxKind.ClassExpression;
let generatedName: string;
if (isClassExpressionWithStaticProperties) {
generatedName = node.name ? getGeneratedNameForNode(node.name) : makeUniqueName("classExpression");
const synthesizedNode = <Identifier>createSynthesizedNode(SyntaxKind.Identifier);
synthesizedNode.text = generatedName;
recordTempDeclaration(synthesizedNode);
write("(");
increaseIndent();
emit(synthesizedNode);
2015-06-26 01:24:41 +02:00
write(" = ");
}
write("class");
// emit name if
// - node has a name
// - this is default export with static initializers
if (node.name || (node.flags & NodeFlags.Default && (staticProperties.length > 0 || modulekind !== ModuleKind.ES6) && !rewriteAsClassExpression)) {
write(" ");
emitDeclarationName(node);
}
2015-11-04 23:02:33 +01:00
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
if (baseTypeNode) {
write(" extends ");
emit(baseTypeNode.expression);
}
write(" {");
increaseIndent();
writeLine();
2015-03-15 00:53:33 +01:00
emitConstructor(node, baseTypeNode);
emitMemberFunctionsForES6AndHigher(node);
decreaseIndent();
writeLine();
emitToken(SyntaxKind.CloseBraceToken, node.members.end);
if (rewriteAsClassExpression) {
if (decoratedClassAlias !== undefined) {
write(";");
writeLine();
if (isES6ExportedDeclaration(node) && !(node.flags & NodeFlags.Default)) {
write("export ");
}
write("let ");
emitDeclarationName(node);
write(` = ${decoratedClassAlias}`);
}
decoratedClassAliases[getNodeId(node)] = undefined;
2015-03-25 01:00:29 +01:00
write(";");
}
2015-03-15 00:53:33 +01:00
// Emit static property assignment. Because classDeclaration is lexically evaluated,
// it is safe to emit static property assignment after classDeclaration
// From ES6 specification:
// HasLexicalDeclaration (N) : Determines if the argument identifier has a binding in this environment record that was created using
// a lexical declaration such as a LexicalDeclaration or a ClassDeclaration.
if (isClassExpressionWithStaticProperties) {
2016-01-16 23:05:46 +01:00
for (const property of staticProperties) {
write(",");
writeLine();
emitPropertyDeclaration(node, property, /*receiver*/ generatedName, /*isExpression*/ true);
}
write(",");
writeLine();
write(generatedName);
decreaseIndent();
write(")");
}
else {
writeLine();
emitPropertyDeclarations(node, staticProperties);
emitDecoratorsOfClass(node, decoratedClassAlias);
}
if (!(node.flags & NodeFlags.Export)) {
return;
}
if (modulekind !== ModuleKind.ES6) {
emitExportMemberAssignment(node as ClassDeclaration);
}
else {
// If this is an exported class, but not on the top level (i.e. on an internal
// module), export it
if (node.flags & NodeFlags.Default) {
// if this is a top level default export of decorated class, write the export after the declaration.
if (isDecorated) {
writeLine();
write("export default ");
emitDeclarationName(node);
write(";");
}
}
else if (node.parent.kind !== SyntaxKind.SourceFile) {
writeLine();
emitStart(node);
emitModuleMemberName(node);
write(" = ");
emitDeclarationName(node);
emitEnd(node);
write(";");
}
2015-03-25 01:00:29 +01:00
}
}
function emitClassLikeDeclarationBelowES6(node: ClassLikeDeclaration) {
2016-06-09 01:43:56 +02:00
const isES6ExportedClass = isES6ExportedDeclaration(node);
if (node.kind === SyntaxKind.ClassDeclaration) {
2016-06-09 01:43:56 +02:00
if (isES6ExportedClass && !(node.flags & NodeFlags.Default)) {
write("export ");
}
2015-04-22 07:27:33 +02:00
// source file level classes in system modules are hoisted so 'var's for them are already defined
2015-04-24 06:14:03 +02:00
if (!shouldHoistDeclarationInSystemJsModule(node)) {
write("var ");
}
emitDeclarationName(node);
write(" = ");
}
write("(function (");
2015-11-04 23:02:33 +01:00
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
if (baseTypeNode) {
write("_super");
}
write(") {");
2015-11-04 23:02:33 +01:00
const saveTempFlags = tempFlags;
const saveTempVariables = tempVariables;
const saveTempParameters = tempParameters;
const saveComputedPropertyNamesToGeneratedNames = computedPropertyNamesToGeneratedNames;
const saveConvertedLoopState = convertedLoopState;
convertedLoopState = undefined;
tempFlags = 0;
tempVariables = undefined;
tempParameters = undefined;
computedPropertyNamesToGeneratedNames = undefined;
increaseIndent();
if (baseTypeNode) {
writeLine();
emitStart(baseTypeNode);
write("__extends(");
emitDeclarationName(node);
write(", _super);");
emitEnd(baseTypeNode);
}
writeLine();
2015-03-15 00:53:33 +01:00
emitConstructor(node, baseTypeNode);
emitMemberFunctionsForES5AndLower(node);
2015-11-14 02:43:53 +01:00
emitPropertyDeclarations(node, getInitializedProperties(node, /*isStatic*/ true));
writeLine();
emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined);
writeLine();
emitToken(SyntaxKind.CloseBraceToken, node.members.end, () => {
write("return ");
emitDeclarationName(node);
});
write(";");
2015-03-18 02:15:02 +01:00
emitTempDeclarations(/*newLine*/ true);
Debug.assert(convertedLoopState === undefined);
convertedLoopState = saveConvertedLoopState;
tempFlags = saveTempFlags;
2015-03-18 02:15:02 +01:00
tempVariables = saveTempVariables;
tempParameters = saveTempParameters;
computedPropertyNamesToGeneratedNames = saveComputedPropertyNamesToGeneratedNames;
decreaseIndent();
writeLine();
emitToken(SyntaxKind.CloseBraceToken, node.members.end);
emitStart(node);
2015-12-09 02:47:12 +01:00
write("(");
if (baseTypeNode) {
emit(baseTypeNode.expression);
}
2015-12-09 02:47:12 +01:00
write("))");
if (node.kind === SyntaxKind.ClassDeclaration) {
write(";");
}
emitEnd(node);
2016-06-09 01:43:56 +02:00
if (node.kind === SyntaxKind.ClassDeclaration && !isES6ExportedClass) {
emitExportMemberAssignment(<ClassDeclaration>node);
}
2016-06-09 01:43:56 +02:00
else if (isES6ExportedClass && (node.flags & NodeFlags.Default)) {
writeLine();
write("export default ");
emitDeclarationName(node);
write(";");
}
}
function emitClassMemberPrefix(node: ClassLikeDeclaration, member: Node) {
emitDeclarationName(node);
if (!(member.flags & NodeFlags.Static)) {
write(".prototype");
}
}
2015-06-18 23:01:49 +02:00
function emitDecoratorsOfClass(node: ClassLikeDeclaration, decoratedClassAlias: string) {
2015-03-24 22:16:52 +01:00
emitDecoratorsOfMembers(node, /*staticFlag*/ 0);
emitDecoratorsOfMembers(node, NodeFlags.Static);
emitDecoratorsOfConstructor(node, decoratedClassAlias);
}
function emitDecoratorsOfConstructor(node: ClassLikeDeclaration, decoratedClassAlias: string) {
2015-11-04 23:02:33 +01:00
const decorators = node.decorators;
const constructor = getFirstConstructorWithBody(node);
2015-11-30 23:03:28 +01:00
const firstParameterDecorator = constructor && forEach(constructor.parameters, parameter => parameter.decorators);
// skip decoration of the constructor if neither it nor its parameters are decorated
2015-11-30 23:03:28 +01:00
if (!decorators && !firstParameterDecorator) {
return;
}
2015-03-24 22:16:52 +01:00
// Emit the call to __decorate. Given the class:
//
// @dec
// class C {
// }
//
// The emit for the class is:
//
2015-03-25 01:00:29 +01:00
// C = __decorate([dec], C);
2015-03-24 22:16:52 +01:00
//
2015-03-25 01:00:29 +01:00
writeLine();
2015-11-30 23:03:28 +01:00
emitStart(node.decorators || firstParameterDecorator);
2015-03-24 22:16:52 +01:00
emitDeclarationName(node);
if (decoratedClassAlias !== undefined) {
write(` = ${decoratedClassAlias}`);
}
write(" = __decorate([");
increaseIndent();
writeLine();
2015-11-04 23:02:33 +01:00
const decoratorCount = decorators ? decorators.length : 0;
let argumentsWritten = emitList(decorators, 0, decoratorCount, /*multiLine*/ true, /*trailingComma*/ false, /*leadingComma*/ false, /*noTrailingNewLine*/ true,
decorator => emit(decorator.expression));
2015-11-30 23:03:28 +01:00
if (firstParameterDecorator) {
argumentsWritten += emitDecoratorsOfParameters(constructor, /*leadingComma*/ argumentsWritten > 0);
}
2015-04-03 00:22:53 +02:00
emitSerializedTypeMetadata(node, /*leadingComma*/ argumentsWritten >= 0);
decreaseIndent();
writeLine();
write("], ");
2015-03-24 22:16:52 +01:00
emitDeclarationName(node);
write(")");
2015-11-30 23:03:28 +01:00
emitEnd(node.decorators || firstParameterDecorator);
write(";");
writeLine();
}
function emitDecoratorsOfMembers(node: ClassLikeDeclaration, staticFlag: NodeFlags) {
2015-11-04 23:02:33 +01:00
for (const member of node.members) {
// only emit members in the correct group
2015-03-24 22:16:52 +01:00
if ((member.flags & NodeFlags.Static) !== staticFlag) {
continue;
2015-03-24 22:16:52 +01:00
}
// skip members that cannot be decorated (such as the constructor)
if (!nodeCanBeDecorated(member)) {
continue;
}
// skip an accessor declaration if it is not the first accessor
let decorators: NodeArray<Decorator>;
let functionLikeMember: FunctionLikeDeclaration;
if (isAccessor(member)) {
2015-11-04 23:02:33 +01:00
const accessors = getAllAccessorDeclarations(node.members, <AccessorDeclaration>member);
if (member !== accessors.firstAccessor) {
continue;
}
// get the decorators from the first accessor with decorators
decorators = accessors.firstAccessor.decorators;
if (!decorators && accessors.secondAccessor) {
decorators = accessors.secondAccessor.decorators;
}
2015-03-24 22:16:52 +01:00
// we only decorate parameters of the set accessor
functionLikeMember = accessors.setAccessor;
2015-03-24 22:16:52 +01:00
}
else {
decorators = member.decorators;
2015-03-24 22:16:52 +01:00
// we only decorate the parameters here if this is a method
if (member.kind === SyntaxKind.MethodDeclaration) {
functionLikeMember = <MethodDeclaration>member;
}
2015-03-24 22:16:52 +01:00
}
2015-11-30 23:03:28 +01:00
const firstParameterDecorator = functionLikeMember && forEach(functionLikeMember.parameters, parameter => parameter.decorators);
// skip a member if it or any of its parameters are not decorated
2015-11-30 23:03:28 +01:00
if (!decorators && !firstParameterDecorator) {
continue;
}
2015-03-24 22:16:52 +01:00
// Emit the call to __decorate. Given the following:
//
// class C {
// @dec method(@dec2 x) {}
2015-03-24 22:16:52 +01:00
// @dec get accessor() {}
// @dec prop;
// }
//
// The emit for a method is:
//
2015-09-11 01:27:35 +02:00
// __decorate([
// dec,
// __param(0, dec2),
// __metadata("design:type", Function),
// __metadata("design:paramtypes", [Object]),
// __metadata("design:returntype", void 0)
// ], C.prototype, "method", undefined);
2015-07-09 23:44:47 +02:00
//
2015-03-24 22:16:52 +01:00
// The emit for an accessor is:
//
2015-09-11 01:27:35 +02:00
// __decorate([
// dec
// ], C.prototype, "accessor", undefined);
2015-03-24 22:16:52 +01:00
//
// The emit for a property is:
//
// __decorate([
// dec
// ], C.prototype, "prop");
2015-03-24 22:16:52 +01:00
//
2015-03-25 01:00:29 +01:00
writeLine();
2015-11-30 23:03:28 +01:00
emitStart(decorators || firstParameterDecorator);
write("__decorate([");
increaseIndent();
writeLine();
2015-11-04 23:02:33 +01:00
const decoratorCount = decorators ? decorators.length : 0;
let argumentsWritten = emitList(decorators, 0, decoratorCount, /*multiLine*/ true, /*trailingComma*/ false, /*leadingComma*/ false, /*noTrailingNewLine*/ true,
decorator => emit(decorator.expression));
2015-04-03 00:22:53 +02:00
2015-11-30 23:03:28 +01:00
if (firstParameterDecorator) {
argumentsWritten += emitDecoratorsOfParameters(functionLikeMember, argumentsWritten > 0);
}
2015-04-03 00:22:53 +02:00
emitSerializedTypeMetadata(member, argumentsWritten > 0);
decreaseIndent();
writeLine();
write("], ");
2015-03-24 22:16:52 +01:00
emitClassMemberPrefix(node, member);
write(", ");
emitExpressionForPropertyName(member.name);
2015-09-11 01:27:35 +02:00
if (languageVersion > ScriptTarget.ES3) {
if (member.kind !== SyntaxKind.PropertyDeclaration) {
// We emit `null` here to indicate to `__decorate` that it can invoke `Object.getOwnPropertyDescriptor` directly.
// We have this extra argument here so that we can inject an explicit property descriptor at a later date.
write(", null");
}
else {
// We emit `void 0` here to indicate to `__decorate` that it can invoke `Object.defineProperty` directly, but that it
// should not invoke `Object.getOwnPropertyDescriptor`.
write(", void 0");
}
2015-03-24 22:16:52 +01:00
}
write(")");
2015-11-30 23:03:28 +01:00
emitEnd(decorators || firstParameterDecorator);
write(";");
2015-03-24 22:16:52 +01:00
writeLine();
}
}
2015-04-03 00:22:53 +02:00
function emitDecoratorsOfParameters(node: FunctionLikeDeclaration, leadingComma: boolean): number {
let argumentsWritten = 0;
if (node) {
let parameterIndex = 0;
2015-11-04 23:02:33 +01:00
for (const parameter of node.parameters) {
if (nodeIsDecorated(parameter)) {
2015-11-04 23:02:33 +01:00
const decorators = parameter.decorators;
2015-04-03 00:22:53 +02:00
argumentsWritten += emitList(decorators, 0, decorators.length, /*multiLine*/ true, /*trailingComma*/ false, /*leadingComma*/ leadingComma, /*noTrailingNewLine*/ true, decorator => {
write(`__param(${parameterIndex}, `);
emit(decorator.expression);
write(")");
});
leadingComma = true;
}
2015-12-23 00:45:00 +01:00
parameterIndex++;
2015-03-24 22:16:52 +01:00
}
}
2015-04-03 00:22:53 +02:00
return argumentsWritten;
}
function shouldEmitTypeMetadata(node: Declaration): boolean {
2015-04-03 00:22:53 +02:00
// This method determines whether to emit the "design:type" metadata based on the node's kind.
2015-07-09 23:44:47 +02:00
// The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata
2015-04-03 00:22:53 +02:00
// compiler option is set.
2015-03-18 01:09:39 +01:00
switch (node.kind) {
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.PropertyDeclaration:
return true;
}
2015-03-18 01:09:39 +01:00
return false;
}
function shouldEmitReturnTypeMetadata(node: Declaration): boolean {
2015-04-03 00:22:53 +02:00
// This method determines whether to emit the "design:returntype" metadata based on the node's kind.
2015-07-09 23:44:47 +02:00
// The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata
2015-04-03 00:22:53 +02:00
// compiler option is set.
2015-03-18 01:09:39 +01:00
switch (node.kind) {
case SyntaxKind.MethodDeclaration:
return true;
}
return false;
}
function shouldEmitParamTypesMetadata(node: Declaration): boolean {
2015-04-03 00:22:53 +02:00
// This method determines whether to emit the "design:paramtypes" metadata based on the node's kind.
2015-07-09 23:44:47 +02:00
// The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata
2015-04-03 00:22:53 +02:00
// compiler option is set.
2015-03-18 01:09:39 +01:00
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.SetAccessor:
return true;
}
return false;
}
2015-07-27 13:52:57 +02:00
/** Serializes the type of a declaration to an appropriate JS constructor value. Used by the __metadata decorator for a class member. */
function emitSerializedTypeOfNode(node: Node) {
// serialization of the type of a declaration uses the following rules:
//
// * The serialized type of a ClassDeclaration is "Function"
// * The serialized type of a ParameterDeclaration is the serialized type of its type annotation.
// * The serialized type of a PropertyDeclaration is the serialized type of its type annotation.
// * The serialized type of an AccessorDeclaration is the serialized type of the return type annotation of its getter or parameter type annotation of its setter.
// * The serialized type of any other FunctionLikeDeclaration is "Function".
// * The serialized type of any other node is "void 0".
2015-07-27 13:52:57 +02:00
//
// For rules on serializing type annotations, see `serializeTypeNode`.
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
write("Function");
return;
2015-07-27 13:52:57 +02:00
case SyntaxKind.PropertyDeclaration:
emitSerializedTypeNode((<PropertyDeclaration>node).type);
return;
2015-07-27 13:52:57 +02:00
case SyntaxKind.Parameter:
emitSerializedTypeNode((<ParameterDeclaration>node).type);
return;
2015-07-27 13:52:57 +02:00
case SyntaxKind.GetAccessor:
emitSerializedTypeNode((<AccessorDeclaration>node).type);
return;
2015-07-27 13:52:57 +02:00
case SyntaxKind.SetAccessor:
emitSerializedTypeNode(getSetAccessorTypeAnnotationNode(<AccessorDeclaration>node));
return;
2015-07-27 13:52:57 +02:00
}
2015-07-27 13:52:57 +02:00
if (isFunctionLike(node)) {
write("Function");
return;
}
2015-07-27 13:52:57 +02:00
write("void 0");
}
2015-07-27 13:52:57 +02:00
function emitSerializedTypeNode(node: TypeNode) {
if (node) {
switch (node.kind) {
case SyntaxKind.VoidKeyword:
write("void 0");
return;
2015-07-27 13:52:57 +02:00
case SyntaxKind.ParenthesizedType:
emitSerializedTypeNode((<ParenthesizedTypeNode>node).type);
return;
2015-07-27 13:52:57 +02:00
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
write("Function");
return;
2015-07-27 13:52:57 +02:00
case SyntaxKind.ArrayType:
case SyntaxKind.TupleType:
write("Array");
return;
2015-03-18 01:09:39 +01:00
case SyntaxKind.TypePredicate:
case SyntaxKind.BooleanKeyword:
write("Boolean");
return;
2015-07-27 13:52:57 +02:00
case SyntaxKind.StringKeyword:
2016-06-18 16:21:23 +02:00
case SyntaxKind.LiteralType:
write("String");
return;
2015-07-27 13:52:57 +02:00
case SyntaxKind.NumberKeyword:
write("Number");
return;
case SyntaxKind.SymbolKeyword:
write("Symbol");
return;
2015-07-27 13:52:57 +02:00
case SyntaxKind.TypeReference:
emitSerializedTypeReferenceNode(<TypeReferenceNode>node);
return;
2015-07-27 13:52:57 +02:00
case SyntaxKind.TypeQuery:
case SyntaxKind.TypeLiteral:
case SyntaxKind.UnionType:
case SyntaxKind.IntersectionType:
case SyntaxKind.AnyKeyword:
case SyntaxKind.ThisType:
break;
2015-07-27 13:52:57 +02:00
default:
Debug.fail("Cannot serialize unexpected type node.");
break;
}
}
write("Object");
}
2015-07-27 13:52:57 +02:00
/** Serializes a TypeReferenceNode to an appropriate JS constructor value. Used by the __metadata decorator. */
function emitSerializedTypeReferenceNode(node: TypeReferenceNode) {
let location: Node = node.parent;
while (isDeclaration(location) || isTypeNode(location)) {
location = location.parent;
}
// Clone the type name and parent it to a location outside of the current declaration.
const typeName = cloneEntityName(node.typeName, location);
2015-11-04 23:02:33 +01:00
const result = resolver.getTypeReferenceSerializationKind(typeName);
switch (result) {
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind.Unknown:
let temp = createAndRecordTempVariable(TempFlags.Auto);
write("(typeof (");
emitNodeWithoutSourceMap(temp);
2015-07-24 00:18:48 +02:00
write(" = ");
emitEntityNameAsExpression(typeName, /*useFallback*/ true);
write(") === 'function' && ");
emitNodeWithoutSourceMap(temp);
write(") || Object");
break;
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue:
emitEntityNameAsExpression(typeName, /*useFallback*/ false);
break;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind.VoidType:
write("void 0");
break;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind.BooleanType:
write("Boolean");
break;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind.NumberLikeType:
write("Number");
break;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind.StringLikeType:
write("String");
break;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind.ArrayLikeType:
write("Array");
break;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind.ESSymbolType:
if (languageVersion < ScriptTarget.ES6) {
write("typeof Symbol === 'function' ? Symbol : Object");
}
else {
write("Symbol");
}
break;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind.TypeWithCallSignature:
write("Function");
break;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind.ObjectType:
write("Object");
break;
}
}
2015-07-27 13:52:57 +02:00
/** Serializes the parameter types of a function or the constructor of a class. Used by the __metadata decorator for a method or set accessor. */
function emitSerializedParameterTypesOfNode(node: Node) {
// serialization of parameter types uses the following rules:
//
// * If the declaration is a class, the parameters of the first constructor with a body are used.
// * If the declaration is function-like and has a body, the parameters of the function are used.
2015-07-27 13:52:57 +02:00
//
// For the rules on serializing the type of each parameter declaration, see `serializeTypeOfDeclaration`.
if (node) {
2015-07-24 00:18:48 +02:00
let valueDeclaration: FunctionLikeDeclaration;
if (node.kind === SyntaxKind.ClassDeclaration) {
valueDeclaration = getFirstConstructorWithBody(<ClassDeclaration>node);
}
else if (isFunctionLike(node) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
valueDeclaration = <FunctionLikeDeclaration>node;
}
2015-07-27 13:52:57 +02:00
if (valueDeclaration) {
2015-07-24 00:18:48 +02:00
const parameters = valueDeclaration.parameters;
const skipThisCount = parameters.length && (<Identifier>parameters[0].name).originalKeywordKind === SyntaxKind.ThisKeyword ? 1 : 0;
2015-07-24 00:18:48 +02:00
const parameterCount = parameters.length;
if (parameterCount > skipThisCount) {
for (let i = skipThisCount; i < parameterCount; i++) {
if (i > skipThisCount) {
write(", ");
}
2015-07-27 13:52:57 +02:00
if (parameters[i].dotDotDotToken) {
2015-07-24 00:18:48 +02:00
let parameterType = parameters[i].type;
if (parameterType && parameterType.kind === SyntaxKind.ArrayType) {
parameterType = (<ArrayTypeNode>parameterType).elementType;
}
else if (parameterType && parameterType.kind === SyntaxKind.TypeReference && (<TypeReferenceNode>parameterType).typeArguments && (<TypeReferenceNode>parameterType).typeArguments.length === 1) {
parameterType = (<TypeReferenceNode>parameterType).typeArguments[0];
}
else {
parameterType = undefined;
}
2015-07-27 13:52:57 +02:00
emitSerializedTypeNode(parameterType);
}
else {
emitSerializedTypeOfNode(parameters[i]);
}
}
}
}
}
}
2015-07-27 13:52:57 +02:00
/** Serializes the return type of function. Used by the __metadata decorator for a method. */
2015-12-01 21:12:31 +01:00
function emitSerializedReturnTypeOfNode(node: Node) {
if (node && isFunctionLike(node)) {
2016-06-14 15:50:44 +02:00
if ((<FunctionLikeDeclaration>node).type) {
emitSerializedTypeNode((<FunctionLikeDeclaration>node).type);
return;
}
2016-06-14 15:50:44 +02:00
else if (isAsyncFunctionLike(<FunctionLikeDeclaration>node)) {
write("Promise");
return;
}
}
2015-07-27 13:52:57 +02:00
write("void 0");
}
2015-07-27 13:52:57 +02:00
2015-04-03 00:22:53 +02:00
function emitSerializedTypeMetadata(node: Declaration, writeComma: boolean): number {
// This method emits the serialized type metadata for a decorator target.
// The caller should have already tested whether the node has decorators.
let argumentsWritten = 0;
if (compilerOptions.emitDecoratorMetadata) {
if (shouldEmitTypeMetadata(node)) {
if (writeComma) {
write(", ");
}
writeLine();
write("__metadata('design:type', ");
emitSerializedTypeOfNode(node);
write(")");
argumentsWritten++;
2015-03-18 01:09:39 +01:00
}
2015-04-03 00:22:53 +02:00
if (shouldEmitParamTypesMetadata(node)) {
if (writeComma || argumentsWritten) {
write(", ");
2015-03-18 01:09:39 +01:00
}
writeLine();
write("__metadata('design:paramtypes', [");
emitSerializedParameterTypesOfNode(node);
write("])");
argumentsWritten++;
2015-03-18 01:09:39 +01:00
}
2015-04-03 00:22:53 +02:00
if (shouldEmitReturnTypeMetadata(node)) {
if (writeComma || argumentsWritten) {
write(", ");
}
2015-07-27 13:52:57 +02:00
writeLine();
write("__metadata('design:returntype', ");
emitSerializedReturnTypeOfNode(node);
write(")");
argumentsWritten++;
2015-03-18 01:09:39 +01:00
}
}
2015-07-27 13:52:57 +02:00
2015-04-03 00:22:53 +02:00
return argumentsWritten;
2015-03-18 01:09:39 +01:00
}
2015-07-27 13:52:57 +02:00
function emitInterfaceDeclaration(node: InterfaceDeclaration) {
emitCommentsOnNotEmittedNode(node);
}
2015-01-23 00:58:00 +01:00
function shouldEmitEnumDeclaration(node: EnumDeclaration) {
2015-11-04 23:02:33 +01:00
const isConstEnum = isConst(node);
return !isConstEnum || compilerOptions.preserveConstEnums || compilerOptions.isolatedModules;
2015-01-23 00:58:00 +01:00
}
function emitEnumDeclaration(node: EnumDeclaration) {
// const enums are completely erased during compilation.
2015-01-23 00:58:00 +01:00
if (!shouldEmitEnumDeclaration(node)) {
return;
}
2015-01-23 00:58:00 +01:00
2015-05-11 23:17:34 +02:00
if (!shouldHoistDeclarationInSystemJsModule(node)) {
// do not emit var if variable was already hoisted
2015-11-23 21:55:29 +01:00
const isES6ExportedEnum = isES6ExportedDeclaration(node);
2015-12-01 23:57:59 +01:00
if (!(node.flags & NodeFlags.Export) || (isES6ExportedEnum && isFirstDeclarationOfKind(node, node.symbol && node.symbol.declarations, SyntaxKind.EnumDeclaration))) {
2015-05-11 23:17:34 +02:00
emitStart(node);
2015-11-23 21:55:29 +01:00
if (isES6ExportedEnum) {
2015-05-11 23:17:34 +02:00
write("export ");
}
write("var ");
emit(node.name);
emitEnd(node);
write(";");
}
}
writeLine();
emitStart(node);
write("(function (");
emitStart(node.name);
2015-03-24 00:16:29 +01:00
write(getGeneratedNameForNode(node));
emitEnd(node.name);
write(") {");
increaseIndent();
2015-01-22 23:45:55 +01:00
emitLines(node.members);
decreaseIndent();
writeLine();
emitToken(SyntaxKind.CloseBraceToken, node.members.end);
write(")(");
emitModuleMemberName(node);
write(" || (");
emitModuleMemberName(node);
write(" = {}));");
emitEnd(node);
if (!isES6ExportedDeclaration(node) && node.flags & NodeFlags.Export && !shouldHoistDeclarationInSystemJsModule(node)) {
// do not emit var if variable was already hoisted
writeLine();
emitStart(node);
write("var ");
emit(node.name);
write(" = ");
emitModuleMemberName(node);
emitEnd(node);
write(";");
}
if (modulekind !== ModuleKind.ES6 && node.parent === currentSourceFile) {
2015-09-17 22:26:04 +02:00
if (modulekind === ModuleKind.System && (node.flags & NodeFlags.Export)) {
2015-05-11 09:09:06 +02:00
// write the call to exporter for enum
writeLine();
write(`${exportFunctionForFile}("`);
emitDeclarationName(node);
write(`", `);
emitDeclarationName(node);
write(");");
}
emitExportMemberAssignments(node.name);
}
2015-01-22 23:45:55 +01:00
}
2015-01-22 23:45:55 +01:00
function emitEnumMember(node: EnumMember) {
2015-11-04 23:02:33 +01:00
const enumParent = <EnumDeclaration>node.parent;
2015-01-22 23:45:55 +01:00
emitStart(node);
2015-03-24 00:16:29 +01:00
write(getGeneratedNameForNode(enumParent));
2015-01-22 23:45:55 +01:00
write("[");
2015-03-24 00:16:29 +01:00
write(getGeneratedNameForNode(enumParent));
2015-01-22 23:45:55 +01:00
write("[");
emitExpressionForPropertyName(node.name);
write("] = ");
2015-02-03 00:50:16 +01:00
writeEnumMemberDeclarationValue(node);
2015-01-22 23:45:55 +01:00
write("] = ");
emitExpressionForPropertyName(node.name);
emitEnd(node);
write(";");
}
function writeEnumMemberDeclarationValue(member: EnumMember) {
2015-11-04 23:02:33 +01:00
const value = resolver.getConstantValue(member);
if (value !== undefined) {
write(value.toString());
return;
}
else if (member.initializer) {
emit(member.initializer);
}
else {
write("undefined");
}
}
function getInnerMostModuleDeclarationFromDottedModule(moduleDeclaration: ModuleDeclaration): ModuleDeclaration {
if (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
2015-11-04 23:02:33 +01:00
const recursiveInnerModule = getInnerMostModuleDeclarationFromDottedModule(<ModuleDeclaration>moduleDeclaration.body);
return recursiveInnerModule || <ModuleDeclaration>moduleDeclaration.body;
}
2014-07-13 01:04:16 +02:00
}
2015-01-28 02:09:57 +01:00
function shouldEmitModuleDeclaration(node: ModuleDeclaration) {
return isInstantiatedModule(node, compilerOptions.preserveConstEnums || compilerOptions.isolatedModules);
2015-01-23 00:58:00 +01:00
}
function isModuleMergedWithES6Class(node: ModuleDeclaration) {
return languageVersion === ScriptTarget.ES6 && !!(resolver.getNodeCheckFlags(node) & NodeCheckFlags.LexicalModuleMergesWithClass);
}
2015-11-23 21:55:29 +01:00
function isFirstDeclarationOfKind(node: Declaration, declarations: Declaration[], kind: SyntaxKind) {
return !forEach(declarations, declaration => declaration.kind === kind && declaration.pos < node.pos);
}
function emitModuleDeclaration(node: ModuleDeclaration) {
// Emit only if this module is non-ambient.
2015-11-04 23:02:33 +01:00
const shouldEmit = shouldEmitModuleDeclaration(node);
2014-07-13 01:04:16 +02:00
if (!shouldEmit) {
return emitCommentsOnNotEmittedNode(node);
}
2015-11-04 23:02:33 +01:00
const hoistedInDeclarationScope = shouldHoistDeclarationInSystemJsModule(node);
const emitVarForModule = !hoistedInDeclarationScope && !isModuleMergedWithES6Class(node);
2015-01-23 00:58:00 +01:00
2015-04-20 22:40:13 +02:00
if (emitVarForModule) {
const isES6ExportedNamespace = isES6ExportedDeclaration(node);
2015-12-01 23:57:59 +01:00
if (!isES6ExportedNamespace || isFirstDeclarationOfKind(node, node.symbol && node.symbol.declarations, SyntaxKind.ModuleDeclaration)) {
emitStart(node);
if (isES6ExportedNamespace) {
write("export ");
}
write("var ");
emit(node.name);
write(";");
emitEnd(node);
writeLine();
}
}
emitStart(node);
write("(function (");
emitStart(node.name);
2015-03-24 00:16:29 +01:00
write(getGeneratedNameForNode(node));
emitEnd(node.name);
write(") ");
2016-06-03 15:34:33 +02:00
Debug.assert(node.body !== undefined); // node.body must exist, as this is a non-ambient module
if (node.body.kind === SyntaxKind.ModuleBlock) {
const saveConvertedLoopState = convertedLoopState;
2015-11-04 23:02:33 +01:00
const saveTempFlags = tempFlags;
const saveTempVariables = tempVariables;
convertedLoopState = undefined;
tempFlags = 0;
tempVariables = undefined;
emit(node.body);
Debug.assert(convertedLoopState === undefined);
convertedLoopState = saveConvertedLoopState;
tempFlags = saveTempFlags;
tempVariables = saveTempVariables;
2014-07-13 01:04:16 +02:00
}
else {
write("{");
increaseIndent();
emitCaptureThisForNodeIfNecessary(node);
writeLine();
emit(node.body);
decreaseIndent();
writeLine();
2015-11-04 23:02:33 +01:00
const moduleBlock = <ModuleBlock>getInnerMostModuleDeclarationFromDottedModule(node).body;
emitToken(SyntaxKind.CloseBraceToken, moduleBlock.statements.end);
2014-07-13 01:04:16 +02:00
}
write(")(");
// write moduleDecl = containingModule.m only if it is not exported es6 module member
if ((node.flags & NodeFlags.Export) && !isES6ExportedDeclaration(node)) {
emit(node.name);
write(" = ");
2014-07-13 01:04:16 +02:00
}
emitModuleMemberName(node);
write(" || (");
emitModuleMemberName(node);
write(" = {}));");
emitEnd(node);
if (!isES6ExportedDeclaration(node) && node.name.kind === SyntaxKind.Identifier && node.parent === currentSourceFile) {
2015-09-17 22:26:04 +02:00
if (modulekind === ModuleKind.System && (node.flags & NodeFlags.Export)) {
2015-04-11 15:33:09 +02:00
writeLine();
write(`${exportFunctionForFile}("`);
emitDeclarationName(node);
write(`", `);
emitDeclarationName(node);
write(");");
2015-04-11 15:33:09 +02:00
}
emitExportMemberAssignments(<Identifier>node.name);
}
}
2015-08-17 19:37:44 +02:00
/*
2015-10-06 21:37:10 +02:00
* Some bundlers (SystemJS builder) sometimes want to rename dependencies.
* Here we check if alternative name was provided for a given moduleName and return it if possible.
2015-08-17 19:37:44 +02:00
*/
function tryRenameExternalModule(moduleName: LiteralExpression): string {
2016-08-16 01:41:32 +02:00
if (renamedDependencies && moduleName.text in renamedDependencies) {
return `"${renamedDependencies[moduleName.text]}"`;
}
return undefined;
}
function emitRequire(moduleName: Expression) {
2015-02-09 02:33:45 +01:00
if (moduleName.kind === SyntaxKind.StringLiteral) {
write("require(");
2015-11-04 23:02:33 +01:00
const text = tryRenameExternalModule(<LiteralExpression>moduleName);
if (text) {
write(text);
}
else {
emitStart(moduleName);
emitLiteral(<LiteralExpression>moduleName);
emitEnd(moduleName);
}
emitToken(SyntaxKind.CloseParenToken, moduleName.end);
}
else {
write("require()");
}
}
function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) {
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
return <ImportEqualsDeclaration>node;
}
2015-11-04 23:02:33 +01:00
const importClause = (<ImportDeclaration>node).importClause;
if (importClause && importClause.namedBindings && importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
return <NamespaceImport>importClause.namedBindings;
}
}
function isDefaultImport(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) {
return node.kind === SyntaxKind.ImportDeclaration && (<ImportDeclaration>node).importClause && !!(<ImportDeclaration>node).importClause.name;
}
function emitExportImportAssignments(node: Node) {
if (isAliasSymbolDeclaration(node) && resolver.isValueAliasDeclaration(node)) {
emitExportMemberAssignments(<Identifier>(<Declaration>node).name);
}
forEachChild(node, emitExportImportAssignments);
}
function emitImportDeclaration(node: ImportDeclaration) {
2015-09-17 22:26:04 +02:00
if (modulekind !== ModuleKind.ES6) {
return emitExternalImportDeclaration(node);
}
// ES6 import
if (node.importClause) {
2015-11-04 23:02:33 +01:00
const shouldEmitDefaultBindings = resolver.isReferencedAliasDeclaration(node.importClause);
const shouldEmitNamedBindings = node.importClause.namedBindings && resolver.isReferencedAliasDeclaration(node.importClause.namedBindings, /* checkChildren */ true);
if (shouldEmitDefaultBindings || shouldEmitNamedBindings) {
write("import ");
emitStart(node.importClause);
if (shouldEmitDefaultBindings) {
emit(node.importClause.name);
if (shouldEmitNamedBindings) {
write(", ");
}
}
if (shouldEmitNamedBindings) {
emitLeadingComments(node.importClause.namedBindings);
emitStart(node.importClause.namedBindings);
if (node.importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
write("* as ");
emit((<NamespaceImport>node.importClause.namedBindings).name);
}
else {
write("{ ");
emitExportOrImportSpecifierList((<NamedImports>node.importClause.namedBindings).elements, resolver.isReferencedAliasDeclaration);
write(" }");
}
emitEnd(node.importClause.namedBindings);
emitTrailingComments(node.importClause.namedBindings);
}
emitEnd(node.importClause);
write(" from ");
emit(node.moduleSpecifier);
write(";");
}
}
else {
write("import ");
emit(node.moduleSpecifier);
write(";");
}
}
function emitExternalImportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration) {
if (contains(externalImports, node)) {
2015-11-04 23:02:33 +01:00
const isExportedImport = node.kind === SyntaxKind.ImportEqualsDeclaration && (node.flags & NodeFlags.Export) !== 0;
const namespaceDeclaration = getNamespaceDeclarationNode(node);
const varOrConst = (languageVersion <= ScriptTarget.ES5) ? "var " : "const ";
2015-09-17 22:26:04 +02:00
if (modulekind !== ModuleKind.AMD) {
2015-02-08 21:13:56 +01:00
emitLeadingComments(node);
emitStart(node);
if (namespaceDeclaration && !isDefaultImport(node)) {
// import x = require("foo")
// import * as x from "foo"
if (!isExportedImport) {
write(varOrConst);
};
emitModuleMemberName(namespaceDeclaration);
2015-02-08 21:13:56 +01:00
write(" = ");
}
else {
// import "foo"
// import x from "foo"
// import { x, y } from "foo"
// import d, * as x from "foo"
// import d, { x, y } from "foo"
2016-08-27 01:46:10 +02:00
const isNakedImport = node.kind === SyntaxKind.ImportDeclaration && !(<ImportDeclaration>node).importClause;
if (!isNakedImport) {
write(varOrConst);
write(getGeneratedNameForNode(<ImportDeclaration>node));
write(" = ");
}
}
emitRequire(getExternalModuleName(node));
if (namespaceDeclaration && isDefaultImport(node)) {
// import d, * as x from "foo"
write(", ");
emitModuleMemberName(namespaceDeclaration);
write(" = ");
write(getGeneratedNameForNode(<ImportDeclaration>node));
}
write(";");
2015-02-08 21:13:56 +01:00
emitEnd(node);
emitExportImportAssignments(node);
2015-02-08 21:13:56 +01:00
emitTrailingComments(node);
}
2015-02-08 21:13:56 +01:00
else {
if (isExportedImport) {
emitModuleMemberName(namespaceDeclaration);
write(" = ");
emit(namespaceDeclaration.name);
write(";");
}
else if (namespaceDeclaration && isDefaultImport(node)) {
// import d, * as x from "foo"
write(varOrConst);
emitModuleMemberName(namespaceDeclaration);
write(" = ");
write(getGeneratedNameForNode(<ImportDeclaration>node));
write(";");
}
emitExportImportAssignments(node);
}
2014-07-13 01:04:16 +02:00
}
}
function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) {
2015-02-08 21:13:56 +01:00
if (isExternalModuleImportEqualsDeclaration(node)) {
emitExternalImportDeclaration(node);
2015-02-08 21:13:56 +01:00
return;
2014-07-13 01:04:16 +02:00
}
2015-02-08 21:13:56 +01:00
// 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
if (resolver.isReferencedAliasDeclaration(node) ||
(!isCurrentFileExternalModule && resolver.isTopLevelValueImportEqualsWithEntityName(node))) {
2015-02-08 21:13:56 +01:00
emitLeadingComments(node);
emitStart(node);
2015-07-27 13:52:57 +02:00
// variable declaration for import-equals declaration can be hoisted in system modules
// in this case 'var' should be omitted and emit should contain only initialization
2015-11-04 23:02:33 +01:00
const variableDeclarationIsHoisted = shouldHoistVariable(node, /*checkIfSourceFileLevelDecl*/ true);
2015-07-27 13:52:57 +02:00
// is it top level export import v = a.b.c in system module?
// if yes - it needs to be rewritten as exporter('v', v = a.b.c)
2015-11-04 23:02:33 +01:00
const isExported = isSourceFileLevelDeclarationInSystemJsModule(node, /*isExported*/ true);
2015-07-27 13:52:57 +02:00
if (!variableDeclarationIsHoisted) {
Debug.assert(!isExported);
2015-07-27 13:52:57 +02:00
if (isES6ExportedDeclaration(node)) {
write("export ");
write("var ");
}
else if (!(node.flags & NodeFlags.Export)) {
write("var ");
}
}
2015-07-27 13:52:57 +02:00
if (isExported) {
write(`${exportFunctionForFile}("`);
emitNodeWithoutSourceMap(node.name);
write(`", `);
}
2015-02-08 21:13:56 +01:00
emitModuleMemberName(node);
write(" = ");
emit(node.moduleReference);
if (isExported) {
write(")");
}
2015-07-27 13:52:57 +02:00
write(";");
2015-02-08 21:13:56 +01:00
emitEnd(node);
emitExportImportAssignments(node);
2015-02-08 21:13:56 +01:00
emitTrailingComments(node);
2014-07-13 01:04:16 +02:00
}
}
function emitExportDeclaration(node: ExportDeclaration) {
2015-09-17 22:26:04 +02:00
Debug.assert(modulekind !== ModuleKind.System);
2015-04-10 21:10:38 +02:00
2015-09-17 22:26:04 +02:00
if (modulekind !== ModuleKind.ES6) {
if (node.moduleSpecifier && (!node.exportClause || resolver.isValueAliasDeclaration(node))) {
emitStart(node);
2015-11-04 23:02:33 +01:00
const generatedName = getGeneratedNameForNode(node);
if (node.exportClause) {
// export { x, y, ... } from "foo"
2015-09-17 22:26:04 +02:00
if (modulekind !== ModuleKind.AMD) {
write("var ");
write(generatedName);
write(" = ");
emitRequire(getExternalModuleName(node));
write(";");
}
2015-11-04 23:02:33 +01:00
for (const specifier of node.exportClause.elements) {
if (resolver.isValueAliasDeclaration(specifier)) {
writeLine();
emitStart(specifier);
emitContainingModuleName(specifier);
write(".");
emitNodeWithCommentsAndWithoutSourcemap(specifier.name);
write(" = ");
write(generatedName);
write(".");
emitNodeWithCommentsAndWithoutSourcemap(specifier.propertyName || specifier.name);
write(";");
emitEnd(specifier);
}
}
}
else {
// export * from "foo"
if (hasExportStarsToExportValues && resolver.moduleExportsSomeValue(node.moduleSpecifier)) {
writeLine();
write("__export(");
if (modulekind !== ModuleKind.AMD) {
emitRequire(getExternalModuleName(node));
}
else {
write(generatedName);
}
write(");");
}
}
2015-03-23 20:37:22 +01:00
emitEnd(node);
2015-02-15 17:25:24 +01:00
}
}
else {
2015-03-23 20:37:22 +01:00
if (!node.exportClause || resolver.isValueAliasDeclaration(node)) {
write("export ");
if (node.exportClause) {
// export { x, y, ... }
write("{ ");
emitExportOrImportSpecifierList(node.exportClause.elements, resolver.isValueAliasDeclaration);
2015-03-23 20:37:22 +01:00
write(" }");
}
else {
write("*");
}
if (node.moduleSpecifier) {
write(" from ");
emit(node.moduleSpecifier);
2015-03-23 20:37:22 +01:00
}
write(";");
2015-02-15 17:25:24 +01:00
}
2015-03-23 20:37:22 +01:00
}
}
function emitExportOrImportSpecifierList(specifiers: ImportOrExportSpecifier[], shouldEmit: (node: Node) => boolean) {
2015-09-17 22:26:04 +02:00
Debug.assert(modulekind === ModuleKind.ES6);
2015-03-23 20:37:22 +01:00
let needsComma = false;
2015-11-04 23:02:33 +01:00
for (const specifier of specifiers) {
if (shouldEmit(specifier)) {
2015-03-23 20:37:22 +01:00
if (needsComma) {
write(", ");
}
if (specifier.propertyName) {
emit(specifier.propertyName);
2015-03-23 20:37:22 +01:00
write(" as ");
}
emit(specifier.name);
2015-03-23 20:37:22 +01:00
needsComma = true;
}
}
}
function emitExportAssignment(node: ExportAssignment) {
if (!node.isExportEquals && resolver.isValueAliasDeclaration(node)) {
2015-09-17 22:26:04 +02:00
if (modulekind === ModuleKind.ES6) {
writeLine();
emitStart(node);
write("export default ");
2015-11-04 23:02:33 +01:00
const expression = node.expression;
emit(expression);
if (expression.kind !== SyntaxKind.FunctionDeclaration &&
expression.kind !== SyntaxKind.ClassDeclaration) {
write(";");
}
emitEnd(node);
}
else {
writeLine();
emitStart(node);
2015-09-17 22:26:04 +02:00
if (modulekind === ModuleKind.System) {
2015-04-10 21:10:38 +02:00
write(`${exportFunctionForFile}("default",`);
emit(node.expression);
write(")");
}
else {
2015-07-11 13:55:27 +02:00
emitEs6ExportDefaultCompat(node);
2015-04-10 21:10:38 +02:00
emitContainingModuleName(node);
2015-04-20 22:40:13 +02:00
if (languageVersion === ScriptTarget.ES3) {
write('["default"] = ');
}
else {
2015-04-20 22:40:13 +02:00
write(".default = ");
}
2015-04-10 21:10:38 +02:00
emit(node.expression);
}
write(";");
emitEnd(node);
2015-02-08 21:13:56 +01:00
}
}
}
function collectExternalModuleInfo(sourceFile: SourceFile) {
2015-02-08 21:13:56 +01:00
externalImports = [];
2016-08-11 01:47:06 +02:00
exportSpecifiers = createMap<ExportSpecifier[]>();
exportEquals = undefined;
hasExportStarsToExportValues = false;
2015-11-04 23:02:33 +01:00
for (const node of sourceFile.statements) {
switch (node.kind) {
case SyntaxKind.ImportDeclaration:
if (!(<ImportDeclaration>node).importClause ||
resolver.isReferencedAliasDeclaration((<ImportDeclaration>node).importClause, /*checkChildren*/ true)) {
// import "mod"
// import x from "mod" where x is referenced
// import * as x from "mod" where x is referenced
// import { x, y } from "mod" where at least one import is referenced
externalImports.push(<ImportDeclaration>node);
}
break;
case SyntaxKind.ImportEqualsDeclaration:
if ((<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference && resolver.isReferencedAliasDeclaration(node)) {
// import x = require("mod") where x is referenced
externalImports.push(<ImportEqualsDeclaration>node);
}
break;
case SyntaxKind.ExportDeclaration:
if ((<ExportDeclaration>node).moduleSpecifier) {
if (!(<ExportDeclaration>node).exportClause) {
// export * from "mod"
if (resolver.moduleExportsSomeValue((<ExportDeclaration>node).moduleSpecifier)) {
externalImports.push(<ExportDeclaration>node);
hasExportStarsToExportValues = true;
}
}
else if (resolver.isValueAliasDeclaration(node)) {
// export { x, y } from "mod" where at least one export is a value symbol
externalImports.push(<ExportDeclaration>node);
}
}
else {
// export { x, y }
2015-11-04 23:02:33 +01:00
for (const specifier of (<ExportDeclaration>node).exportClause.elements) {
const name = (specifier.propertyName || specifier.name).text;
2016-08-15 21:03:39 +02:00
(exportSpecifiers[name] || (exportSpecifiers[name] = [])).push(specifier);
}
}
break;
case SyntaxKind.ExportAssignment:
if ((<ExportAssignment>node).isExportEquals && !exportEquals) {
// export = x
exportEquals = <ExportAssignment>node;
}
break;
2015-02-08 21:13:56 +01:00
}
}
}
function emitExportStarHelper() {
if (hasExportStarsToExportValues) {
writeLine();
write("function __export(m) {");
increaseIndent();
writeLine();
write("for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];");
decreaseIndent();
writeLine();
write("}");
}
}
function getLocalNameForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): string {
2015-11-04 23:02:33 +01:00
const namespaceDeclaration = getNamespaceDeclarationNode(node);
if (namespaceDeclaration && !isDefaultImport(node)) {
return getTextOfNodeFromSourceText(currentText, namespaceDeclaration.name);
}
if (node.kind === SyntaxKind.ImportDeclaration && (<ImportDeclaration>node).importClause) {
return getGeneratedNameForNode(node);
}
if (node.kind === SyntaxKind.ExportDeclaration && (<ExportDeclaration>node).moduleSpecifier) {
return getGeneratedNameForNode(node);
2015-04-10 21:10:38 +02:00
}
}
function getExternalModuleNameText(importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration, emitRelativePathAsModuleName: boolean): string {
if (emitRelativePathAsModuleName) {
const name = getExternalModuleNameFromDeclaration(host, resolver, importNode);
if (name) {
return `"${name}"`;
}
}
2015-11-04 23:02:33 +01:00
const moduleName = getExternalModuleName(importNode);
2015-04-20 22:40:13 +02:00
if (moduleName.kind === SyntaxKind.StringLiteral) {
return tryRenameExternalModule(<LiteralExpression>moduleName) || getLiteralText(<LiteralExpression>moduleName);
2015-04-10 21:10:38 +02:00
}
2015-04-20 22:40:13 +02:00
return undefined;
2015-04-10 21:10:38 +02:00
}
function emitVariableDeclarationsForImports(): void {
if (externalImports.length === 0) {
return;
}
writeLine();
let started = false;
2015-11-04 23:02:33 +01:00
for (const importNode of externalImports) {
2015-04-21 01:56:36 +02:00
// do not create variable declaration for exports and imports that lack import clause
2015-11-04 23:02:33 +01:00
const skipNode =
importNode.kind === SyntaxKind.ExportDeclaration ||
2015-06-26 01:24:41 +02:00
(importNode.kind === SyntaxKind.ImportDeclaration && !(<ImportDeclaration>importNode).importClause);
if (skipNode) {
2015-04-10 21:10:38 +02:00
continue;
}
if (!started) {
write("var ");
started = true;
}
else {
2015-04-10 21:10:38 +02:00
write(", ");
}
2015-04-20 22:40:13 +02:00
write(getLocalNameForExternalImport(importNode));
2015-04-10 21:10:38 +02:00
}
if (started) {
write(";");
}
}
function emitLocalStorageForExportedNamesIfNecessary(exportedDeclarations: (Identifier | Declaration)[]): string {
// when resolving exports local exported entries/indirect exported entries in the module
// should always win over entries with similar names that were added via star exports
// to support this we store names of local/indirect exported entries in a set.
// this set is used to filter names brought by star exports.
if (!hasExportStarsToExportValues) {
// local names set is needed only in presence of star exports
return undefined;
}
// local names set should only be added if we have anything exported
2016-08-15 21:03:39 +02:00
if (!exportedDeclarations && !someProperties(exportSpecifiers)) {
// no exported declarations (export var ...) or export specifiers (export {x})
// check if we have any non star export declarations.
let hasExportDeclarationWithExportClause = false;
2015-11-04 23:02:33 +01:00
for (const externalImport of externalImports) {
if (externalImport.kind === SyntaxKind.ExportDeclaration && (<ExportDeclaration>externalImport).exportClause) {
hasExportDeclarationWithExportClause = true;
break;
}
}
if (!hasExportDeclarationWithExportClause) {
// we still need to emit exportStar helper
return emitExportStarFunction(/*localNames*/ undefined);
}
}
const exportedNamesStorageRef = makeUniqueName("exportedNames");
writeLine();
write(`var ${exportedNamesStorageRef} = {`);
increaseIndent();
let started = false;
if (exportedDeclarations) {
2015-12-23 00:45:00 +01:00
for (let i = 0; i < exportedDeclarations.length; i++) {
// write name of exported declaration, i.e 'export var x...'
writeExportedName(exportedDeclarations[i]);
}
}
if (exportSpecifiers) {
2015-11-04 23:02:33 +01:00
for (const n in exportSpecifiers) {
for (const specifier of exportSpecifiers[n]) {
// write name of export specified, i.e. 'export {x}'
writeExportedName(specifier.name);
}
}
}
2015-11-04 23:02:33 +01:00
for (const externalImport of externalImports) {
2015-04-29 01:37:08 +02:00
if (externalImport.kind !== SyntaxKind.ExportDeclaration) {
continue;
}
2015-11-04 23:02:33 +01:00
const exportDecl = <ExportDeclaration>externalImport;
2015-04-29 01:37:08 +02:00
if (!exportDecl.exportClause) {
// export * from ...
continue;
}
2015-11-04 23:02:33 +01:00
for (const element of exportDecl.exportClause.elements) {
2015-04-29 01:37:08 +02:00
// write name of indirectly exported entry, i.e. 'export {x} from ...'
writeExportedName(element.name || element.propertyName);
}
}
decreaseIndent();
writeLine();
write("};");
return emitExportStarFunction(exportedNamesStorageRef);
2015-06-18 23:01:49 +02:00
function emitExportStarFunction(localNames: string): string {
const exportStarFunction = makeUniqueName("exportStar");
writeLine();
// define an export star helper function
write(`function ${exportStarFunction}(m) {`);
2015-04-29 22:56:17 +02:00
increaseIndent();
writeLine();
2015-07-21 07:40:22 +02:00
write(`var exports = {};`);
writeLine();
2015-04-29 22:56:17 +02:00
write(`for(var n in m) {`);
increaseIndent();
writeLine();
2015-04-29 22:56:17 +02:00
write(`if (n !== "default"`);
if (localNames) {
2015-04-29 20:43:23 +02:00
write(`&& !${localNames}.hasOwnProperty(n)`);
}
2015-07-21 07:40:22 +02:00
write(`) exports[n] = m[n];`);
2015-04-29 22:56:17 +02:00
decreaseIndent();
writeLine();
2015-04-29 22:56:17 +02:00
write("}");
2015-07-21 07:40:22 +02:00
writeLine();
2015-07-24 00:18:48 +02:00
write(`${exportFunctionForFile}(exports);`);
2015-04-29 22:56:17 +02:00
decreaseIndent();
writeLine();
2015-06-26 01:24:41 +02:00
write("}");
return exportStarFunction;
}
2015-06-18 23:01:49 +02:00
function writeExportedName(node: Identifier | Declaration): void {
2015-04-29 22:56:17 +02:00
// do not record default exports
2015-07-09 23:44:47 +02:00
// they are local to module and never overwritten (explicitly skipped) by star export
2015-04-29 20:43:23 +02:00
if (node.kind !== SyntaxKind.Identifier && node.flags & NodeFlags.Default) {
return;
}
if (started) {
write(",");
}
else {
started = true;
}
writeLine();
write("'");
if (node.kind === SyntaxKind.Identifier) {
emitNodeWithCommentsAndWithoutSourcemap(node);
}
else {
emitDeclarationName(<Declaration>node);
}
write("': true");
}
}
function processTopLevelVariableAndFunctionDeclarations(node: SourceFile): (Identifier | Declaration)[] {
2015-07-09 23:44:47 +02:00
// per ES6 spec:
2015-04-21 01:56:36 +02:00
// 15.2.1.16.4 ModuleDeclarationInstantiation() Concrete Method
// - var declarations are initialized to undefined - 14.a.ii
// - function/generator declarations are instantiated - 16.a.iv
// this means that after module is instantiated but before its evaluation
// exported functions are already accessible at import sites
// in theory we should hoist only exported functions and its dependencies
// in practice to simplify things we'll hoist all source level functions and variable declaration
// including variables declarations for module and class declarations
let hoistedVars: (Identifier | ClassDeclaration | ModuleDeclaration | EnumDeclaration)[];
2015-04-10 21:10:38 +02:00
let hoistedFunctionDeclarations: FunctionDeclaration[];
let exportedDeclarations: (Identifier | Declaration)[];
2015-04-10 21:10:38 +02:00
visit(node);
if (hoistedVars) {
2015-04-10 21:10:38 +02:00
writeLine();
write("var ");
2016-08-11 01:47:06 +02:00
const seen = createMap<string>();
2015-12-23 00:45:00 +01:00
for (let i = 0; i < hoistedVars.length; i++) {
2015-11-04 23:02:33 +01:00
const local = hoistedVars[i];
const name = local.kind === SyntaxKind.Identifier
? <Identifier>local
: <Identifier>(<ClassDeclaration | ModuleDeclaration | EnumDeclaration>local).name;
if (name) {
// do not emit duplicate entries (in case of declaration merging) in the list of hoisted variables
2015-11-04 23:02:33 +01:00
const text = unescapeIdentifier(name.text);
2016-08-15 21:03:39 +02:00
if (text in seen) {
continue;
}
else {
seen[text] = text;
}
}
2015-04-10 21:10:38 +02:00
if (i !== 0) {
write(", ");
}
if (local.kind === SyntaxKind.ClassDeclaration || local.kind === SyntaxKind.ModuleDeclaration || local.kind === SyntaxKind.EnumDeclaration) {
emitDeclarationName(<ClassDeclaration | ModuleDeclaration | EnumDeclaration>local);
}
else {
emit(local);
}
2015-11-04 23:02:33 +01:00
const flags = getCombinedNodeFlags(local.kind === SyntaxKind.Identifier ? local.parent : local);
if (flags & NodeFlags.Export) {
if (!exportedDeclarations) {
exportedDeclarations = [];
}
exportedDeclarations.push(local);
}
2015-04-10 21:10:38 +02:00
}
2015-06-26 01:24:41 +02:00
write(";");
2015-04-10 21:10:38 +02:00
}
if (hoistedFunctionDeclarations) {
2015-11-04 23:02:33 +01:00
for (const f of hoistedFunctionDeclarations) {
2015-04-10 21:10:38 +02:00
writeLine();
emit(f);
if (f.flags & NodeFlags.Export) {
if (!exportedDeclarations) {
exportedDeclarations = [];
}
exportedDeclarations.push(f);
}
2015-04-10 21:10:38 +02:00
}
}
return exportedDeclarations;
2015-04-10 21:10:38 +02:00
function visit(node: Node): void {
2015-05-11 07:23:12 +02:00
if (node.flags & NodeFlags.Ambient) {
return;
}
2015-04-10 21:10:38 +02:00
if (node.kind === SyntaxKind.FunctionDeclaration) {
2015-04-22 07:27:33 +02:00
if (!hoistedFunctionDeclarations) {
hoistedFunctionDeclarations = [];
}
hoistedFunctionDeclarations.push(<FunctionDeclaration>node);
2015-04-10 21:10:38 +02:00
return;
}
if (node.kind === SyntaxKind.ClassDeclaration) {
2015-04-22 07:27:33 +02:00
if (!hoistedVars) {
hoistedVars = [];
}
hoistedVars.push(<ClassDeclaration>node);
return;
}
if (node.kind === SyntaxKind.EnumDeclaration) {
if (shouldEmitEnumDeclaration(<EnumDeclaration>node)) {
if (!hoistedVars) {
hoistedVars = [];
}
hoistedVars.push(<ModuleDeclaration>node);
2015-04-22 07:27:33 +02:00
}
return;
}
if (node.kind === SyntaxKind.ModuleDeclaration) {
if (shouldEmitModuleDeclaration(<ModuleDeclaration>node)) {
if (!hoistedVars) {
hoistedVars = [];
}
hoistedVars.push(<ModuleDeclaration>node);
}
2015-04-11 15:33:09 +02:00
return;
}
2015-04-10 21:10:38 +02:00
if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) {
if (shouldHoistVariable(<VariableDeclaration | BindingElement>node, /*checkIfSourceFileLevelDecl*/ false)) {
2015-11-04 23:02:33 +01:00
const name = (<VariableDeclaration | BindingElement>node).name;
if (name.kind === SyntaxKind.Identifier) {
2015-04-22 07:27:33 +02:00
if (!hoistedVars) {
hoistedVars = [];
}
hoistedVars.push(<Identifier>name);
}
else {
forEachChild(name, visit);
}
2015-04-10 21:10:38 +02:00
}
return;
}
2015-07-27 13:52:57 +02:00
if (isInternalModuleImportEqualsDeclaration(node) && resolver.isValueAliasDeclaration(node)) {
if (!hoistedVars) {
hoistedVars = [];
}
2015-07-27 13:52:57 +02:00
hoistedVars.push(node.name);
return;
}
2015-04-10 21:10:38 +02:00
if (isBindingPattern(node)) {
forEach((<BindingPattern>node).elements, visit);
return;
}
if (!isDeclaration(node)) {
forEachChild(node, visit);
}
}
}
function shouldHoistVariable(node: VariableDeclaration | VariableDeclarationList | BindingElement, checkIfSourceFileLevelDecl: boolean): boolean {
2015-04-24 06:14:03 +02:00
if (checkIfSourceFileLevelDecl && !shouldHoistDeclarationInSystemJsModule(node)) {
return false;
}
// hoist variable if
// - it is not block scoped
// - it is top level block scoped
2015-07-09 23:44:47 +02:00
// if block scoped variables are nested in some another block then
// no other functions can use them except ones that are defined at least in the same block
return (getCombinedNodeFlags(node) & NodeFlags.BlockScoped) === 0 ||
getEnclosingBlockScopeContainer(node).kind === SyntaxKind.SourceFile;
}
2015-04-13 10:32:16 +02:00
function isCurrentFileSystemExternalModule() {
return modulekind === ModuleKind.System && isCurrentFileExternalModule;
2015-04-10 21:10:38 +02:00
}
2015-08-07 01:38:53 +02:00
function emitSystemModuleBody(node: SourceFile, dependencyGroups: DependencyGroup[], startIndex: number): void {
2015-04-21 01:56:36 +02:00
// shape of the body in system modules:
// function (exports) {
// <list of local aliases for imports>
// <hoisted function declarations>
// <hoisted variable declarations>
// return {
// setters: [
// <list of setter function for imports>
// ],
// execute: function() {
// <module statements>
// }
// }
// <temp declarations>
// }
// I.e:
// import {x} from 'file1'
// var y = 1;
// export function foo() { return y + x(); }
// console.log(y);
// will be transformed to
// function(exports) {
// var file1; // local alias
// var y;
// function foo() { return y + file1.x(); }
// exports("foo", foo);
// return {
// setters: [
// function(v) { file1 = v }
// ],
// execute(): function() {
// y = 1;
// console.log(y);
// }
// };
// }
2015-04-10 21:10:38 +02:00
emitVariableDeclarationsForImports();
writeLine();
2015-11-04 23:02:33 +01:00
const exportedDeclarations = processTopLevelVariableAndFunctionDeclarations(node);
const exportStarFunction = emitLocalStorageForExportedNamesIfNecessary(exportedDeclarations);
2015-04-10 21:10:38 +02:00
writeLine();
write("return {");
increaseIndent();
writeLine();
2015-08-07 01:38:53 +02:00
emitSetters(exportStarFunction, dependencyGroups);
2015-04-10 21:10:38 +02:00
writeLine();
emitExecute(node, startIndex);
decreaseIndent();
writeLine();
write("}"); // return
2015-06-09 07:46:59 +02:00
emitTempDeclarations(/*newLine*/ true);
2015-04-10 21:10:38 +02:00
}
2015-08-07 01:38:53 +02:00
function emitSetters(exportStarFunction: string, dependencyGroups: DependencyGroup[]) {
2015-04-22 07:27:33 +02:00
write("setters:[");
2015-12-23 00:45:00 +01:00
for (let i = 0; i < dependencyGroups.length; i++) {
2015-04-10 21:10:38 +02:00
if (i !== 0) {
write(",");
}
writeLine();
increaseIndent();
2015-11-04 23:02:33 +01:00
const group = dependencyGroups[i];
2015-08-07 01:38:53 +02:00
// derive a unique name for parameter from the first named entry in the group
2015-11-04 23:02:33 +01:00
const parameterName = makeUniqueName(forEach(group, getLocalNameForExternalImport) || "");
2015-04-21 01:56:36 +02:00
write(`function (${parameterName}) {`);
2015-08-07 01:38:53 +02:00
increaseIndent();
2015-11-04 23:02:33 +01:00
for (const entry of group) {
const importVariableName = getLocalNameForExternalImport(entry) || "";
2015-08-07 01:38:53 +02:00
switch (entry.kind) {
case SyntaxKind.ImportDeclaration:
if (!(<ImportDeclaration>entry).importClause) {
// 'import "..."' case
// module is imported only for side-effects, no emit required
break;
}
// fall-through
case SyntaxKind.ImportEqualsDeclaration:
Debug.assert(importVariableName !== "");
2015-04-10 21:10:38 +02:00
2015-04-13 22:00:00 +02:00
writeLine();
2015-08-07 01:38:53 +02:00
// save import into the local
write(`${importVariableName} = ${parameterName};`);
2015-07-21 07:40:22 +02:00
writeLine();
2015-08-07 01:38:53 +02:00
break;
case SyntaxKind.ExportDeclaration:
Debug.assert(importVariableName !== "");
if ((<ExportDeclaration>entry).exportClause) {
// export {a, b as c} from 'foo'
// emit as:
2015-08-10 22:51:54 +02:00
// exports_({
// "a": _["a"],
// "c": _["b"]
// });
2015-04-13 22:00:00 +02:00
writeLine();
2015-08-10 22:51:54 +02:00
write(`${exportFunctionForFile}({`);
2015-07-21 07:40:22 +02:00
writeLine();
2015-08-10 22:51:54 +02:00
increaseIndent();
2015-12-23 00:45:00 +01:00
for (let i = 0, len = (<ExportDeclaration>entry).exportClause.elements.length; i < len; i++) {
2015-08-10 22:51:54 +02:00
if (i !== 0) {
write(",");
writeLine();
}
2015-11-04 23:02:33 +01:00
const e = (<ExportDeclaration>entry).exportClause.elements[i];
2015-08-10 22:51:54 +02:00
write(`"`);
2015-08-17 20:26:49 +02:00
emitNodeWithCommentsAndWithoutSourcemap(e.name);
2015-08-10 22:51:54 +02:00
write(`": ${parameterName}["`);
2015-08-17 20:26:49 +02:00
emitNodeWithCommentsAndWithoutSourcemap(e.propertyName || e.name);
2015-08-10 22:51:54 +02:00
write(`"]`);
2015-04-13 22:00:00 +02:00
}
2015-08-10 22:51:54 +02:00
decreaseIndent();
writeLine();
write("});");
2015-04-13 22:00:00 +02:00
}
2015-08-07 01:38:53 +02:00
else {
// collectExternalModuleInfo prefilters star exports to keep only ones that export values
// this means that check 'resolver.moduleExportsSomeValue' is redundant and can be omitted here
2015-07-21 07:40:22 +02:00
writeLine();
2015-08-07 01:38:53 +02:00
// export * from 'foo'
// emit as:
// exportStar(_foo);
write(`${exportStarFunction}(${parameterName});`);
2015-04-10 21:10:38 +02:00
}
2015-08-07 01:38:53 +02:00
2015-04-10 21:10:38 +02:00
writeLine();
2015-08-07 01:38:53 +02:00
break;
}
2015-04-10 21:10:38 +02:00
}
2015-04-10 21:10:38 +02:00
2015-08-07 01:38:53 +02:00
decreaseIndent();
2015-06-26 01:24:41 +02:00
write("}");
2015-04-10 21:10:38 +02:00
decreaseIndent();
}
write("],");
}
function emitExecute(node: SourceFile, startIndex: number) {
write("execute: function() {");
increaseIndent();
writeLine();
2015-12-23 00:45:00 +01:00
for (let i = startIndex; i < node.statements.length; i++) {
2015-11-04 23:02:33 +01:00
const statement = node.statements[i];
2015-04-10 21:10:38 +02:00
switch (statement.kind) {
// - function declarations are not emitted because they were already hoisted
// - import declarations are not emitted since they are already handled in setters
// - export declarations with module specifiers are not emitted since they were already written in setters
// - export declarations without module specifiers are emitted preserving the order
case SyntaxKind.FunctionDeclaration:
2015-04-10 21:10:38 +02:00
case SyntaxKind.ImportDeclaration:
2015-08-10 22:51:54 +02:00
continue;
case SyntaxKind.ExportDeclaration:
if (!(<ExportDeclaration>statement).moduleSpecifier) {
2015-11-04 23:02:33 +01:00
for (const element of (<ExportDeclaration>statement).exportClause.elements) {
2015-08-10 22:51:54 +02:00
// write call to exporter function for every export specifier in exports list
emitExportSpecifierInSystemModule(element);
}
}
2015-04-10 21:10:38 +02:00
continue;
case SyntaxKind.ImportEqualsDeclaration:
if (!isInternalModuleImportEqualsDeclaration(statement)) {
// - import equals declarations that import external modules are not emitted
continue;
}
// fall-though for import declarations that import internal modules
default:
writeLine();
emit(statement);
}
2015-04-10 21:10:38 +02:00
}
decreaseIndent();
writeLine();
2015-06-26 01:24:41 +02:00
write("}"); // execute
2015-04-10 21:10:38 +02:00
}
2015-11-03 01:54:12 +01:00
function writeModuleName(node: SourceFile, emitRelativePathAsModuleName?: boolean): void {
2015-11-02 21:53:27 +01:00
let moduleName = node.moduleName;
2015-11-03 01:54:12 +01:00
if (moduleName || (emitRelativePathAsModuleName && (moduleName = getResolvedExternalModuleName(host, node)))) {
2015-11-02 21:53:27 +01:00
write(`"${moduleName}", `);
}
}
2015-11-03 01:54:12 +01:00
function emitSystemModule(node: SourceFile, emitRelativePathAsModuleName?: boolean): void {
collectExternalModuleInfo(node);
2015-04-21 01:56:36 +02:00
// System modules has the following shape
// System.register(['dep-1', ... 'dep-n'], function(exports) {/* module body function */})
// 'exports' here is a function 'exports<T>(name: string, value: T): T' that is used to publish exported values.
2015-07-09 23:44:47 +02:00
// 'exports' returns its 'value' argument so in most cases expressions
2015-04-21 01:56:36 +02:00
// that mutate exported values can be rewritten as:
2015-07-09 23:44:47 +02:00
// expr -> exports('name', expr).
// The only exception in this rule is postfix unary operators,
2015-04-21 01:56:36 +02:00
// see comment to 'emitPostfixUnaryExpression' for more details
2015-04-10 21:10:38 +02:00
Debug.assert(!exportFunctionForFile);
2015-04-21 01:56:36 +02:00
// make sure that name of 'exports' function does not conflict with existing identifiers
2015-04-10 21:10:38 +02:00
exportFunctionForFile = makeUniqueName("exports");
contextObjectForFile = makeUniqueName("context");
writeLine();
write("System.register(");
2015-11-03 01:54:12 +01:00
writeModuleName(node, emitRelativePathAsModuleName);
2015-06-26 01:24:41 +02:00
write("[");
2016-08-11 01:47:06 +02:00
const groupIndices = createMap<number>();
2015-11-04 23:02:33 +01:00
const dependencyGroups: DependencyGroup[] = [];
2015-08-07 01:38:53 +02:00
2015-12-23 00:45:00 +01:00
for (let i = 0; i < externalImports.length; i++) {
const text = getExternalModuleNameText(externalImports[i], emitRelativePathAsModuleName);
if (text === undefined) {
continue;
}
// text should be quoted string
// for deduplication purposes in key remove leading and trailing quotes so 'a' and "a" will be considered the same
const key = text.substr(1, text.length - 2);
2016-08-15 21:03:39 +02:00
if (key in groupIndices) {
2015-08-07 01:38:53 +02:00
// deduplicate/group entries in dependency list by the dependency name
const groupIndex = groupIndices[key];
2015-08-07 01:38:53 +02:00
dependencyGroups[groupIndex].push(externalImports[i]);
continue;
}
else {
groupIndices[key] = dependencyGroups.length;
2015-08-07 01:38:53 +02:00
dependencyGroups.push([externalImports[i]]);
}
2015-08-17 19:37:44 +02:00
2015-04-20 22:40:13 +02:00
if (i !== 0) {
write(", ");
}
2015-04-20 22:40:13 +02:00
write(text);
}
write(`], function(${exportFunctionForFile}, ${contextObjectForFile}) {`);
2015-04-10 21:10:38 +02:00
writeLine();
increaseIndent();
2016-02-06 01:30:01 +01:00
const startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ true, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict);
writeLine();
write(`var __moduleName = ${contextObjectForFile} && ${contextObjectForFile}.id;`);
writeLine();
emitEmitHelpers(node);
2015-04-10 21:10:38 +02:00
emitCaptureThisForNodeIfNecessary(node);
2015-08-07 01:38:53 +02:00
emitSystemModuleBody(node, dependencyGroups, startIndex);
2015-04-10 21:10:38 +02:00
decreaseIndent();
writeLine();
write("});");
}
interface AMDDependencyNames {
aliasedModuleNames: string[];
unaliasedModuleNames: string[];
importAliasNames: string[];
}
2015-11-03 01:54:12 +01:00
function getAMDDependencyNames(node: SourceFile, includeNonAmdDependencies: boolean, emitRelativePathAsModuleName?: boolean): AMDDependencyNames {
2015-07-09 23:44:47 +02:00
// names of modules with corresponding parameter in the factory function
2015-11-04 23:02:33 +01:00
const aliasedModuleNames: string[] = [];
2015-07-09 23:44:47 +02:00
// names of modules with no corresponding parameters in factory function
2015-11-04 23:02:33 +01:00
const unaliasedModuleNames: string[] = [];
const importAliasNames: string[] = []; // names of the parameters in the factory function; these
2015-07-09 23:44:47 +02:00
// parameters need to match the indexes of the corresponding
2015-06-26 01:24:41 +02:00
// module names in aliasedModuleNames.
2015-04-17 02:32:40 +02:00
// Fill in amd-dependency tags
2015-11-04 23:02:33 +01:00
for (const amdDependency of node.amdDependencies) {
2015-04-17 02:32:40 +02:00
if (amdDependency.name) {
aliasedModuleNames.push('"' + amdDependency.path + '"');
2015-04-17 02:32:40 +02:00
importAliasNames.push(amdDependency.name);
}
else {
unaliasedModuleNames.push('"' + amdDependency.path + '"');
2015-04-17 02:32:40 +02:00
}
}
2015-04-17 02:32:40 +02:00
2015-11-04 23:02:33 +01:00
for (const importNode of externalImports) {
// Find the name of the external module
const externalModuleName = getExternalModuleNameText(importNode, emitRelativePathAsModuleName);
2015-04-20 22:40:13 +02:00
// Find the name of the module alias, if there is one
2015-11-04 23:02:33 +01:00
const importAliasName = getLocalNameForExternalImport(importNode);
if (includeNonAmdDependencies && importAliasName) {
aliasedModuleNames.push(externalModuleName);
2015-04-17 02:26:06 +02:00
importAliasNames.push(importAliasName);
}
else {
unaliasedModuleNames.push(externalModuleName);
}
}
2015-04-20 22:40:13 +02:00
return { aliasedModuleNames, unaliasedModuleNames, importAliasNames };
}
2015-11-03 01:54:12 +01:00
function emitAMDDependencies(node: SourceFile, includeNonAmdDependencies: boolean, emitRelativePathAsModuleName?: boolean) {
// An AMD define function has the following shape:
// define(id?, dependencies?, factory);
//
// This has the shape of
// define(name, ["module1", "module2"], function (module1Alias) {
// The location of the alias in the parameter list in the factory function needs to
// match the position of the module name in the dependency list.
//
// To ensure this is true in cases of modules with no aliases, e.g.:
// `import "module"` or `<amd-dependency path= "a.css" />`
// we need to add modules without alias names to the end of the dependencies list
const dependencyNames = getAMDDependencyNames(node, includeNonAmdDependencies, emitRelativePathAsModuleName);
emitAMDDependencyList(dependencyNames);
write(", ");
emitAMDFactoryHeader(dependencyNames);
}
function emitAMDDependencyList({ aliasedModuleNames, unaliasedModuleNames }: AMDDependencyNames) {
write('["require", "exports"');
if (aliasedModuleNames.length) {
write(", ");
write(aliasedModuleNames.join(", "));
}
if (unaliasedModuleNames.length) {
write(", ");
write(unaliasedModuleNames.join(", "));
}
write("]");
}
function emitAMDFactoryHeader({ importAliasNames }: AMDDependencyNames) {
write("function (require, exports");
2015-04-17 02:26:06 +02:00
if (importAliasNames.length) {
write(", ");
2015-04-17 02:26:06 +02:00
write(importAliasNames.join(", "));
}
write(") {");
}
2015-11-03 01:54:12 +01:00
function emitAMDModule(node: SourceFile, emitRelativePathAsModuleName?: boolean) {
emitEmitHelpers(node);
collectExternalModuleInfo(node);
writeLine();
write("define(");
2015-11-03 01:54:12 +01:00
writeModuleName(node, emitRelativePathAsModuleName);
emitAMDDependencies(node, /*includeNonAmdDependencies*/ true, emitRelativePathAsModuleName);
increaseIndent();
2016-02-06 01:30:01 +01:00
const startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ true, /*ensureUseStrict*/!compilerOptions.noImplicitUseStrict);
emitExportStarHelper();
emitCaptureThisForNodeIfNecessary(node);
emitLinesStartingAt(node.statements, startIndex);
emitExportEquals(/*emitAsReturn*/ true);
emitTempDeclarations(/*newLine*/ true);
decreaseIndent();
writeLine();
write("});");
}
function emitCommonJSModule(node: SourceFile) {
2016-02-06 01:30:01 +01:00
const startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ false, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict);
emitEmitHelpers(node);
collectExternalModuleInfo(node);
emitExportStarHelper();
emitCaptureThisForNodeIfNecessary(node);
emitLinesStartingAt(node.statements, startIndex);
emitExportEquals(/*emitAsReturn*/ false);
emitTempDeclarations(/*newLine*/ true);
}
function emitUMDModule(node: SourceFile) {
emitEmitHelpers(node);
collectExternalModuleInfo(node);
2015-11-04 23:02:33 +01:00
const dependencyNames = getAMDDependencyNames(node, /*includeNonAmdDependencies*/ false);
// Module is detected first to support Browserify users that load into a browser with an AMD loader
writeLines(`(function (factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
var v = factory(require, exports); if (v !== undefined) module.exports = v;
}
else if (typeof define === 'function' && define.amd) {
define(`);
emitAMDDependencyList(dependencyNames);
write(", factory);");
writeLines(` }
})(`);
emitAMDFactoryHeader(dependencyNames);
increaseIndent();
2016-02-06 01:30:01 +01:00
const startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ true, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict);
emitExportStarHelper();
emitCaptureThisForNodeIfNecessary(node);
emitLinesStartingAt(node.statements, startIndex);
emitExportEquals(/*emitAsReturn*/ true);
emitTempDeclarations(/*newLine*/ true);
decreaseIndent();
writeLine();
write("});");
}
function emitES6Module(node: SourceFile) {
externalImports = undefined;
exportSpecifiers = undefined;
exportEquals = undefined;
hasExportStarsToExportValues = false;
2015-11-04 23:02:33 +01:00
const startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ false);
emitEmitHelpers(node);
emitCaptureThisForNodeIfNecessary(node);
emitLinesStartingAt(node.statements, startIndex);
emitTempDeclarations(/*newLine*/ true);
2015-07-09 23:44:47 +02:00
// Emit exportDefault if it exists will happen as part
2015-03-18 03:25:40 +01:00
// or normal statement emit.
2015-03-17 03:25:02 +01:00
}
function emitExportEquals(emitAsReturn: boolean) {
if (exportEquals && resolver.isValueAliasDeclaration(exportEquals)) {
writeLine();
emitStart(exportEquals);
write(emitAsReturn ? "return " : "module.exports = ");
emit((<ExportAssignment>exportEquals).expression);
write(";");
emitEnd(exportEquals);
}
}
2015-06-22 22:55:09 +02:00
function emitJsxElement(node: JsxElement | JsxSelfClosingElement) {
2015-06-18 23:01:49 +02:00
switch (compilerOptions.jsx) {
case JsxEmit.React:
jsxEmitReact(node);
break;
2015-06-22 22:55:09 +02:00
case JsxEmit.Preserve:
2015-06-18 23:01:49 +02:00
// Fall back to preserve if None was specified (we'll error earlier)
default:
jsxEmitPreserve(node);
break;
}
}
function trimReactWhitespaceAndApplyEntities(node: JsxText): string {
2015-06-26 23:18:51 +02:00
let result: string = undefined;
2015-11-04 23:02:33 +01:00
const text = getTextOfNode(node, /*includeTrivia*/ true);
2015-06-18 23:01:49 +02:00
let firstNonWhitespace = 0;
let lastNonWhitespace = -1;
2015-06-22 22:55:09 +02:00
// JSX trims whitespace at the end and beginning of lines, except that the
// start/end of a tag is considered a start/end of a line only if that line is
// on the same line as the closing tag. See examples in tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx
for (let i = 0; i < text.length; i++) {
2015-11-04 23:02:33 +01:00
const c = text.charCodeAt(i);
2015-06-26 23:18:51 +02:00
if (isLineBreak(c)) {
2015-06-18 23:01:49 +02:00
if (firstNonWhitespace !== -1 && (lastNonWhitespace - firstNonWhitespace + 1 > 0)) {
2015-11-04 23:02:33 +01:00
const part = text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1);
result = (result ? result + `" + ' ' + "` : "") + escapeString(part);
2015-06-18 23:01:49 +02:00
}
firstNonWhitespace = -1;
}
2016-07-01 22:57:20 +02:00
else if (!isWhiteSpaceSingleLine(c)) {
2015-06-18 23:01:49 +02:00
lastNonWhitespace = i;
if (firstNonWhitespace === -1) {
firstNonWhitespace = i;
}
}
}
2015-06-18 23:01:49 +02:00
if (firstNonWhitespace !== -1) {
2015-11-04 23:02:33 +01:00
const part = text.substr(firstNonWhitespace);
2015-10-08 20:28:18 +02:00
result = (result ? result + "\" + ' ' + \"" : "") + escapeString(part);
2015-06-18 23:01:49 +02:00
}
if (result) {
// Replace entities like &nbsp;
result = result.replace(/&(\w+);/g, function(s: any, m: string) {
2015-08-20 21:30:12 +02:00
if (entities[m] !== undefined) {
2015-12-29 22:16:00 +01:00
const ch = String.fromCharCode(entities[m]);
// &quot; needs to be escaped
return ch === '"' ? "\\\"" : ch;
2015-08-20 21:30:12 +02:00
}
else {
return s;
}
});
}
2015-06-26 23:18:51 +02:00
return result;
2015-06-18 23:01:49 +02:00
}
2016-01-27 07:59:34 +01:00
function isJsxChildEmittable(child: JsxChild): boolean {
if (child.kind === SyntaxKind.JsxExpression) {
// Don't emit empty expressions
return !!(<JsxExpression>child).expression;
}
else if (child.kind === SyntaxKind.JsxText) {
// Don't emit empty strings
return !!getTextToEmit(<JsxText>child);
}
return true;
};
function getTextToEmit(node: JsxText): string {
2015-06-22 22:55:09 +02:00
switch (compilerOptions.jsx) {
case JsxEmit.React:
let text = trimReactWhitespaceAndApplyEntities(node);
2015-08-20 20:38:03 +02:00
if (text === undefined || text.length === 0) {
2015-06-26 23:18:51 +02:00
return undefined;
}
else {
return text;
}
2015-06-22 22:55:09 +02:00
case JsxEmit.Preserve:
default:
2015-08-20 21:23:27 +02:00
return getTextOfNode(node, /*includeTrivia*/ true);
2015-06-18 23:01:49 +02:00
}
}
function emitJsxText(node: JsxText) {
switch (compilerOptions.jsx) {
case JsxEmit.React:
write('"');
write(trimReactWhitespaceAndApplyEntities(node));
write('"');
2015-06-18 23:01:49 +02:00
break;
case JsxEmit.Preserve:
default: // Emit JSX-preserve as default when no --jsx flag is specified
2015-08-20 21:23:27 +02:00
writer.writeLiteral(getTextOfNode(node, /*includeTrivia*/ true));
2015-06-18 23:01:49 +02:00
break;
}
}
function emitJsxExpression(node: JsxExpression) {
2015-06-22 22:55:09 +02:00
if (node.expression) {
2015-06-18 23:01:49 +02:00
switch (compilerOptions.jsx) {
case JsxEmit.Preserve:
default:
write("{");
2015-06-18 23:01:49 +02:00
emit(node.expression);
write("}");
2015-06-18 23:01:49 +02:00
break;
case JsxEmit.React:
emit(node.expression);
break;
}
}
}
function isUseStrictPrologue(node: ExpressionStatement): boolean {
return (node.expression as StringLiteral).text === "use strict";
}
function ensureUseStrictPrologue(startWithNewLine: boolean, writeUseStrict: boolean) {
if (writeUseStrict) {
if (startWithNewLine) {
writeLine();
}
write("\"use strict\";");
}
}
function emitDirectivePrologues(statements: Node[], startWithNewLine: boolean, ensureUseStrict?: boolean): number {
let foundUseStrict = false;
2015-12-23 00:45:00 +01:00
for (let i = 0; i < statements.length; i++) {
if (isPrologueDirective(statements[i])) {
if (isUseStrictPrologue(statements[i] as ExpressionStatement)) {
foundUseStrict = true;
}
if (startWithNewLine || i > 0) {
writeLine();
}
emit(statements[i]);
}
else {
ensureUseStrictPrologue(startWithNewLine || i > 0, !foundUseStrict && ensureUseStrict);
// return index of the first non prologue directive
return i;
}
}
ensureUseStrictPrologue(startWithNewLine, !foundUseStrict && ensureUseStrict);
return statements.length;
2014-07-12 01:36:06 +02:00
}
function writeLines(text: string): void {
2015-11-04 23:02:33 +01:00
const lines = text.split(/\r\n|\r|\n/g);
2015-12-23 00:45:00 +01:00
for (let i = 0; i < lines.length; i++) {
2015-11-04 23:02:33 +01:00
const line = lines[i];
if (line.length) {
writeLine();
write(line);
}
}
}
function emitEmitHelpers(node: SourceFile): void {
// Only emit helpers if the user did not say otherwise.
if (!compilerOptions.noEmitHelpers) {
// Only Emit __extends function when target ES5.
// For target ES6 and above, we can emit classDeclaration as is.
if (languageVersion < ScriptTarget.ES6 && !extendsEmitted && node.flags & NodeFlags.HasClassExtends) {
writeLines(extendsHelper);
extendsEmitted = true;
}
if (compilerOptions.jsx !== JsxEmit.Preserve && !assignEmitted && (node.flags & NodeFlags.HasJsxSpreadAttribute)) {
writeLines(assignHelper);
assignEmitted = true;
}
if (!decorateEmitted && node.flags & NodeFlags.HasDecorators) {
writeLines(decorateHelper);
if (compilerOptions.emitDecoratorMetadata) {
writeLines(metadataHelper);
}
decorateEmitted = true;
}
if (!paramEmitted && node.flags & NodeFlags.HasParamDecorators) {
writeLines(paramHelper);
paramEmitted = true;
}
2015-07-09 23:44:47 +02:00
if (!awaiterEmitted && node.flags & NodeFlags.HasAsyncFunctions) {
2015-05-07 02:33:58 +02:00
writeLines(awaiterHelper);
awaiterEmitted = true;
}
}
}
2015-09-17 22:26:04 +02:00
function emitSourceFileNode(node: SourceFile) {
// Start new file on new line
writeLine();
2015-08-02 04:24:18 +02:00
emitShebang();
emitDetachedCommentsAndUpdateCommentsInfo(node);
if (isExternalModule(node) || compilerOptions.isolatedModules) {
if (isOwnFileEmit || (!isExternalModule(node) && compilerOptions.isolatedModules)) {
const emitModule = moduleEmitDelegates[modulekind] || moduleEmitDelegates[ModuleKind.CommonJS];
2015-10-05 22:25:40 +02:00
emitModule(node);
2015-10-02 04:23:12 +02:00
}
else {
2015-11-03 01:54:12 +01:00
bundleEmitDelegates[modulekind](node, /*emitRelativePathAsModuleName*/true);
2015-10-02 04:23:12 +02:00
}
}
else {
// emit prologue directives prior to __extends
2015-11-04 23:02:33 +01:00
const startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ false);
externalImports = undefined;
exportSpecifiers = undefined;
exportEquals = undefined;
hasExportStarsToExportValues = false;
emitEmitHelpers(node);
emitCaptureThisForNodeIfNecessary(node);
emitLinesStartingAt(node.statements, startIndex);
emitTempDeclarations(/*newLine*/ true);
}
2014-07-13 01:04:16 +02:00
emitLeadingComments(node.endOfFileToken);
2014-07-13 01:04:16 +02:00
}
function emit(node: Node): void {
emitNodeConsideringCommentsOption(node, emitNodeWithSourceMap);
}
function emitNodeWithCommentsAndWithoutSourcemap(node: Node): void {
emitNodeConsideringCommentsOption(node, emitNodeWithoutSourceMap);
}
function emitNodeConsideringCommentsOption(node: Node, emitNodeConsideringSourcemap: (node: Node) => void): void {
if (node) {
if (node.flags & NodeFlags.Ambient) {
return emitCommentsOnNotEmittedNode(node);
}
2015-01-23 00:58:00 +01:00
if (isSpecializedCommentHandling(node)) {
// This is the node that will handle its own comments and sourcemap
return emitNodeWithoutSourceMap(node);
}
2015-01-23 00:58:00 +01:00
2015-11-04 23:02:33 +01:00
const emitComments = shouldEmitLeadingAndTrailingComments(node);
if (emitComments) {
emitLeadingComments(node);
}
2015-01-23 00:58:00 +01:00
emitNodeConsideringSourcemap(node);
2015-01-23 00:58:00 +01:00
if (emitComments) {
emitTrailingComments(node);
}
2015-01-23 00:58:00 +01:00
}
}
function emitNodeWithSourceMap(node: Node): void {
if (node) {
emitStart(node);
emitNodeWithoutSourceMap(node);
emitEnd(node);
}
}
function emitNodeWithoutSourceMap(node: Node): void {
if (node) {
emitJavaScriptWorker(node);
}
}
2015-12-03 23:20:25 +01:00
function changeSourceMapEmit(writer: SourceMapWriter) {
sourceMap = writer;
emitStart = writer.emitStart;
emitEnd = writer.emitEnd;
emitPos = writer.emitPos;
setSourceFile = writer.setSourceFile;
}
function withTemporaryNoSourceMap(callback: () => void) {
const prevSourceMap = sourceMap;
setSourceMapWriterEmit(getNullSourceMapWriter());
callback();
setSourceMapWriterEmit(prevSourceMap);
}
function isSpecializedCommentHandling(node: Node): boolean {
2015-01-23 00:58:00 +01:00
switch (node.kind) {
2015-01-28 02:16:28 +01:00
// All of these entities are emitted in a specialized fashion. As such, we allow
2015-03-18 03:25:40 +01:00
// the specialized methods for each to handle the comments on the nodes.
2015-01-23 00:58:00 +01:00
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
2015-01-23 00:58:00 +01:00
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.ExportAssignment:
return true;
}
}
2015-01-23 00:58:00 +01:00
function shouldEmitLeadingAndTrailingComments(node: Node) {
switch (node.kind) {
case SyntaxKind.VariableStatement:
return shouldEmitLeadingAndTrailingCommentsForVariableStatement(<VariableStatement>node);
2015-01-23 00:58:00 +01:00
case SyntaxKind.ModuleDeclaration:
// Only emit the leading/trailing comments for a module if we're actually
2015-01-23 00:58:00 +01:00
// emitting the module as well.
2015-01-28 02:09:57 +01:00
return shouldEmitModuleDeclaration(<ModuleDeclaration>node);
2015-01-23 00:58:00 +01:00
case SyntaxKind.EnumDeclaration:
// Only emit the leading/trailing comments for an enum if we're actually
2015-01-23 00:58:00 +01:00
// emitting the module as well.
return shouldEmitEnumDeclaration(<EnumDeclaration>node);
}
2015-10-06 21:37:10 +02:00
// If the node is emitted in specialized fashion, dont emit comments as this node will handle
// emitting comments when emitting itself
Debug.assert(!isSpecializedCommentHandling(node));
2015-07-09 23:44:47 +02:00
// If this is the expression body of an arrow function that we're down-leveling,
2015-03-18 03:13:00 +01:00
// then we don't want to emit comments when we emit the body. It will have already
// been taken care of when we emitted the 'return' statement for the function
// expression body.
if (node.kind !== SyntaxKind.Block &&
node.parent &&
node.parent.kind === SyntaxKind.ArrowFunction &&
2015-06-18 23:01:49 +02:00
(<ArrowFunction>node.parent).body === node &&
2016-04-19 14:50:50 +02:00
languageVersion <= ScriptTarget.ES5) {
return false;
}
2015-01-23 00:58:00 +01:00
// Emit comments for everything else.
return true;
}
function emitJavaScriptWorker(node: Node) {
2014-12-02 22:29:49 +01:00
// Check if the node can be emitted regardless of the ScriptTarget
switch (node.kind) {
case SyntaxKind.Identifier:
return emitIdentifier(<Identifier>node);
case SyntaxKind.Parameter:
return emitParameter(<ParameterDeclaration>node);
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return emitMethod(<MethodDeclaration>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:
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.TemplateHead:
case SyntaxKind.TemplateMiddle:
case SyntaxKind.TemplateTail:
return emitLiteral(<LiteralExpression>node);
case SyntaxKind.TemplateExpression:
return emitTemplateExpression(<TemplateExpression>node);
case SyntaxKind.TemplateSpan:
return emitTemplateSpan(<TemplateSpan>node);
2015-06-18 23:01:49 +02:00
case SyntaxKind.JsxElement:
case SyntaxKind.JsxSelfClosingElement:
return emitJsxElement(<JsxElement | JsxSelfClosingElement>node);
2015-06-18 23:01:49 +02:00
case SyntaxKind.JsxText:
return emitJsxText(<JsxText>node);
case SyntaxKind.JsxExpression:
return emitJsxExpression(<JsxExpression>node);
case SyntaxKind.QualifiedName:
return emitQualifiedName(<QualifiedName>node);
case SyntaxKind.ObjectBindingPattern:
return emitObjectBindingPattern(<BindingPattern>node);
case SyntaxKind.ArrayBindingPattern:
return emitArrayBindingPattern(<BindingPattern>node);
case SyntaxKind.BindingElement:
return emitBindingElement(<BindingElement>node);
2014-11-30 00:58:55 +01:00
case SyntaxKind.ArrayLiteralExpression:
return emitArrayLiteral(<ArrayLiteralExpression>node);
case SyntaxKind.ObjectLiteralExpression:
return emitObjectLiteral(<ObjectLiteralExpression>node);
case SyntaxKind.PropertyAssignment:
return emitPropertyAssignment(<PropertyDeclaration>node);
case SyntaxKind.ShorthandPropertyAssignment:
return emitShorthandPropertyAssignment(<ShorthandPropertyAssignment>node);
2014-11-18 00:53:03 +01:00
case SyntaxKind.ComputedPropertyName:
return emitComputedPropertyName(<ComputedPropertyName>node);
2014-11-30 00:58:55 +01:00
case SyntaxKind.PropertyAccessExpression:
return emitPropertyAccess(<PropertyAccessExpression>node);
case SyntaxKind.ElementAccessExpression:
return emitIndexedAccess(<ElementAccessExpression>node);
case SyntaxKind.CallExpression:
return emitCallExpression(<CallExpression>node);
case SyntaxKind.NewExpression:
return emitNewExpression(<NewExpression>node);
case SyntaxKind.TaggedTemplateExpression:
return emitTaggedTemplateExpression(<TaggedTemplateExpression>node);
2014-11-30 00:58:55 +01:00
case SyntaxKind.TypeAssertionExpression:
2015-06-18 23:01:49 +02:00
case SyntaxKind.AsExpression:
case SyntaxKind.NonNullExpression:
return emit((<AssertionExpression | NonNullExpression>node).expression);
2014-11-30 00:58:55 +01:00
case SyntaxKind.ParenthesizedExpression:
return emitParenExpression(<ParenthesizedExpression>node);
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return emitFunctionDeclaration(<FunctionLikeDeclaration>node);
case SyntaxKind.DeleteExpression:
return emitDeleteExpression(<DeleteExpression>node);
case SyntaxKind.TypeOfExpression:
return emitTypeOfExpression(<TypeOfExpression>node);
case SyntaxKind.VoidExpression:
return emitVoidExpression(<VoidExpression>node);
2015-05-07 02:33:58 +02:00
case SyntaxKind.AwaitExpression:
return emitAwaitExpression(<AwaitExpression>node);
case SyntaxKind.PrefixUnaryExpression:
return emitPrefixUnaryExpression(<PrefixUnaryExpression>node);
2014-11-30 00:58:55 +01:00
case SyntaxKind.PostfixUnaryExpression:
return emitPostfixUnaryExpression(<PostfixUnaryExpression>node);
case SyntaxKind.BinaryExpression:
return emitBinaryExpression(<BinaryExpression>node);
case SyntaxKind.ConditionalExpression:
return emitConditionalExpression(<ConditionalExpression>node);
case SyntaxKind.SpreadElementExpression:
return emitSpreadElementExpression(<SpreadElementExpression>node);
case SyntaxKind.YieldExpression:
return emitYieldExpression(<YieldExpression>node);
case SyntaxKind.OmittedExpression:
return;
case SyntaxKind.Block:
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.ForOfStatement:
case SyntaxKind.ForInStatement:
return emitForInOrForOfStatement(<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.LabeledStatement:
2015-10-20 00:51:20 +02:00
return emitLabeledStatement(<LabeledStatement>node);
case SyntaxKind.ThrowStatement:
return emitThrowStatement(<ThrowStatement>node);
case SyntaxKind.TryStatement:
return emitTryStatement(<TryStatement>node);
case SyntaxKind.CatchClause:
return emitCatchClause(<CatchClause>node);
case SyntaxKind.DebuggerStatement:
return emitDebuggerStatement(node);
case SyntaxKind.VariableDeclaration:
return emitVariableDeclaration(<VariableDeclaration>node);
case SyntaxKind.ClassExpression:
return emitClassExpression(<ClassExpression>node);
case SyntaxKind.ClassDeclaration:
return emitClassDeclaration(<ClassDeclaration>node);
case SyntaxKind.InterfaceDeclaration:
return emitInterfaceDeclaration(<InterfaceDeclaration>node);
case SyntaxKind.EnumDeclaration:
return emitEnumDeclaration(<EnumDeclaration>node);
2015-01-22 23:45:55 +01:00
case SyntaxKind.EnumMember:
return emitEnumMember(<EnumMember>node);
case SyntaxKind.ModuleDeclaration:
return emitModuleDeclaration(<ModuleDeclaration>node);
case SyntaxKind.ImportDeclaration:
return emitImportDeclaration(<ImportDeclaration>node);
case SyntaxKind.ImportEqualsDeclaration:
return emitImportEqualsDeclaration(<ImportEqualsDeclaration>node);
case SyntaxKind.ExportDeclaration:
return emitExportDeclaration(<ExportDeclaration>node);
case SyntaxKind.ExportAssignment:
return emitExportAssignment(<ExportAssignment>node);
case SyntaxKind.SourceFile:
2015-03-09 21:32:02 +01:00
return emitSourceFileNode(<SourceFile>node);
2014-08-07 03:42:14 +02:00
}
}
function hasDetachedComments(pos: number) {
return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos;
2014-07-13 01:04:16 +02:00
}
function getLeadingCommentsWithoutDetachedComments() {
// get the leading comments from detachedPos
const leadingComments = getLeadingCommentRanges(currentText,
lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos);
if (detachedCommentsInfo.length - 1) {
detachedCommentsInfo.pop();
}
else {
detachedCommentsInfo = undefined;
}
2014-07-13 01:04:16 +02:00
return leadingComments;
}
2014-07-13 01:04:16 +02:00
2015-07-17 19:33:07 +02:00
/**
* Determine if the given comment is a triple-slash
*
* @return true if the comment is a triple-slash comment else false
**/
2015-09-10 22:05:51 +02:00
function isTripleSlashComment(comment: CommentRange) {
// Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
// so that we don't end up computing comment string and doing match for all // comments
if (currentText.charCodeAt(comment.pos + 1) === CharacterCodes.slash &&
comment.pos + 2 < comment.end &&
currentText.charCodeAt(comment.pos + 2) === CharacterCodes.slash) {
const textSubStr = currentText.substring(comment.pos, comment.end);
return textSubStr.match(fullTripleSlashReferencePathRegEx) ||
textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ?
true : false;
2015-03-18 03:25:40 +01:00
}
return false;
2015-03-18 03:25:40 +01:00
}
function getLeadingCommentsToEmit(node: Node) {
// Emit the leading comments only if the parent's pos doesn't match because parent should take care of emitting these comments
2015-01-23 00:58:00 +01:00
if (node.parent) {
if (node.parent.kind === SyntaxKind.SourceFile || node.pos !== node.parent.pos) {
if (hasDetachedComments(node.pos)) {
// get comments without detached comments
2015-03-18 03:25:40 +01:00
return getLeadingCommentsWithoutDetachedComments();
2015-01-23 00:58:00 +01:00
}
else {
// get the leading comments from the node
return getLeadingCommentRangesOfNodeFromText(node, currentText);
2015-01-23 00:58:00 +01:00
}
}
}
}
2014-08-07 02:58:03 +02:00
2015-03-18 03:25:40 +01:00
function getTrailingCommentsToEmit(node: Node) {
// Emit the trailing comments only if the parent's pos doesn't match because parent should take care of emitting these comments
if (node.parent) {
if (node.parent.kind === SyntaxKind.SourceFile || node.end !== node.parent.end) {
return getTrailingCommentRanges(currentText, node.end);
}
}
}
2015-08-21 01:29:55 +02:00
/**
* Emit comments associated with node that will not be emitted into JS file
*/
function emitCommentsOnNotEmittedNode(node: Node) {
2015-11-14 02:43:53 +01:00
emitLeadingCommentsWorker(node, /*isEmittedNode*/ false);
}
function emitLeadingComments(node: Node) {
2015-11-14 02:43:53 +01:00
return emitLeadingCommentsWorker(node, /*isEmittedNode*/ true);
}
function emitLeadingCommentsWorker(node: Node, isEmittedNode: boolean) {
2015-09-01 05:33:02 +02:00
if (compilerOptions.removeComments) {
return;
}
let leadingComments: CommentRange[];
if (isEmittedNode) {
leadingComments = getLeadingCommentsToEmit(node);
}
else {
// If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
// unless it is a triple slash comment at the top of the file.
// For Example:
// /// <reference-path ...>
// declare var x;
// /// <reference-path ...>
// interface F {}
// The first /// will NOT be removed while the second one will be removed even though both node will not be emitted
2015-09-01 05:33:02 +02:00
if (node.pos === 0) {
2015-09-10 22:05:51 +02:00
leadingComments = filter(getLeadingCommentsToEmit(node), isTripleSlashComment);
2015-09-01 05:33:02 +02:00
}
}
emitNewLineBeforeLeadingComments(currentLineMap, writer, node, leadingComments);
// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
2015-11-14 02:43:53 +01:00
emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment);
2014-07-13 01:04:16 +02:00
}
2014-08-07 02:58:03 +02:00
function emitTrailingComments(node: Node) {
2015-09-01 05:33:02 +02:00
if (compilerOptions.removeComments) {
return;
}
// Emit the trailing comments only if the parent's end doesn't match
2015-11-04 23:02:33 +01:00
const trailingComments = getTrailingCommentsToEmit(node);
2015-03-18 03:25:40 +01:00
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment);
2014-07-13 01:04:16 +02:00
}
/**
* Emit trailing comments at the position. The term trailing comment is used here to describe following comment:
2015-08-13 02:52:36 +02:00
* x, /comment1/ y
* ^ => pos; the function will emit "comment1" in the emitJS
*/
function emitTrailingCommentsOfPosition(pos: number) {
2015-09-01 05:33:02 +02:00
if (compilerOptions.removeComments) {
return;
}
const trailingComments = getTrailingCommentRanges(currentText, pos);
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ true, newLine, writeComment);
}
2015-09-10 22:05:51 +02:00
function emitLeadingCommentsOfPositionWorker(pos: number) {
2015-09-01 05:33:02 +02:00
if (compilerOptions.removeComments) {
return;
}
2015-03-13 20:34:12 +01:00
let leadingComments: CommentRange[];
if (hasDetachedComments(pos)) {
// get comments without detached comments
leadingComments = getLeadingCommentsWithoutDetachedComments();
}
else {
// get the leading comments from the node
leadingComments = getLeadingCommentRanges(currentText, pos);
}
emitNewLineBeforeLeadingComments(currentLineMap, writer, { pos: pos, end: pos }, leadingComments);
// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment);
}
2014-07-13 01:04:16 +02:00
function emitDetachedCommentsAndUpdateCommentsInfo(node: TextRange) {
const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, node, newLine, compilerOptions.removeComments);
if (currentDetachedCommentInfo) {
if (detachedCommentsInfo) {
detachedCommentsInfo.push(currentDetachedCommentInfo);
}
else {
detachedCommentsInfo = [currentDetachedCommentInfo];
}
}
2014-07-13 01:04:16 +02:00
}
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) {
emitPos(comment.pos);
writeCommentRange(text, lineMap, writer, comment, newLine);
emitPos(comment.end);
}
2015-08-02 04:24:18 +02:00
function emitShebang() {
const shebang = getShebang(currentText);
2015-08-02 04:24:18 +02:00
if (shebang) {
write(shebang);
2015-12-04 22:38:24 +01:00
writeLine();
2015-08-02 04:24:18 +02:00
}
}
2014-07-13 01:04:16 +02:00
}
function emitFile({ jsFilePath, sourceMapFilePath, declarationFilePath }: EmitFileNames,
sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean) {
if (!emitOnlyDtsFiles) {
// Make sure not to write js File and source map file if any of them cannot be written
if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) {
emitJavaScript(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit);
}
else {
emitSkipped = true;
}
}
if (declarationFilePath) {
emitSkipped = writeDeclarationFile(declarationFilePath, sourceFiles, isBundledEmit, host, resolver, emitterDiagnostics, emitOnlyDtsFiles) || emitSkipped;
}
if (!emitSkipped && emittedFilesList) {
if (!emitOnlyDtsFiles) {
emittedFilesList.push(jsFilePath);
if (sourceMapFilePath) {
emittedFilesList.push(sourceMapFilePath);
}
}
if (declarationFilePath) {
emittedFilesList.push(declarationFilePath);
}
}
}
2014-07-13 01:04:16 +02:00
}
}