2014-12-16 22:14:14 +01:00
/// <reference path="checker.ts"/>
2015-11-24 07:38:05 +01:00
/// <reference path="sourcemap.ts" />
2015-03-19 00:37:52 +01:00
/// <reference path="declarationEmitter.ts"/>
2014-07-13 01:04:16 +02:00
2015-04-09 23:18:14 +02:00
/* @internal */
2015-06-12 18:01:48 +02:00
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 . module Name || 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 ) ;
}
2015-10-01 00:45:23 +02:00
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
}
2015-11-04 23:02:33 +01:00
const entities : Map < number > = {
2015-10-01 00:45:23 +02:00
"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
} ;
2015-03-26 18:51:07 +01: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
}
2015-02-06 03:26:56 +01:00
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
2015-02-06 02:11:06 +01:00
export function emitFiles ( resolver : EmitResolver , host : EmitHost , targetSourceFile : SourceFile ) : EmitResult {
2015-04-02 02:58:28 +02:00
// 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 ) {
2015-04-02 02:58:28 +02:00
for ( var p in b ) if ( b . hasOwnProperty ( p ) ) d [ p ] = b [ p ] ;
function __() { this . constructor = d ; }
2015-06-16 21:13:18 +02:00
d . prototype = b === null ? Object . create ( b ) : ( __ . prototype = b . prototype , new __ ( ) ) ;
2015-04-02 02:58:28 +02:00
} ; ` ;
// 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 ;
2015-04-02 02:58:28 +02:00
} ; ` ;
// 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 ) {
2015-04-21 23:10:37 +02:00
if ( typeof Reflect === "object" && typeof Reflect . metadata === "function" ) return Reflect . metadata ( k , v ) ;
} ; ` ;
2015-04-02 02:58:28 +02:00
// 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-04-02 02:58:28 +02:00
2015-05-07 02:33:58 +02:00
const awaiterHelper = `
2015-06-20 01:01:12 +02:00
var __awaiter = ( this && this . __awaiter ) || function ( thisArg , _arguments , Promise , generator ) {
return new Promise ( function ( resolve , reject ) {
generator = generator . call ( thisArg , _arguments ) ;
function cast ( value ) { return value instanceof Promise && value . constructor === Promise ? value : new Promise ( function ( resolve ) { resolve ( value ) ; } ) ; }
2015-06-04 21:08:09 +02:00
function onfulfill ( value ) { try { step ( "next" , value ) ; } catch ( e ) { reject ( e ) ; } }
function onreject ( value ) { try { step ( "throw" , value ) ; } catch ( e ) { reject ( e ) ; } }
function step ( verb , value ) {
var result = generator [ verb ] ( value ) ;
result . done ? resolve ( result . value ) : cast ( result . value ) . then ( onfulfill , onreject ) ;
2015-05-07 02:33:58 +02:00
}
2015-06-04 21:08:09 +02:00
step ( "next" , void 0 ) ;
} ) ;
} ; ` ;
2015-06-18 23:01:49 +02:00
2015-11-04 23:02:33 +01:00
const compilerOptions = host . getCompilerOptions ( ) ;
2015-11-13 22:44:57 +01:00
const languageVersion = getEmitScriptTarget ( compilerOptions ) ;
const module kind = getEmitModuleKind ( compilerOptions ) ;
2015-11-04 23:02:33 +01:00
const sourceMapDataList : SourceMapData [ ] = compilerOptions . sourceMap || compilerOptions . inlineSourceMap ? [ ] : undefined ;
2015-11-06 21:39:42 +01:00
const emitterDiagnostics = createDiagnosticCollection ( ) ;
2015-10-21 00:51:37 +02:00
let emitSkipped = false ;
2015-11-04 23:02:33 +01:00
const newLine = host . getNewLine ( ) ;
2014-11-13 20:33:31 +01:00
2015-11-07 01:21:43 +01:00
const emitJavaScript = createFileEmitter ( ) ;
2015-10-30 20:54:30 +01:00
forEachExpectedEmitFile ( host , emitFile , targetSourceFile ) ;
2015-02-05 23:46:50 +01:00
return {
2015-10-21 00:51:37 +02:00
emitSkipped ,
2015-10-30 21:43:52 +01:00
diagnostics : emitterDiagnostics.getDiagnostics ( ) ,
2015-02-05 23:46:50 +01:00
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 ) {
if ( node . locals && hasProperty ( node . locals , name ) ) {
// 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 ;
}
2015-10-11 10:12:26 +02:00
interface ConvertedLoopState {
2015-10-19 23:10:27 +02:00
/ *
* set of labels that occured inside the converted loop
* used to determine if labeled jump can be emitted as is or it should be dispatched to calling code
* /
2015-10-11 10:12:26 +02:00
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 apprear in code
* - label marker - return value that should be interpreted by calling code as 'jump to <label>'
2015-10-19 23:21:53 +02:00
* /
2015-10-11 10:12:26 +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
* /
2015-10-11 10:12:26 +02:00
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
* /
2015-10-11 10:12:26 +02:00
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 witg ' arguments_1 ' > }
* var arguments_1 = arguments
* for ( var x ; ; ) loop ( x ) ;
2015-11-17 19:53:29 +01:00
* 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 .
* /
2015-10-11 10:12:26 +02:00
argumentsName? : string ;
2015-11-22 05:11:39 +01:00
/ *
2015-11-23 06:28:07 +01:00
* alias for 'this' from the calling code stack frame in case if this was used inside the converted loop
2015-11-22 05:11:39 +01:00
* /
thisName? : string ;
2015-10-19 23:10:27 +02:00
/ *
* list of non - block scoped variable declarations that appear inside converted loop
2015-11-17 19:53:29 +01:00
* 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-11-17 19:53:29 +01:00
* . . .
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 ) ;
* /
2015-10-11 10:12:26 +02:00
hoistedLocalVariables? : Identifier [ ] ;
}
function setLabeledJump ( state : ConvertedLoopState , isBreak : boolean , labelText : string , labelMarker : string ) : void {
if ( isBreak ) {
if ( ! state . labeledNonLocalBreaks ) {
state . labeledNonLocalBreaks = { } ;
}
state . labeledNonLocalBreaks [ labelText ] = labelMarker ;
}
else {
if ( ! state . labeledNonLocalContinues ) {
state . labeledNonLocalContinues = { } ;
}
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 ) ;
}
}
}
}
2015-11-13 22:44:57 +01:00
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 ;
2014-11-04 23:43:43 +01:00
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-11-24 07:38:05 +01:00
2015-03-13 20:34:12 +01:00
let currentSourceFile : SourceFile ;
2015-11-07 01:21:43 +01:00
let currentText : string ;
let currentLineMap : number [ ] ;
let currentFileIdentifiers : Map < string > ;
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 ;
2014-11-02 02:16:48 +01:00
2015-11-07 01:21:43 +01:00
let generatedNameSet : Map < string > ;
let nodeToGeneratedName : string [ ] ;
2015-03-25 03:57:21 +01:00
let computedPropertyNamesToGeneratedNames : string [ ] ;
2015-02-14 09:48:46 +01:00
2015-10-11 10:12:26 +02:00
let convertedLoopState : ConvertedLoopState ;
2015-11-07 01:21:43 +01:00
let extendsEmitted : boolean ;
let decorateEmitted : boolean ;
let paramEmitted : boolean ;
let awaiterEmitted : boolean ;
2015-11-17 08:24:25 +01:00
let tempFlags : TempFlags = 0 ;
2015-03-13 20:34:12 +01:00
let tempVariables : Identifier [ ] ;
let tempParameters : Identifier [ ] ;
2015-03-21 23:09:54 +01:00
let externalImports : ( ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration ) [ ] ;
2015-03-13 20:34:12 +01:00
let exportSpecifiers : Map < ExportSpecifier [ ] > ;
2015-03-21 21:12:39 +01:00
let exportEquals : ExportAssignment ;
2015-10-05 07:00:57 +02:00
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 } [ ] ;
2015-03-09 23:47:23 +01:00
2014-10-11 01:44:14 +02:00
/** Sourcemap data that will get encoded */
2015-03-13 20:34:12 +01:00
let sourceMapData : SourceMapData ;
2014-07-17 00:39:14 +02:00
2015-11-13 22:44:57 +01:00
/** Is the file being emitted into its own file */
let isOwnFileEmit : boolean ;
2015-11-09 21:38:23 +01:00
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-10-01 00:45:23 +02:00
2015-12-03 23:20:25 +01:00
const setSourceMapWriterEmit = compilerOptions . sourceMap || compilerOptions . inlineSourceMap ? changeSourceMapEmit : function ( writer : SourceMapWriter ) { } ;
2015-11-09 21:38:23 +01:00
const module EmitDelegates : Map < ( node : SourceFile , emitRelativePathAsModuleName? : boolean ) = > void > = {
2015-09-16 00:36:37 +02:00
[ ModuleKind . ES6 ] : emitES6Module ,
[ ModuleKind . AMD ] : emitAMDModule ,
[ ModuleKind . System ] : emitSystemModule ,
[ ModuleKind . UMD ] : emitUMDModule ,
[ ModuleKind . CommonJS ] : emitCommonJSModule ,
} ;
2015-09-10 22:05:51 +02:00
2015-11-09 21:38:23 +01:00
const bundleEmitDelegates : Map < ( node : SourceFile , emitRelativePathAsModuleName? : boolean ) = > void > = {
2015-10-03 02:43:58 +02:00
[ ModuleKind . ES6 ] ( ) { } ,
2015-10-01 21:44:24 +02:00
[ ModuleKind . AMD ] : emitAMDModule ,
[ ModuleKind . System ] : emitSystemModule ,
2015-10-03 02:43:58 +02:00
[ ModuleKind . UMD ] ( ) { } ,
[ ModuleKind . CommonJS ] ( ) { } ,
2015-10-01 21:44:24 +02:00
} ;
2015-11-07 01:21:43 +01:00
return doEmit ;
2015-02-05 23:41:04 +01:00
2015-11-13 22:44:57 +01:00
function doEmit ( jsFilePath : string , sourceMapFilePath : string , sourceFiles : SourceFile [ ] , isBundledEmit : boolean ) {
2015-11-24 07:38:05 +01:00
sourceMap . initialize ( jsFilePath , sourceMapFilePath , sourceFiles , isBundledEmit ) ;
2015-11-07 01:21:43 +01:00
generatedNameSet = { } ;
nodeToGeneratedName = [ ] ;
2015-11-18 20:26:58 +01:00
isOwnFileEmit = ! isBundledEmit ;
2015-02-05 23:41:04 +01:00
2015-11-18 20:26:58 +01:00
// Emit helpers from all the files
if ( isBundledEmit && module kind ) {
forEach ( sourceFiles , emitEmitHelpers ) ;
2015-11-07 01:21:43 +01:00
}
2015-11-09 21:50:38 +01:00
2015-11-18 20:26:58 +01:00
// Do not call emit directly. It does not set the currentSourceFile.
forEach ( sourceFiles , emitSourceFile ) ;
2015-02-05 23:41:04 +01:00
2015-11-07 01:21:43 +01:00
writeLine ( ) ;
2015-11-24 07:38:05 +01:00
const sourceMappingURL = sourceMap . getSourceMappingURL ( ) ;
if ( sourceMappingURL ) {
write ( ` //# sourceMappingURL= ${ sourceMappingURL } ` ) ;
}
writeEmittedFiles ( writer . getText ( ) , jsFilePath , sourceMapFilePath , /*writeByteOrderMark*/ compilerOptions . emitBOM ) ;
2015-11-17 08:24:25 +01:00
2015-11-07 01:21:43 +01:00
// reset the state
2015-11-24 07:38:05 +01:00
sourceMap . reset ( ) ;
2015-11-07 01:21:43 +01:00
writer . reset ( ) ;
currentSourceFile = undefined ;
currentText = undefined ;
currentLineMap = undefined ;
exportFunctionForFile = undefined ;
2015-11-17 08:24:25 +01:00
generatedNameSet = undefined ;
nodeToGeneratedName = undefined ;
2015-11-07 01:21:43 +01:00
computedPropertyNamesToGeneratedNames = undefined ;
convertedLoopState = undefined ;
extendsEmitted = false ;
decorateEmitted = false ;
paramEmitted = false ;
awaiterEmitted = false ;
tempFlags = 0 ;
tempVariables = undefined ;
tempParameters = undefined ;
externalImports = undefined ;
exportSpecifiers = undefined ;
exportEquals = undefined ;
2015-11-09 19:46:50 +01:00
hasExportStarsToExportValues = undefined ;
2015-11-07 01:21:43 +01:00
detachedCommentsInfo = undefined ;
sourceMapData = undefined ;
isEs6Module = false ;
renamedDependencies = undefined ;
isCurrentFileExternalModule = false ;
}
2015-02-05 23:41:04 +01:00
2015-03-09 21:32:02 +01:00
function emitSourceFile ( sourceFile : SourceFile ) : void {
currentSourceFile = sourceFile ;
2015-11-07 01:21:43 +01:00
currentText = sourceFile . text ;
currentLineMap = getLineStarts ( sourceFile ) ;
2015-04-10 21:10:38 +02:00
exportFunctionForFile = undefined ;
2015-11-07 01:21:43 +01:00
isEs6Module = sourceFile . symbol && sourceFile . symbol . exports && ! ! sourceFile . symbol . exports [ "___esModule" ] ;
renamedDependencies = sourceFile . renamedDependencies ;
currentFileIdentifiers = sourceFile . identifiers ;
isCurrentFileExternalModule = isExternalModule ( sourceFile ) ;
2015-11-24 07:38:05 +01:00
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 ) &&
2015-11-07 01:21:43 +01:00
! hasProperty ( currentFileIdentifiers , name ) &&
2015-03-27 00:32:27 +01:00
! hasProperty ( generatedNameSet , name ) ;
2015-02-14 09:48:46 +01:00
}
2015-03-26 18:51:07 +01:00
// 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" ;
2015-03-26 18:51:07 +01:00
if ( isUniqueName ( name ) ) {
tempFlags |= flags ;
return name ;
2015-03-24 00:16:29 +01:00
}
2015-02-25 08:36:02 +01:00
}
2015-03-26 18:51:07 +01:00
while ( true ) {
2015-11-04 23:02:33 +01:00
const count = tempFlags & TempFlags . CountMask ;
2015-03-26 18:51:07 +01:00
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
}
}
2015-02-14 09:48:46 +01:00
}
2015-03-05 00:50:43 +01:00
}
2015-03-23 19:06:44 +01:00
2015-03-26 18:51:07 +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-02-14 09:48:46 +01:00
}
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-02-14 09:48:46 +01:00
}
}
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 ;
2015-06-02 19:45:50 +02:00
// 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" ;
2015-06-02 19:45:50 +02:00
return makeUniqueName ( baseName ) ;
2015-03-24 00:16:29 +01:00
}
2015-06-02 19:45:50 +02:00
function generateNameForExportDefault() {
return makeUniqueName ( "default" ) ;
2015-03-26 18:51:07 +01:00
}
2015-06-18 23:17:13 +02:00
function generateNameForClassExpression() {
return makeUniqueName ( "class" ) ;
}
2015-03-26 18:51:07 +01:00
function generateNameForNode ( node : Node ) {
switch ( node . kind ) {
2015-06-03 15:57:20 +02:00
case SyntaxKind . Identifier :
return makeUniqueName ( ( < Identifier > node ) . text ) ;
2015-03-26 18:51:07 +01:00
case SyntaxKind . ModuleDeclaration :
case SyntaxKind . EnumDeclaration :
2015-06-02 19:45:50 +02:00
return generateNameForModuleOrEnum ( < ModuleDeclaration | EnumDeclaration > node ) ;
2015-03-26 18:51:07 +01:00
case SyntaxKind . ImportDeclaration :
case SyntaxKind . ExportDeclaration :
2015-06-02 19:45:50 +02:00
return generateNameForImportOrExportDeclaration ( < ImportDeclaration | ExportDeclaration > node ) ;
case SyntaxKind . FunctionDeclaration :
case SyntaxKind . ClassDeclaration :
2015-03-26 18:51:07 +01:00
case SyntaxKind . ExportAssignment :
2015-06-02 19:45:50 +02:00
return generateNameForExportDefault ( ) ;
2015-06-18 23:17:13 +02:00
case SyntaxKind . ClassExpression :
return generateNameForClassExpression ( ) ;
2015-03-04 06:34:20 +01:00
}
2015-03-24 00:16:29 +01:00
}
function getGeneratedNameForNode ( node : Node ) {
2015-11-04 23:02:33 +01:00
const id = getNodeId ( node ) ;
2015-06-02 19:45:50 +02:00
return nodeToGeneratedName [ id ] || ( nodeToGeneratedName [ id ] = unescapeIdentifier ( generateNameForNode ( node ) ) ) ;
2015-02-14 09:48:46 +01:00
}
2015-11-24 07:38:05 +01:00
/** Write emitted output to disk */
function writeEmittedFiles ( emitOutput : string , jsFilePath : string , sourceMapFilePath : string , writeByteOrderMark : boolean ) {
if ( compilerOptions . sourceMap && ! compilerOptions . inlineSourceMap ) {
writeFile ( host , emitterDiagnostics , sourceMapFilePath , sourceMap . getText ( ) , /*writeByteOrderMark*/ false ) ;
2014-11-23 00:38:20 +01:00
}
2015-10-01 00:45:23 +02:00
2015-11-24 07:38:05 +01:00
if ( sourceMapDataList ) {
sourceMapDataList . push ( sourceMap . getSourceMapData ( ) ) ;
2015-07-30 20:11:36 +02:00
}
2014-10-11 01:44:14 +02:00
2015-10-30 21:43:52 +01:00
writeFile ( host , emitterDiagnostics , jsFilePath , emitOutput , writeByteOrderMark ) ;
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.
2015-03-26 18:51:07 +01:00
function createTempVariable ( flags : TempFlags ) : Identifier {
2015-11-04 23:02:33 +01:00
const result = < Identifier > createSynthesizedNode ( SyntaxKind . Identifier ) ;
2015-03-26 18:51:07 +01:00
result . text = makeTempVariableName ( flags ) ;
2014-12-04 01:43:01 +01:00
return result ;
2014-07-13 01:04:16 +02:00
}
2014-07-17 00:39:14 +02:00
2015-02-18 21:10:00 +01:00
function recordTempDeclaration ( name : Identifier ) : void {
2014-12-04 01:43:01 +01:00
if ( ! tempVariables ) {
tempVariables = [ ] ;
}
tempVariables . push ( name ) ;
2014-07-13 01:04:16 +02:00
}
2015-03-26 18:51:07 +01:00
function createAndRecordTempVariable ( flags : TempFlags ) : Identifier {
2015-11-04 23:02:33 +01:00
const temp = createTempVariable ( flags ) ;
2015-02-18 21:10:00 +01:00
recordTempDeclaration ( temp ) ;
return temp ;
}
2014-12-04 01:43:01 +01:00
function emitTempDeclarations ( newLine : boolean ) {
if ( tempVariables ) {
if ( newLine ) {
writeLine ( ) ;
}
else {
write ( " " ) ;
}
write ( "var " ) ;
emitCommaList ( tempVariables ) ;
write ( ";" ) ;
2014-08-22 23:40:47 +02:00
}
2014-12-04 01:43:01 +01:00
}
2014-08-22 23:40:47 +02:00
2015-11-24 07:38:05 +01:00
/ * * E m i t t h e t e x t f o r t h e g i v e n t o k e n t h a t c o m e s a f t e r s t a r t P o s
* 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 ) ;
2014-10-11 01:44:14 +02:00
if ( emitFn ) {
emitFn ( ) ;
2014-07-13 01:04:16 +02:00
}
2014-07-17 00:39:14 +02:00
else {
2014-10-11 01:44:14 +02:00
write ( tokenString ) ;
2014-08-14 15:21:30 +02:00
}
2015-11-24 07:38:05 +01:00
const tokenEndPos = tokenStartPos + tokenString . length ;
emitPos ( tokenEndPos ) ;
return tokenEndPos ;
2014-07-13 01:04:16 +02:00
}
2014-10-11 01:44:14 +02:00
function emitOptional ( prefix : string , node : Node ) {
if ( node ) {
write ( prefix ) ;
emit ( node ) ;
2014-07-13 01:04:16 +02:00
}
}
2015-01-23 15:46:32 +01:00
function emitParenthesizedIf ( node : Node , parenthesized : boolean ) {
2014-12-12 01:23:57 +01:00
if ( parenthesized ) {
write ( "(" ) ;
}
emit ( node ) ;
if ( parenthesized ) {
write ( ")" ) ;
}
}
2014-12-04 01:43:01 +01:00
function emitTrailingCommaIfPresent ( nodeList : NodeArray < Node > ) : void {
2014-10-11 01:44:14 +02:00
if ( nodeList . hasTrailingComma ) {
write ( "," ) ;
2014-07-18 04:54:21 +02:00
}
}
2015-02-07 05:16:35 +01:00
function emitLinePreservingList ( parent : Node , nodes : NodeArray < Node > , allowTrailingComma : boolean , spacesBetweenBraces : boolean ) {
Debug . assert ( nodes . length > 0 ) ;
2015-02-22 00:00:23 +01:00
2015-02-07 05:16:35 +01:00
increaseIndent ( ) ;
2015-02-22 00:00:23 +01:00
2015-03-26 21:46:35 +01:00
if ( nodeStartPositionsAreOnSameLine ( parent , nodes [ 0 ] ) ) {
2015-02-07 05:16:35 +01:00
if ( spacesBetweenBraces ) {
write ( " " ) ;
}
2014-07-17 00:39:14 +02:00
}
2015-02-07 05:16:35 +01:00
else {
writeLine ( ) ;
}
2015-03-13 20:34:12 +01:00
for ( let i = 0 , n = nodes . length ; i < n ; i ++ ) {
2015-02-07 05:16:35 +01:00
if ( i ) {
2015-03-26 21:46:35 +01:00
if ( nodeEndIsOnSameLineAsNodeStart ( nodes [ i - 1 ] , nodes [ i ] ) ) {
2015-02-07 05:16:35 +01:00
write ( ", " ) ;
}
else {
write ( "," ) ;
writeLine ( ) ;
}
}
emit ( nodes [ i ] ) ;
}
if ( nodes . hasTrailingComma && allowTrailingComma ) {
write ( "," ) ;
}
decreaseIndent ( ) ;
2015-02-22 00:00:23 +01:00
2015-03-26 21:46:35 +01:00
if ( nodeEndPositionsAreOnSameLine ( parent , lastOrUndefined ( nodes ) ) ) {
2015-02-07 05:16:35 +01:00
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 ++ ) {
2014-12-12 01:23:57 +01:00
if ( multiLine ) {
2015-04-03 00:22:53 +02:00
if ( i || leadingComma ) {
2014-12-12 01:23:57 +01:00
write ( "," ) ;
}
writeLine ( ) ;
}
else {
2015-04-03 00:22:53 +02:00
if ( i || leadingComma ) {
2014-10-11 01:44:14 +02:00
write ( ", " ) ;
2014-08-22 23:40:47 +02:00
}
2014-07-17 00:39:14 +02:00
}
2015-11-04 23:02:33 +01:00
const node = nodes [ start + i ] ;
2015-08-07 02:20:35 +02:00
// This emitting is to make sure we emit following comment properly
2015-08-11 02:34:39 +02:00
// ...(x, /*comment1*/ y)...
2015-08-07 02:20:35 +02:00
// ^ => node.pos
2015-08-11 02:34:39 +02:00
// "comment1" is not considered leading comment for "y" but rather
2015-08-07 02:20:35 +02:00
// considered as trailing comment of the previous node.
emitTrailingCommentsOfPosition ( node . pos ) ;
emitNode ( node ) ;
2015-04-03 00:22:53 +02:00
leadingComma = true ;
2014-12-12 01:23:57 +01:00
}
if ( trailingComma ) {
write ( "," ) ;
}
2015-04-03 00:22:53 +02:00
if ( multiLine && ! noTrailingNewLine ) {
2014-12-12 01:23:57 +01:00
writeLine ( ) ;
}
2015-04-03 00:22:53 +02:00
return count ;
2014-12-12 01:23:57 +01:00
}
function emitCommaList ( nodes : Node [ ] ) {
if ( nodes ) {
2015-11-14 02:43:53 +01:00
emitList ( nodes , 0 , nodes . length , /*multiLine*/ false , /*trailingComma*/ false ) ;
2014-07-17 00:39:14 +02:00
}
}
2014-10-11 01:44:14 +02:00
function emitLines ( nodes : Node [ ] ) {
emitLinesStartingAt ( nodes , /*startIndex*/ 0 ) ;
2014-07-17 00:39:14 +02:00
}
2014-10-11 01:44:14 +02:00
function emitLinesStartingAt ( nodes : Node [ ] , startIndex : number ) : void {
2015-03-13 20:34:12 +01:00
for ( let i = startIndex ; i < nodes . length ; i ++ ) {
2014-07-17 00:39:14 +02:00
writeLine ( ) ;
2014-10-11 01:44:14 +02:00
emit ( nodes [ i ] ) ;
2014-07-13 01:04:16 +02:00
}
2014-07-17 00:39:14 +02:00
}
2015-11-20 02:08:51 +01:00
function isBinaryOrOctalIntegerLiteral ( node : LiteralLikeNode , text : string ) : boolean {
2015-03-03 00:51:23 +01:00
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 ;
}
2014-12-01 20:22:37 +01:00
}
2015-03-23 19:06:44 +01:00
2014-12-01 20:22:37 +01:00
return false ;
2014-07-17 00:39:14 +02:00
}
2015-11-20 02:08:51 +01:00
function emitLiteral ( node : LiteralExpression | TemplateLiteralFragment ) {
2015-11-04 23:02:33 +01:00
const text = getLiteralText ( node ) ;
2015-03-23 19:06:44 +01:00
2015-04-08 09:14:23 +02:00
if ( ( compilerOptions . sourceMap || compilerOptions . inlineSourceMap ) && ( node . kind === SyntaxKind . StringLiteral || isTemplateLiteralKind ( node . kind ) ) ) {
2014-10-11 01:44:14 +02:00
writer . writeLiteral ( text ) ;
2014-07-17 00:39:14 +02:00
}
2015-03-03 00:51:23 +01:00
// For versions below ES6, emit binary & octal literals in their canonical decimal form.
else if ( languageVersion < ScriptTarget . ES6 && isBinaryOrOctalIntegerLiteral ( node , text ) ) {
2014-12-01 20:22:37 +01:00
write ( node . text ) ;
2014-07-17 00:39:14 +02:00
}
else {
2014-10-11 01:44:14 +02:00
write ( text ) ;
2014-07-17 00:39:14 +02:00
}
}
2015-03-23 19:06:44 +01:00
2015-11-20 02:08:51 +01:00
function getLiteralText ( node : LiteralExpression | TemplateLiteralFragment ) {
2015-03-03 01:40:00 +01:00
// 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 ) ) {
2015-07-24 02:30:31 +02:00
return getQuotedEscapedLiteralText ( "\"" , node . text , "\"" ) ;
2015-03-03 01:40:00 +01:00
}
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
2015-03-03 01:40:00 +01:00
// the node's parent reference, then simply get the text as it was originally written.
if ( node . parent ) {
2015-11-07 01:21:43 +01:00
return getTextOfNodeFromSourceText ( currentText , node ) ;
2015-03-03 01:40:00 +01:00
}
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.
2015-03-03 01:40:00 +01:00
switch ( node . kind ) {
case SyntaxKind . StringLiteral :
2015-07-24 02:30:31 +02:00
return getQuotedEscapedLiteralText ( "\"" , node . text , "\"" ) ;
2015-03-03 01:40:00 +01:00
case SyntaxKind . NoSubstitutionTemplateLiteral :
2015-07-24 02:30:31 +02:00
return getQuotedEscapedLiteralText ( "`" , node . text , "`" ) ;
2015-03-03 01:40:00 +01:00
case SyntaxKind . TemplateHead :
2015-07-24 02:30:31 +02:00
return getQuotedEscapedLiteralText ( "`" , node . text , "${" ) ;
2015-03-03 01:40:00 +01:00
case SyntaxKind . TemplateMiddle :
2015-07-24 02:30:31 +02:00
return getQuotedEscapedLiteralText ( "}" , node . text , "${" ) ;
2015-03-03 01:40:00 +01:00
case SyntaxKind . TemplateTail :
2015-07-24 02:30:31 +02:00
return getQuotedEscapedLiteralText ( "}" , node . text , "`" ) ;
2015-03-03 01:40:00 +01:00
case SyntaxKind . NumericLiteral :
return node . text ;
}
2015-03-23 19:06:44 +01:00
2015-03-03 01:40:00 +01:00
Debug . fail ( ` Literal kind ' ${ node . kind } ' not accounted for. ` ) ;
}
2015-03-23 19:06:44 +01:00
2015-03-03 01:40:00 +01:00
function getQuotedEscapedLiteralText ( leftQuote : string , text : string , rightQuote : string ) {
return leftQuote + escapeNonAsciiCharacters ( escapeString ( text ) ) + rightQuote ;
2014-07-17 00:39:14 +02:00
}
2015-03-23 19:06:44 +01:00
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".
2015-11-07 01:21:43 +01:00
let text = getTextOfNodeFromSourceText ( currentText , node ) ;
2015-06-26 01:24:41 +02:00
2015-01-19 11:33:43 +01:00
// text contains the original source, it will also contain quotes ("`"), dolar signs and braces ("${" and "}"),
2015-01-09 21:22:42 +01:00
// 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 ;
2015-01-09 21:22:42 +01:00
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 ) ;
2015-03-23 19:06:44 +01:00
2015-07-24 02:30:31 +02:00
write ( ` " ${ text } " ` ) ;
2015-01-19 11:45:48 +01:00
}
2015-03-23 19:06:44 +01:00
2015-11-20 02:08:51 +01:00
function emitDownlevelTaggedTemplateArray ( node : TaggedTemplateExpression , literalEmitter : ( literal : LiteralExpression | TemplateLiteralFragment ) = > void ) {
2015-02-22 10:07:32 +01:00
write ( "[" ) ;
2015-01-09 21:22:42 +01:00
if ( node . template . kind === SyntaxKind . NoSubstitutionTemplateLiteral ) {
2015-02-24 06:29:21 +01:00
literalEmitter ( < LiteralExpression > node . template ) ;
2015-01-19 11:33:43 +01:00
}
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 ) = > {
2015-01-09 21:22:42 +01:00
write ( ", " ) ;
2015-02-24 06:29:21 +01:00
literalEmitter ( child . literal ) ;
2015-01-09 21:22:42 +01:00
} ) ;
}
2015-02-22 10:07:32 +01:00
write ( "]" ) ;
}
2015-03-23 19:06:44 +01:00
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 ( ", " ) ;
2015-03-23 19:06:44 +01:00
2015-01-19 11:33:43 +01:00
emit ( tempVariable ) ;
2015-02-22 10:07:32 +01:00
write ( ".raw = " ) ;
emitDownlevelTaggedTemplateArray ( node , emitDownlevelRawTemplateLiteral ) ;
write ( ", " ) ;
2015-03-23 19:06:44 +01:00
2015-02-13 18:34:32 +01:00
emitParenthesizedIf ( node . tag , needsParenthesisForPropertyAccessOrInvocation ( node . tag ) ) ;
2015-01-19 11:33:43 +01:00
write ( "(" ) ;
emit ( tempVariable ) ;
2015-06-26 01:24:41 +02:00
2015-01-05 21:22:12 +01:00
// Now we emit the expressions
2015-01-19 11:33:43 +01:00
if ( node . template . kind === SyntaxKind . TemplateExpression ) {
2015-01-23 15:44:21 +01:00
forEach ( ( < TemplateExpression > node . template ) . templateSpans , templateSpan = > {
2015-01-09 21:22:42 +01:00
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 ;
2015-01-23 15:46:32 +01:00
emitParenthesizedIf ( templateSpan . expression , needsParens ) ;
2015-01-09 21:22:42 +01:00
} ) ;
}
2015-01-19 11:33:43 +01:00
write ( "))" ) ;
2014-07-17 00:39:14 +02:00
}
2014-11-21 05:24:08 +01:00
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 ) {
2014-11-21 05:24:08 +01:00
forEachChild ( node , emit ) ;
2014-10-28 07:56:07 +01:00
return ;
}
2014-08-22 23:40:47 +02:00
2015-11-04 23:02:33 +01:00
const emitOuterParens = isExpression ( node . parent )
2014-11-21 05:24:08 +01:00
&& templateNeedsParens ( node , < Expression > node . parent ) ;
2014-07-13 01:04:16 +02:00
2014-11-21 05:24:08 +01:00
if ( emitOuterParens ) {
write ( "(" ) ;
2014-08-14 15:42:18 +02:00
}
2014-08-15 00:41:57 +02:00
2015-03-13 20:34:12 +01:00
let headEmitted = false ;
2015-01-07 00:28:06 +01:00
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 ] ;
2014-08-22 23:40:47 +02:00
2014-11-21 05:24:08 +01:00
// 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
2014-11-21 05:24:08 +01:00
&& comparePrecedenceToBinaryPlus ( templateSpan . expression ) !== Comparison . GreaterThan ;
2015-01-07 00:28:06 +01:00
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 ( " + " ) ;
}
2015-01-23 15:46:32 +01:00
emitParenthesizedIf ( templateSpan . expression , needsParens ) ;
2015-01-24 21:33:35 +01:00
2014-11-21 05:24:08 +01:00
// 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 ( " + " ) ;
2014-11-21 05:24:08 +01:00
emitLiteral ( templateSpan . literal ) ;
2014-08-15 00:41:57 +02:00
}
2015-01-07 00:28:06 +01:00
}
2014-07-13 01:04:16 +02:00
2014-11-21 05:24:08 +01:00
if ( emitOuterParens ) {
write ( ")" ) ;
}
2014-07-13 01:04:16 +02:00
2015-01-07 00:28:06 +01: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 ;
}
2014-11-21 05:24:08 +01:00
function templateNeedsParens ( template : TemplateExpression , parent : Expression ) {
switch ( parent . kind ) {
case SyntaxKind . CallExpression :
case SyntaxKind . NewExpression :
2014-11-30 00:47:02 +01:00
return ( < CallExpression > parent ) . expression === template ;
2015-02-03 00:14:19 +01:00
case SyntaxKind . TaggedTemplateExpression :
2014-11-30 00:58:55 +01:00
case SyntaxKind . ParenthesizedExpression :
2014-11-21 05:24:08 +01:00
return false ;
default :
return comparePrecedenceToBinaryPlus ( parent ) !== Comparison . LessThan ;
2014-08-22 02:17:32 +02:00
}
2014-11-12 20:45:43 +01:00
}
2014-11-21 05:24:08 +01:00
/ * *
* Returns whether the expression has lesser , greater ,
* or equal precedence to the binary '+' operator
* /
function comparePrecedenceToBinaryPlus ( expression : Expression ) : Comparison {
2015-01-06 11:42:02 +01:00
// All binary expressions have lower precedence than '+' apart from '*', '/', and '%'
// which have greater precedence and '-' which has equal precedence.
2014-11-21 05:24:08 +01:00
// All unary operators have a higher precedence apart from yield.
2015-02-05 22:57:27 +01:00
// Arrow functions and conditionals have a lower precedence,
2014-11-21 05:24:08 +01:00
// although we convert the former into regular function expressions in ES5 mode,
// and in ES6 mode this function won't get called anyway.
2015-02-05 22:57:27 +01:00
//
2014-11-21 05:24:08 +01:00
// 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 :
2015-02-21 03:56:42 +01:00
switch ( ( < BinaryExpression > expression ) . operatorToken . kind ) {
2014-11-21 05:24:08 +01:00
case SyntaxKind . AsteriskToken :
case SyntaxKind . SlashToken :
case SyntaxKind . PercentToken :
return Comparison . GreaterThan ;
case SyntaxKind . PlusToken :
2015-01-06 11:42:02 +01:00
case SyntaxKind . MinusToken :
2014-11-21 05:24:08 +01:00
return Comparison . EqualTo ;
default :
return Comparison . LessThan ;
}
2015-04-10 07:57:13 +02:00
case SyntaxKind . YieldExpression :
2014-11-21 05:24:08 +01:00
case SyntaxKind . ConditionalExpression :
return Comparison . LessThan ;
default :
return Comparison . GreaterThan ;
2014-08-15 00:41:57 +02:00
}
}
2014-07-13 01:04:16 +02:00
}
2014-11-21 05:24:08 +01:00
function emitTemplateSpan ( span : TemplateSpan ) {
emit ( span . expression ) ;
emit ( span . literal ) ;
2014-08-22 23:17:52 +02:00
}
2015-10-22 20:35:48 +02:00
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
2015-10-22 20:35:48 +02:00
function emitTagName ( name : Identifier | QualifiedName ) {
2015-06-26 23:18:51 +02:00
if ( name . kind === SyntaxKind . Identifier && isIntrinsicJsxName ( ( < Identifier > name ) . text ) ) {
2015-07-24 02:30:31 +02:00
write ( "\"" ) ;
2015-06-26 23:18:51 +02:00
emit ( name ) ;
2015-07-24 02:30:31 +02:00
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 {
2015-11-12 20:02:19 +01:00
write ( "\"" ) ;
2015-06-18 23:01:49 +02:00
emit ( name ) ;
2015-11-12 20:02:19 +01:00
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 ) ;
2015-10-01 00:45:23 +02:00
syntheticReactRef . text = "React" ;
2015-08-27 01:12:50 +02:00
syntheticReactRef . parent = openingNode ;
2015-06-18 23:01:49 +02:00
// Call React.createElement(tag, ...
emitLeadingComments ( openingNode ) ;
2015-08-27 01:12:50 +02:00
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 React.__spread
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 ) ) {
2015-08-27 01:12:50 +02:00
emitExpressionIdentifier ( syntheticReactRef ) ;
write ( ".__spread(" ) ;
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 ) {
2015-06-19 20:30:34 +02:00
// 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-19 20:30:34 +02:00
}
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 ( "{" ) ;
2015-06-18 23:01:49 +02:00
for ( var i = 0 ; i < attrs . length ; 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 ) {
for ( var i = 0 ; i < children . length ; i ++ ) {
// Don't emit empty expressions
2015-06-22 22:55:09 +02:00
if ( children [ i ] . kind === SyntaxKind . JsxExpression && ! ( ( < JsxExpression > children [ i ] ) . expression ) ) {
continue ;
}
2015-06-18 23:01:49 +02:00
// Don't emit empty strings
2015-06-26 23:18:51 +02:00
if ( children [ i ] . kind === SyntaxKind . JsxText ) {
2015-11-04 23:02:33 +01:00
const text = getTextToEmit ( < JsxText > children [ i ] ) ;
2015-07-24 00:18:48 +02:00
if ( text !== undefined ) {
2015-07-24 02:30:31 +02:00
write ( ", \"" ) ;
2015-06-26 23:18:51 +02:00
write ( text ) ;
2015-07-24 02:30:31 +02:00
write ( "\"" ) ;
2015-06-26 23:18:51 +02:00
}
}
else {
2015-06-29 18:07:12 +02:00
write ( ", " ) ;
2015-06-26 23:18:51 +02:00
emit ( children [ i ] ) ;
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 ) ;
}
}
2015-10-22 20:35:48 +02:00
function jsxEmitPreserve ( node : JsxElement | JsxSelfClosingElement ) {
2015-06-18 23:01:49 +02:00
function emitJsxAttribute ( node : JsxAttribute ) {
emit ( node . name ) ;
2015-09-09 03:40:30 +02:00
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
}
2015-10-22 20:35:48 +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 ] ) ;
}
}
}
2015-10-22 20:35:48 +02:00
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 ) ;
for ( var i = 0 , n = node . children . length ; i < n ; i ++ ) {
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 ) ;
}
}
2014-10-11 01:44:14 +02:00
// This function specifically handles numeric/string literals for enum and accessor 'identifiers'.
// In a sense, it does not actually emit identifiers as much as it declares a name for a specific property.
2015-02-05 03:35:04 +01:00
// For example, this is utilized when feeding in a result to Object.defineProperty.
2014-11-21 05:24:08 +01:00
function emitExpressionForPropertyName ( node : DeclarationName ) {
2015-02-05 03:35:04 +01:00
Debug . assert ( node . kind !== SyntaxKind . BindingElement ) ;
2014-10-11 01:44:14 +02:00
if ( node . kind === SyntaxKind . StringLiteral ) {
2014-11-21 05:24:08 +01:00
emitLiteral ( < LiteralExpression > node ) ;
2014-07-17 00:39:14 +02:00
}
2014-11-18 00:53:03 +01:00
else if ( node . kind === SyntaxKind . ComputedPropertyName ) {
2015-03-23 22:09:38 +01:00
// 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
2015-03-23 22:09:38 +01:00
// 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));
2015-03-23 22:09:38 +01:00
//
2015-03-17 21:41:29 +01:00
if ( nodeIsDecorated ( node . parent ) ) {
2015-03-25 03:57:21 +01:00
if ( ! computedPropertyNamesToGeneratedNames ) {
computedPropertyNamesToGeneratedNames = [ ] ;
2015-03-17 21:41:29 +01:00
}
2015-04-10 21:10:38 +02:00
2015-04-07 22:17:57 +02:00
let generatedName = computedPropertyNamesToGeneratedNames [ getNodeId ( node ) ] ;
2015-03-17 21:41:29 +01:00
if ( generatedName ) {
2015-03-23 22:09:38 +01:00
// we have already generated a variable for this node, write that value instead.
2015-03-17 21:41:29 +01:00
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 ;
2015-03-17 21:41:29 +01:00
write ( generatedName ) ;
write ( " = " ) ;
}
2014-11-18 00:53:03 +01:00
emit ( ( < ComputedPropertyName > node ) . expression ) ;
2014-07-17 00:39:14 +02:00
}
2014-10-11 01:44:14 +02:00
else {
write ( "\"" ) ;
2014-08-15 00:41:57 +02:00
2014-10-11 01:44:14 +02:00
if ( node . kind === SyntaxKind . NumericLiteral ) {
2014-11-21 05:24:08 +01:00
write ( ( < LiteralExpression > node ) . text ) ;
2014-07-18 02:43:48 +02:00
}
else {
2015-11-07 01:21:43 +01:00
writeTextOfNode ( currentText , node ) ;
2014-07-18 02:43:48 +02:00
}
2014-10-11 01:44:14 +02:00
write ( "\"" ) ;
2014-08-15 00:41:57 +02:00
}
2014-07-13 01:04:16 +02:00
}
2015-06-02 19:45:50 +02:00
function isExpressionIdentifier ( node : Node ) : boolean {
2015-11-04 23:02:33 +01:00
const parent = node . parent ;
2014-10-11 01:44:14 +02:00
switch ( parent . kind ) {
2015-06-02 19:45:50 +02:00
case SyntaxKind . ArrayLiteralExpression :
2015-09-29 02:24:59 +02:00
case SyntaxKind . AsExpression :
2015-06-02 19:45:50 +02:00
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 :
2015-07-28 22:49:39 +02:00
case SyntaxKind . JsxExpression :
2015-06-02 19:45:50 +02:00
case SyntaxKind . NewExpression :
case SyntaxKind . ParenthesizedExpression :
case SyntaxKind . PostfixUnaryExpression :
case SyntaxKind . PrefixUnaryExpression :
case SyntaxKind . ReturnStatement :
2015-06-03 15:57:20 +02:00
case SyntaxKind . ShorthandPropertyAssignment :
2015-06-02 19:45:50 +02:00
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 ;
2014-12-06 22:53:06 +01:00
case SyntaxKind . BindingElement :
2014-10-11 01:44:14 +02:00
case SyntaxKind . EnumMember :
2015-06-02 19:45:50 +02:00
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 :
2014-10-11 01:44:14 +02:00
case SyntaxKind . FunctionExpression :
2015-06-02 19:45:50 +02:00
return ( < FunctionLikeDeclaration > parent ) . body === node ;
2015-01-27 23:42:20 +01:00
case SyntaxKind . ImportEqualsDeclaration :
2015-06-02 19:45:50 +02:00
return ( < ImportEqualsDeclaration > parent ) . module Reference === node ;
case SyntaxKind . QualifiedName :
return ( < QualifiedName > parent ) . left === node ;
2014-07-17 00:39:14 +02:00
}
2015-06-02 19:45:50 +02:00
return false ;
2014-07-13 01:04:16 +02:00
}
2014-11-21 05:24:08 +01:00
function emitExpressionIdentifier ( node : Identifier ) {
2015-06-20 01:01:12 +02:00
if ( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . LexicalArguments ) {
write ( "_arguments" ) ;
return ;
}
2015-07-09 23:44:47 +02:00
2015-11-04 23:02:33 +01:00
const container = resolver . getReferencedExportContainer ( node ) ;
2015-06-02 19:45:50 +02:00
if ( container ) {
if ( container . kind === SyntaxKind . SourceFile ) {
// Identifier references module export
2015-09-17 22:26:04 +02:00
if ( module kind !== ModuleKind . ES6 && module kind !== ModuleKind . System ) {
2015-06-02 19:45:50 +02:00
write ( "exports." ) ;
}
}
else {
// Identifier references namespace export
write ( getGeneratedNameForNode ( container ) ) ;
write ( "." ) ;
}
2015-02-10 23:59:20 +01:00
}
2015-10-20 19:59:23 +02:00
else {
if ( module kind !== ModuleKind . ES6 ) {
2015-11-04 23:02:33 +01:00
const declaration = resolver . getReferencedImportDeclaration ( node ) ;
2015-10-20 19:59:23 +02:00
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
}
2015-10-20 19:59:23 +02:00
else if ( declaration . kind === SyntaxKind . ImportSpecifier ) {
// Identifier references named import
write ( getGeneratedNameForNode ( < ImportDeclaration > declaration . parent . parent . parent ) ) ;
2015-11-04 23:02:33 +01:00
const name = ( < ImportSpecifier > declaration ) . propertyName || ( < ImportSpecifier > declaration ) . name ;
2015-11-07 01:21:43 +01:00
const identifier = getTextOfNodeFromSourceText ( currentText , name ) ;
2015-10-20 19:59:23 +02:00
if ( languageVersion === ScriptTarget . ES3 && identifier === "default" ) {
write ( ` ["default"] ` ) ;
}
else {
write ( "." ) ;
write ( identifier ) ;
}
return ;
2015-09-17 02:33:49 +02:00
}
2015-06-02 19:45:50 +02:00
}
}
2015-10-20 19:59:23 +02:00
if ( languageVersion !== ScriptTarget . ES6 ) {
2015-11-04 23:02:33 +01:00
const declaration = resolver . getReferencedNestedRedeclaration ( node ) ;
2015-10-20 19:59:23 +02:00
if ( declaration ) {
write ( getGeneratedNameForNode ( declaration . name ) ) ;
return ;
}
2015-06-03 15:57:20 +02:00
}
2014-08-22 23:50:56 +02:00
}
2015-08-21 22:06:42 +02:00
if ( nodeIsSynthesized ( node ) ) {
write ( node . text ) ;
}
else {
2015-11-07 01:21:43 +01:00
writeTextOfNode ( currentText , node ) ;
2015-08-21 22:06:42 +02:00
}
2014-11-21 05:24:08 +01:00
}
2015-06-03 15:57:20 +02:00
function isNameOfNestedRedeclaration ( node : Identifier ) {
if ( languageVersion < ScriptTarget . ES6 ) {
2015-11-04 23:02:33 +01:00
const parent = node . parent ;
2015-06-03 15:57:20 +02:00
switch ( parent . kind ) {
case SyntaxKind . BindingElement :
case SyntaxKind . ClassDeclaration :
case SyntaxKind . EnumDeclaration :
case SyntaxKind . VariableDeclaration :
return ( < Declaration > parent ) . name === node && resolver . isNestedRedeclaration ( < Declaration > parent ) ;
}
2015-03-16 20:37:21 +01:00
}
2015-06-03 15:57:20 +02:00
return false ;
2015-02-14 09:48:46 +01:00
}
2015-06-03 15:57:20 +02:00
function emitIdentifier ( node : Identifier ) {
2015-10-11 10:12:26 +02:00
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" ) ) ;
2015-10-11 10:12:26 +02:00
write ( name ) ;
return ;
}
}
2014-12-04 01:43:01 +01:00
if ( ! node . parent ) {
write ( node . text ) ;
2014-07-13 01:04:16 +02:00
}
2015-06-02 19:45:50 +02:00
else if ( isExpressionIdentifier ( node ) ) {
2014-11-21 05:24:08 +01:00
emitExpressionIdentifier ( node ) ;
2014-07-17 00:39:14 +02:00
}
2015-06-03 15:57:20 +02:00
else if ( isNameOfNestedRedeclaration ( node ) ) {
write ( getGeneratedNameForNode ( node ) ) ;
}
2015-08-24 21:06:21 +02:00
else if ( nodeIsSynthesized ( node ) ) {
write ( node . text ) ;
}
2014-11-21 05:24:08 +01:00
else {
2015-11-07 01:21:43 +01:00
writeTextOfNode ( currentText , node ) ;
2014-09-07 02:40:19 +02:00
}
2014-07-13 01:04:16 +02:00
}
2014-10-11 01:44:14 +02:00
function emitThis ( node : Node ) {
if ( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . LexicalThis ) {
write ( "_this" ) ;
2014-07-13 01:04:16 +02:00
}
2015-11-22 05:11:39 +01:00
else if ( convertedLoopState ) {
write ( convertedLoopState . thisName || ( convertedLoopState . thisName = makeUniqueName ( "this" ) ) ) ;
}
2014-10-11 01:44:14 +02:00
else {
write ( "this" ) ;
2014-07-13 01:04:16 +02:00
}
2014-07-17 00:39:14 +02:00
}
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
function emitSuper ( node : Node ) {
2015-03-13 00:34:06 +01:00
if ( languageVersion >= ScriptTarget . ES6 ) {
write ( "super" ) ;
2014-07-17 00:39:14 +02:00
}
2014-10-11 01:44:14 +02:00
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-17 00:39:14 +02:00
}
2014-07-13 01:04:16 +02:00
}
2014-12-04 01:43:01 +01:00
function emitObjectBindingPattern ( node : BindingPattern ) {
write ( "{ " ) ;
2015-11-04 23:02:33 +01:00
const elements = node . elements ;
2014-12-12 01:23:57 +01:00
emitList ( elements , 0 , elements . length , /*multiLine*/ false , /*trailingComma*/ elements . hasTrailingComma ) ;
2014-12-04 01:43:01 +01:00
write ( " }" ) ;
2014-07-13 01:04:16 +02:00
}
2014-12-04 01:43:01 +01:00
function emitArrayBindingPattern ( node : BindingPattern ) {
write ( "[" ) ;
2015-11-04 23:02:33 +01:00
const elements = node . elements ;
2014-12-12 01:23:57 +01:00
emitList ( elements , 0 , elements . length , /*multiLine*/ false , /*trailingComma*/ elements . hasTrailingComma ) ;
2014-12-04 01:43:01 +01:00
write ( "]" ) ;
2014-07-13 01:04:16 +02:00
}
2014-07-17 00:39:14 +02:00
2014-12-14 18:43:14 +01:00
function emitBindingElement ( node : BindingElement ) {
if ( node . propertyName ) {
2015-06-03 15:57:20 +02:00
emit ( node . propertyName ) ;
2014-12-14 18:43:14 +01:00
write ( ": " ) ;
}
if ( node . dotDotDotToken ) {
write ( "..." ) ;
}
if ( isBindingPattern ( node . name ) ) {
emit ( node . name ) ;
2014-10-11 01:44:14 +02:00
}
else {
2014-12-14 18:43:14 +01:00
emitModuleMemberName ( node ) ;
}
emitOptional ( " = " , node . initializer ) ;
}
2014-12-12 01:23:57 +01:00
function emitSpreadElementExpression ( node : SpreadElementExpression ) {
write ( "..." ) ;
emit ( ( < SpreadElementExpression > node ) . expression ) ;
}
2015-04-10 07:57:13 +02:00
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 ) {
2015-05-13 20:15:51 +02:00
if ( node . parent . kind === SyntaxKind . BinaryExpression && ! isAssignmentOperator ( ( < BinaryExpression > node . parent ) . operatorToken . kind ) ) {
return true ;
2015-05-07 02:33:58 +02:00
}
2015-05-13 20:15:51 +02:00
else if ( node . parent . kind === SyntaxKind . ConditionalExpression && ( < ConditionalExpression > node . parent ) . condition === node ) {
return true ;
}
2015-05-07 02:33:58 +02:00
return false ;
}
2015-04-10 07:57:13 +02:00
2015-02-13 18:34:32 +01:00
function needsParenthesisForPropertyAccessOrInvocation ( node : Expression ) {
2014-12-12 01:23:57 +01:00
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.
2014-12-12 01:23:57 +01:00
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 ;
2014-12-12 01:23:57 +01:00
while ( pos < length ) {
2014-12-16 00:16:54 +01:00
// Emit using the pattern <group0>.concat(<group1>, <group2>, ...)
2015-05-17 03:27:20 +02:00
if ( group === 1 && useConcat ) {
2014-12-12 01:23:57 +01:00
write ( ".concat(" ) ;
}
2015-05-17 03:27:20 +02:00
else if ( group > 0 ) {
2014-12-12 01:23:57 +01:00
write ( ", " ) ;
}
2015-03-13 20:34:12 +01:00
let e = elements [ pos ] ;
2014-12-12 01:23:57 +01:00
if ( e . kind === SyntaxKind . SpreadElementExpression ) {
e = ( < SpreadElementExpression > e ) . expression ;
2015-02-13 18:34:32 +01:00
emitParenthesizedIf ( e , /*parenthesized*/ group === 0 && needsParenthesisForPropertyAccessOrInvocation ( e ) ) ;
2014-12-12 01:23:57 +01:00
pos ++ ;
2015-05-15 03:18:24 +02:00
if ( pos === length && group === 0 && needsUniqueCopy && e . kind !== SyntaxKind . ArrayLiteralExpression ) {
2015-05-13 00:49:41 +02:00
write ( ".slice()" ) ;
}
2014-12-12 01:23:57 +01:00
}
else {
2015-03-13 20:34:12 +01:00
let i = pos ;
2014-12-16 00:16:54 +01:00
while ( i < length && elements [ i ] . kind !== SyntaxKind . SpreadElementExpression ) {
i ++ ;
}
2014-12-12 01:23:57 +01:00
write ( "[" ) ;
2015-02-19 22:11:59 +01:00
if ( multiLine ) {
increaseIndent ( ) ;
}
2015-02-06 16:39:11 +01:00
emitList ( elements , pos , i - pos , multiLine , trailingComma && i === length ) ;
2015-02-19 22:11:59 +01:00
if ( multiLine ) {
decreaseIndent ( ) ;
}
2014-12-12 01:23:57 +01:00
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 ( ")" ) ;
}
2014-07-17 00:39:14 +02:00
}
}
2015-02-22 00:00:23 +01:00
function isSpreadElementExpression ( node : Node ) {
return node . kind === SyntaxKind . SpreadElementExpression ;
}
2015-02-04 22:39:24 +01:00
function emitArrayLiteral ( node : ArrayLiteralExpression ) {
2015-11-04 23:02:33 +01:00
const elements = node . elements ;
2015-02-04 22:39:24 +01:00
if ( elements . length === 0 ) {
write ( "[]" ) ;
}
2015-02-22 00:00:23 +01:00
else if ( languageVersion >= ScriptTarget . ES6 || ! forEach ( elements , isSpreadElementExpression ) ) {
2015-02-04 22:39:24 +01:00
write ( "[" ) ;
2015-11-14 02:43:53 +01:00
emitLinePreservingList ( node , node . elements , elements . hasTrailingComma , /*spacesBetweenBraces*/ false ) ;
2015-02-04 22:39:24 +01:00
write ( "]" ) ;
}
2015-02-06 16:39:11 +01:00
else {
2015-05-15 03:18:24 +02:00
emitListWithSpread ( elements , /*needsUniqueCopy*/ true , /*multiLine*/ ( node . flags & NodeFlags . MultiLine ) !== 0 ,
2015-05-16 10:42:49 +02:00
/*trailingComma*/ elements . hasTrailingComma , /*useConcat*/ true ) ;
2015-02-06 16:39:11 +01:00
}
2015-02-04 22:39:24 +01:00
}
2015-02-24 02:44:48 +01:00
function emitObjectLiteralBody ( node : ObjectLiteralExpression , numElements : number ) : void {
if ( numElements === 0 ) {
write ( "{}" ) ;
return ;
}
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
write ( "{" ) ;
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
if ( numElements > 0 ) {
2015-11-04 23:02:33 +01:00
const properties = node . properties ;
2015-02-21 03:16:52 +01:00
2015-02-24 02:44:48 +01:00
// 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 ) ;
2015-02-24 02:44:48 +01:00
}
else {
2015-11-04 23:02:33 +01:00
const multiLine = ( node . flags & NodeFlags . MultiLine ) !== 0 ;
2015-02-24 02:44:48 +01:00
if ( ! multiLine ) {
write ( " " ) ;
}
else {
increaseIndent ( ) ;
2015-02-21 03:16:52 +01:00
}
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
emitList ( properties , 0 , numElements , /*multiLine*/ multiLine , /*trailingComma*/ false ) ;
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
if ( ! multiLine ) {
write ( " " ) ;
2015-02-21 03:16:52 +01:00
}
2015-02-24 02:44:48 +01:00
else {
decreaseIndent ( ) ;
2015-02-21 03:16:52 +01:00
}
2015-02-24 02:44:48 +01:00
}
2015-02-21 03:16:52 +01:00
}
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
write ( "}" ) ;
2015-02-20 23:16:09 +01:00
}
2015-02-24 02:44:48 +01:00
function emitDownlevelObjectLiteralWithComputedProperties ( node : ObjectLiteralExpression , firstComputedPropertyIndex : number ) {
2015-11-04 23:02:33 +01:00
const multiLine = ( node . flags & NodeFlags . MultiLine ) !== 0 ;
const properties = node . properties ;
2015-03-23 19:06:44 +01:00
2015-02-24 02:44:48 +01:00
write ( "(" ) ;
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
if ( multiLine ) {
increaseIndent ( ) ;
2015-02-20 23:16:09 +01:00
}
2015-02-24 02:44:48 +01:00
// 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 ) ;
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
// 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 ) ;
2015-02-24 02:44:48 +01:00
write ( " = " ) ;
emitObjectLiteralBody ( node , firstComputedPropertyIndex ) ;
2015-02-20 23:16:09 +01:00
2015-04-06 23:19:28 +02:00
for ( let i = firstComputedPropertyIndex , n = properties . length ; i < n ; i ++ ) {
2015-02-24 02:44:48 +01:00
writeComma ( ) ;
2015-02-20 23:16:09 +01:00
2015-11-04 23:02:33 +01:00
const property = properties [ i ] ;
2015-02-20 23:16:09 +01:00
2015-06-26 01:24:41 +02:00
emitStart ( property ) ;
2015-02-24 02:44:48 +01:00
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 ) ;
2015-02-24 02:44:48 +01:00
if ( property !== accessors . firstAccessor ) {
continue ;
}
write ( "Object.defineProperty(" ) ;
emit ( tempVar ) ;
write ( ", " ) ;
emitStart ( node . name ) ;
emitExpressionForPropertyName ( property . name ) ;
emitEnd ( property . name ) ;
write ( ", {" ) ;
increaseIndent ( ) ;
if ( accessors . getAccessor ) {
2015-06-26 01:24:41 +02:00
writeLine ( ) ;
2015-02-24 02:44:48 +01:00
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 ) ;
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
write ( " = " ) ;
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
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 ) ;
}
}
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
emitEnd ( property ) ;
}
2015-03-23 19:06:44 +01:00
2015-02-24 02:44:48 +01:00
writeComma ( ) ;
emit ( tempVar ) ;
2015-02-21 03:02:14 +01:00
2015-02-24 02:44:48 +01:00
if ( multiLine ) {
decreaseIndent ( ) ;
writeLine ( ) ;
}
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
write ( ")" ) ;
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
function writeComma() {
if ( multiLine ) {
write ( "," ) ;
writeLine ( ) ;
}
else {
write ( ", " ) ;
}
}
2015-02-20 23:16:09 +01:00
}
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-05 03:35:04 +01:00
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-05 03:35:04 +01:00
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 ;
}
2014-07-18 02:43:48 +02:00
}
2015-03-23 19:06:44 +01:00
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 ;
2014-07-18 02:43:48 +02:00
}
}
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.
2015-02-24 02:44:48 +01:00
emitObjectLiteralBody ( node , properties . length ) ;
}
2015-02-22 00:11:37 +01:00
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 ) ;
2015-04-06 23:19:28 +02:00
result . operatorToken = createSynthesizedNode ( operator ) ;
result . left = left ;
result . right = right ;
2015-02-22 00:11:37 +01:00
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-12-04 01:41:58 +01:00
const result = < PropertyAccessExpression > createSourceMappedSynthesizedNode ( SyntaxKind . PropertyAccessExpression , name ) ;
2015-04-11 03:56:11 +02:00
result . expression = parenthesizeForAccess ( expression ) ;
2015-04-06 23:19:28 +02:00
result . dotToken = createSynthesizedNode ( SyntaxKind . DotToken ) ;
result . name = name ;
return result ;
2015-04-11 03:56:11 +02:00
}
2015-04-06 23:19:28 +02:00
2015-12-09 01:36:19 +01:00
function createElementAccessExpression ( expression : Expression , argumentExpression : Expression , sourceMapNode : Node ) : ElementAccessExpression {
const result = < ElementAccessExpression > createSourceMappedSynthesizedNode ( SyntaxKind . ElementAccessExpression , sourceMapNode ) ;
2015-04-11 03:56:11 +02:00
result . expression = parenthesizeForAccess ( expression ) ;
2015-04-06 23:19:28 +02:00
result . argumentExpression = argumentExpression ;
return result ;
2014-07-19 01:59:52 +02:00
}
2015-04-11 03:56:11 +02:00
function parenthesizeForAccess ( expr : Expression ) : LeftHandSideExpression {
2015-06-04 02:22:11 +02:00
// When diagnosing whether the expression needs parentheses, the decision should be based
// on the innermost expression in a chain of nested type assertions.
2015-06-18 23:01:49 +02:00
while ( expr . kind === SyntaxKind . TypeAssertionExpression || expr . kind === SyntaxKind . AsExpression ) {
expr = ( < AssertionExpression > expr ) . expression ;
2015-06-04 02:22:11 +02:00
}
2015-04-16 02:22:19 +02:00
// 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
//
2015-06-04 02:34:56 +02:00
if ( isLeftHandSideExpression ( expr ) &&
expr . kind !== SyntaxKind . NewExpression &&
expr . kind !== SyntaxKind . NumericLiteral ) {
2015-06-04 02:22:11 +02:00
2015-04-11 03:56:11 +02:00
return < LeftHandSideExpression > expr ;
}
2015-12-04 01:41:58 +01:00
const node = < ParenthesizedExpression > createSourceMappedSynthesizedNode ( SyntaxKind . ParenthesizedExpression , expr ) ;
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 ( "[" ) ;
2015-03-17 21:41:29 +01:00
emitExpressionForPropertyName ( node ) ;
2014-11-18 00:53:03 +01:00
write ( "]" ) ;
}
2014-12-03 09:04:54 +01:00
2014-12-03 11:09:15 +01:00
function emitMethod ( node : MethodDeclaration ) {
2015-04-10 07:57:13 +02:00
if ( languageVersion >= ScriptTarget . ES6 && node . asteriskToken ) {
write ( "*" ) ;
}
2015-06-03 15:57:20 +02:00
emit ( node . name ) ;
2015-01-16 16:15:31 +01:00
if ( languageVersion < ScriptTarget . ES6 ) {
2014-12-08 23:34:00 +01:00
write ( ": function " ) ;
}
2014-12-03 11:09:15 +01:00
emitSignatureAndBody ( node ) ;
2014-10-11 01:44:14 +02:00
}
2014-08-15 00:41:57 +02:00
2014-10-11 01:44:14 +02:00
function emitPropertyAssignment ( node : PropertyDeclaration ) {
2015-06-03 15:57:20 +02:00
emit ( node . name ) ;
2014-11-25 21:12:55 +01:00
write ( ": " ) ;
2015-08-07 02:20:35 +02:00
// 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
2015-08-07 02:20:35 +02:00
// }
// "comment1" is not considered to be leading comment for node.initializer
// but rather a trailing comment on the previous node.
emitTrailingCommentsOfPosition ( node . initializer . pos ) ;
2014-10-11 01:44:14 +02:00
emit ( node . initializer ) ;
2014-11-25 21:12:55 +01:00
}
2015-06-03 15:57:20 +02:00
// Return true if identifier resolves to an exported member of a namespace
function isNamespaceExportReference ( node : Identifier ) {
2015-11-04 23:02:33 +01:00
const container = resolver . getReferencedExportContainer ( node ) ;
2015-06-03 15:57:20 +02:00
return container && container . kind !== SyntaxKind . SourceFile ;
}
2014-12-08 23:34:00 +01:00
function emitShorthandPropertyAssignment ( node : ShorthandPropertyAssignment ) {
2015-06-03 15:57:20 +02:00
// The name property of a short-hand property assignment is considered an expression position, so here
// we manually emit the identifier to avoid rewriting.
2015-11-07 01:21:43 +01:00
writeTextOfNode ( currentText , node . name ) ;
2015-06-03 15:57:20 +02:00
// 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.
2015-11-09 22:34:30 +01:00
if ( module kind !== ModuleKind . ES6 || isNamespaceExportReference ( node . name ) ) {
2015-03-16 20:37:21 +01:00
// Emit identifier as an identifier
write ( ": " ) ;
2015-06-03 15:57:20 +02:00
emit ( node . name ) ;
2014-11-21 05:24:08 +01:00
}
2015-10-05 22:46:56 +02:00
if ( languageVersion >= ScriptTarget . ES6 && node . objectAssignmentInitializer ) {
write ( " = " ) ;
emit ( node . objectAssignmentInitializer ) ;
}
2014-11-21 05:24:08 +01:00
}
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 ) ;
2014-10-11 01:44:14 +02:00
if ( constantValue !== undefined ) {
2015-01-30 12:09:31 +01:00
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 ) ;
2015-01-30 12:09:31 +01:00
write ( " /* " + propertyName + " */" ) ;
}
2014-11-21 05:24:08 +01:00
return true ;
2014-07-17 00:39:14 +02:00
}
2014-11-21 05:24:08 +01:00
return false ;
2014-07-17 00:39:14 +02:00
}
2015-10-01 00:45:23 +02:00
2015-08-05 09:02:29 +02:00
function tryGetConstEnumValue ( node : Node ) : number {
if ( compilerOptions . isolatedModules ) {
return undefined ;
}
2015-10-01 00:45:23 +02:00
return node . kind === SyntaxKind . PropertyAccessExpression || node . kind === SyntaxKind . ElementAccessExpression
2015-08-05 09:02:29 +02:00
? resolver . getConstantValue ( < PropertyAccessExpression | ElementAccessExpression > node )
2015-10-01 00:45:23 +02:00
: undefined ;
2015-08-05 09:02:29 +02:00
}
2014-07-17 00:39:14 +02:00
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
2015-03-09 23:01:02 +01:00
// emitted instead.
function indentIfOnDifferentLines ( parent : Node , node1 : Node , node2 : Node , 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
2015-03-09 06:35:28 +01:00
// Always use a newline for synthesized code if the synthesizer desires it.
2015-11-04 23:02:33 +01:00
const synthesizedNodeIsOnDifferentLine = synthesizedNodeStartsOnNewLine ( node2 ) ;
2015-03-09 02:23:23 +01:00
2015-03-07 22:33:02 +01:00
if ( realNodesAreOnDifferentLines || synthesizedNodeIsOnDifferentLine ) {
increaseIndent ( ) ;
writeLine ( ) ;
return true ;
}
2015-03-09 07:07:03 +01:00
else {
2015-03-09 09:07:02 +01:00
if ( valueToWriteWhenNotIndenting ) {
write ( valueToWriteWhenNotIndenting ) ;
2015-03-09 07:07:03 +01:00
}
return false ;
}
2015-03-07 22:33:02 +01:00
}
2014-11-30 00:58:55 +01:00
function emitPropertyAccess ( node : PropertyAccessExpression ) {
2014-11-21 05:24:08 +01:00
if ( tryEmitConstantValue ( node ) ) {
2014-08-22 23:35:07 +02:00
return ;
}
2015-03-05 11:31:55 +01:00
2014-11-30 02:25:52 +01:00
emit ( node . expression ) ;
2015-11-04 23:02:33 +01:00
const indentedBeforeDot = indentIfOnDifferentLines ( node , node . expression , node . 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
2015-08-05 09:02:29 +02:00
// Also emit a space if expression is a integer const enum value - it will appear in generated code as numeric literal
2015-11-07 01:21:43 +01:00
let shouldEmitSpace = false ;
2015-08-05 09:02:29 +02:00
if ( ! indentedBeforeDot ) {
if ( node . expression . kind === SyntaxKind . NumericLiteral ) {
// check if numeric literal was originally written with a dot
2015-11-07 01:21:43 +01:00
const text = getTextOfNodeFromSourceText ( currentText , node . expression ) ;
2015-08-05 09:02:29 +02:00
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 ) ;
2015-08-05 19:36:47 +02:00
// isFinite handles cases when constantValue is undefined
shouldEmitSpace = isFinite ( constantValue ) && Math . floor ( constantValue ) === constantValue ;
2015-08-05 09:02:29 +02:00
}
2015-06-25 00:02:20 +02:00
}
if ( shouldEmitSpace ) {
2015-06-26 01:24:41 +02:00
write ( " ." ) ;
2015-06-25 00:02:20 +02:00
}
else {
2015-06-24 16:56:23 +02:00
write ( "." ) ;
}
2015-06-25 00:02:20 +02:00
2015-11-04 23:02:33 +01:00
const indentedAfterDot = indentIfOnDifferentLines ( node , node . dotToken , node . name ) ;
2015-06-03 15:57:20 +02:00
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
2014-11-30 01:48:28 +01:00
function emitQualifiedName ( node : QualifiedName ) {
2014-11-21 05:24:08 +01:00
emit ( node . left ) ;
write ( "." ) ;
emit ( node . right ) ;
2014-07-13 01:04:16 +02:00
}
2014-08-22 23:35:07 +02:00
2015-06-19 19:32:10 +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 ) ;
2015-06-19 19:32:10 +02:00
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
2015-06-19 19:32:10 +02:00
write ( "." ) ;
2015-07-30 20:11:36 +02:00
emit ( node . right ) ;
2015-06-19 19:32:10 +02:00
}
2015-07-27 13:52:57 +02:00
2015-12-01 21:12:31 +01:00
function emitEntityNameAsExpression ( node : EntityName | Expression , useFallback : boolean ) {
2015-06-19 19:32:10 +02:00
switch ( node . kind ) {
case SyntaxKind . Identifier :
if ( useFallback ) {
write ( "typeof " ) ;
emitExpressionIdentifier ( < Identifier > node ) ;
write ( " !== 'undefined' && " ) ;
}
2015-07-27 13:52:57 +02:00
2015-06-19 19:32:10 +02:00
emitExpressionIdentifier ( < Identifier > node ) ;
break ;
2015-07-27 13:52:57 +02:00
2015-06-19 19:32:10 +02:00
case SyntaxKind . QualifiedName :
emitQualifiedNameAsExpression ( < QualifiedName > node , useFallback ) ;
break ;
2015-12-01 21:12:31 +01:00
default :
emitNodeWithoutSourceMap ( node ) ;
break ;
2015-06-19 19:32:10 +02:00
}
}
2014-11-30 00:47:02 +01:00
function emitIndexedAccess ( node : ElementAccessExpression ) {
2014-11-21 05:24:08 +01:00
if ( tryEmitConstantValue ( node ) ) {
return ;
2014-08-22 23:35:07 +02:00
}
2014-11-30 00:47:02 +01:00
emit ( node . expression ) ;
2014-10-11 01:44:14 +02:00
write ( "[" ) ;
2014-11-30 00:47:02 +01:00
emit ( node . argumentExpression ) ;
2014-10-11 01:44:14 +02:00
write ( "]" ) ;
2014-07-13 01:04:16 +02:00
}
2014-08-22 23:35:07 +02:00
2015-02-04 22:39:24 +01:00
function hasSpreadElement ( elements : Expression [ ] ) {
return forEach ( elements , e = > e . kind === SyntaxKind . SpreadElementExpression ) ;
}
function skipParentheses ( node : Expression ) : Expression {
2015-06-18 23:01:49 +02:00
while ( node . kind === SyntaxKind . ParenthesizedExpression || node . kind === SyntaxKind . TypeAssertionExpression || node . kind === SyntaxKind . AsExpression ) {
node = ( < ParenthesizedExpression | AssertionExpression > node ) . expression ;
2015-02-04 22:39:24 +01:00
}
return node ;
}
2015-02-06 16:39:11 +01:00
function emitCallTarget ( node : Expression ) : Expression {
2015-02-04 22:39:24 +01:00
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 ) ;
2015-02-18 21:10:00 +01:00
2015-02-04 22:39:24 +01:00
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 ) ;
2015-02-04 22:39:24 +01:00
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 ) ;
2015-02-04 22:39:24 +01:00
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 ) ;
2015-02-04 22:39:24 +01:00
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(...)
2015-02-04 22:39:24 +01:00
emitThis ( target ) ;
}
else {
2015-02-06 16:39:11 +01:00
// Calls of form obj.foo(...)
2015-02-04 22:39:24 +01:00
emit ( target ) ;
}
}
else {
2015-02-06 16:39:11 +01:00
// Calls of form foo(...)
2015-02-04 22:39:24 +01:00
write ( "void 0" ) ;
}
write ( ", " ) ;
2015-05-16 10:42:49 +02:00
emitListWithSpread ( node . arguments , /*needsUniqueCopy*/ false , /*multiLine*/ false , /*trailingComma*/ false , /*useConcat*/ true ) ;
2015-02-04 22:39:24 +01:00
write ( ")" ) ;
}
2014-10-11 01:44:14 +02:00
function emitCallExpression ( node : CallExpression ) {
2015-02-04 22:39:24 +01:00
if ( languageVersion < ScriptTarget . ES6 && hasSpreadElement ( node . arguments ) ) {
emitCallWithSpread ( node ) ;
return ;
}
2015-03-13 20:34:12 +01:00
let superCall = false ;
2014-11-30 00:47:02 +01:00
if ( node . expression . kind === SyntaxKind . SuperKeyword ) {
2015-03-15 00:53:33 +01:00
emitSuper ( node . expression ) ;
2014-10-11 01:44:14 +02:00
superCall = true ;
2014-08-22 23:50:56 +02:00
}
2014-10-11 01:44:14 +02:00
else {
2014-11-30 00:47:02 +01:00
emit ( node . expression ) ;
2014-11-30 02:25:52 +01:00
superCall = node . expression . kind === SyntaxKind . PropertyAccessExpression && ( < PropertyAccessExpression > node . expression ) . expression . kind === SyntaxKind . SuperKeyword ;
2014-10-11 01:44:14 +02:00
}
2015-03-10 20:12:41 +01:00
if ( superCall && languageVersion < ScriptTarget . ES6 ) {
2014-10-11 01:44:14 +02:00
write ( ".call(" ) ;
2014-11-30 00:47:02 +01:00
emitThis ( node . expression ) ;
2014-10-11 01:44:14 +02:00
if ( node . arguments . length ) {
write ( ", " ) ;
2014-12-04 01:43:01 +01:00
emitCommaList ( node . arguments ) ;
2014-10-11 01:44:14 +02:00
}
write ( ")" ) ;
2014-07-13 01:04:16 +02:00
}
else {
2014-10-11 01:44:14 +02:00
write ( "(" ) ;
2014-12-04 01:43:01 +01:00
emitCommaList ( node . arguments ) ;
2014-10-11 01:44:14 +02:00
write ( ")" ) ;
2014-07-17 00:39:14 +02:00
}
2014-10-11 01:44:14 +02:00
}
function emitNewExpression ( node : NewExpression ) {
write ( "new " ) ;
2015-05-13 03:36:40 +02:00
2015-06-09 23:58:56 +02:00
// Spread operator logic is supported in new expressions in ES5 using a combination
2015-05-13 03:36:40 +02:00
// 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-05-13 03:36:40 +02:00
//
2015-06-09 23:58:56 +02:00
// is compiled into the following ES5:
2015-05-13 03:36:40 +02:00
//
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-05-13 03:36:40 +02:00
//
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').
2015-05-13 03:36:40 +02:00
if ( languageVersion === ScriptTarget . ES5 &&
node . arguments &&
hasSpreadElement ( node . arguments ) ) {
2014-10-11 01:44:14 +02:00
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 ) ;
2015-05-13 03:36:40 +02:00
write ( ", [void 0].concat(" ) ;
2015-11-14 02:43:53 +01:00
emitListWithSpread ( node . arguments , /*needsUniqueCopy*/ false , /*multiLine*/ false , /*trailingComma*/ false , /*useConcat*/ false ) ;
2015-05-13 03:36:40 +02:00
write ( ")))" ) ;
2015-05-15 02:16:01 +02:00
write ( "()" ) ;
2015-05-13 03:36:40 +02:00
}
else {
emit ( node . expression ) ;
if ( node . arguments ) {
write ( "(" ) ;
emitCommaList ( node . arguments ) ;
write ( ")" ) ;
}
2014-09-07 02:40:19 +02:00
}
2014-07-13 01:04:16 +02:00
}
2014-11-21 05:24:08 +01:00
function emitTaggedTemplateExpression ( node : TaggedTemplateExpression ) : void {
2015-02-13 02:54:30 +01:00
if ( languageVersion >= ScriptTarget . ES6 ) {
2015-01-19 11:33:43 +01:00
emit ( node . tag ) ;
2015-01-04 14:47:18 +01:00
write ( " " ) ;
emit ( node . template ) ;
2015-01-19 11:33:43 +01:00
}
else {
emitDownlevelTaggedTemplate ( node ) ;
2015-01-04 14:47:18 +01:00
}
2014-11-21 05:24:08 +01:00
}
2014-11-30 00:58:55 +01:00
function emitParenExpression ( node : ParenthesizedExpression ) {
2015-06-04 02:22:11 +02:00
// 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 ) {
2015-06-18 23:01:49 +02:00
if ( node . expression . kind === SyntaxKind . TypeAssertionExpression || node . expression . kind === SyntaxKind . AsExpression ) {
2015-03-13 20:34:12 +01:00
let operand = ( < TypeAssertion > 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;
2015-06-18 23:01:49 +02:00
while ( operand . kind === SyntaxKind . TypeAssertionExpression || operand . kind === SyntaxKind . AsExpression ) {
2015-03-07 11:08:36 +01:00
operand = ( < TypeAssertion > operand ) . expression ;
}
// We have an expression of the form: (<Type>SubExpr)
// Emitting this as (SubExpr) is really not desirable. We would like to emit the subexpr as is.
// Omitting the parentheses, however, could cause change in the semantics of the generated
// code if the casted expression has a lower precedence than the rest of the expression, e.g.:
// (<any>new A).foo should be emitted as (new A).foo and not new A.foo
// (<any>typeof A).toString() should be emitted as (typeof A).toString() and not typeof A.toString()
// new (<any>A()) should be emitted as new (A()) and not new A()
// (<any>function foo() { })() should be emitted as an IIF (function foo(){})() and not declaration function foo(){} ()
if ( operand . kind !== SyntaxKind . PrefixUnaryExpression &&
operand . kind !== SyntaxKind . VoidExpression &&
operand . kind !== SyntaxKind . TypeOfExpression &&
operand . kind !== SyntaxKind . DeleteExpression &&
operand . kind !== SyntaxKind . PostfixUnaryExpression &&
operand . kind !== SyntaxKind . NewExpression &&
! ( 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 ) &&
2015-09-10 21:06:13 +02:00
! ( operand . kind === SyntaxKind . NumericLiteral && node . parent . kind === SyntaxKind . PropertyAccessExpression ) ) {
2015-03-07 11:08:36 +01:00
emit ( operand ) ;
return ;
}
2014-10-11 01:44:14 +02:00
}
2014-07-17 00:39:14 +02:00
}
2015-03-07 11:08:36 +01:00
2014-10-11 01:44:14 +02:00
write ( "(" ) ;
emit ( node . expression ) ;
write ( ")" ) ;
}
2014-07-13 01:04:16 +02:00
2014-11-29 22:43:30 +01: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 ) ) {
2015-04-11 12:59:03 +02:00
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 ) ;
2015-04-11 12:59:03 +02:00
}
2014-11-30 01:48:28 +01:00
function emitPrefixUnaryExpression ( node : PrefixUnaryExpression ) {
2015-04-13 10:32:16 +02:00
const exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule ( node . operand ) ;
2015-04-11 12:59:03 +02:00
2015-04-13 10:32:16 +02:00
if ( exportChanged ) {
2015-04-21 01:56:36 +02:00
// emit
// ++x
// as
// exports('x', ++x)
2015-04-11 12:59:03 +02:00
write ( ` ${ exportFunctionForFile } (" ` ) ;
emitNodeWithoutSourceMap ( node . operand ) ;
write ( ` ", ` ) ;
}
2014-11-30 01:48:28 +01:00
write ( tokenToString ( node . operator ) ) ;
2014-10-11 01:44:14 +02:00
// 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 ;
2014-10-11 01:44:14 +02:00
if ( node . operator === SyntaxKind . PlusToken && ( operand . operator === SyntaxKind . PlusToken || operand . operator === SyntaxKind . PlusPlusToken ) ) {
write ( " " ) ;
2014-07-17 00:39:14 +02:00
}
2014-10-11 01:44:14 +02:00
else if ( node . operator === SyntaxKind . MinusToken && ( operand . operator === SyntaxKind . MinusToken || operand . operator === SyntaxKind . MinusMinusToken ) ) {
write ( " " ) ;
2014-07-17 00:39:14 +02:00
}
2014-07-13 01:04:16 +02:00
}
2014-10-11 01:44:14 +02:00
emit ( node . operand ) ;
2015-04-11 12:59:03 +02:00
2015-04-13 10:32:16 +02:00
if ( exportChanged ) {
2015-04-11 12:59:03 +02:00
write ( ")" ) ;
}
2014-07-17 00:39:14 +02:00
}
2014-07-13 01:04:16 +02:00
2014-11-30 01:48:28 +01:00
function emitPostfixUnaryExpression ( node : PostfixUnaryExpression ) {
2015-04-13 10:32:16 +02:00
const exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule ( node . operand ) ;
if ( exportChanged ) {
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.
2015-04-14 06:32:27 +02:00
// emit 'x++' as '(export('x', ++x) - 1)' and 'x--' as '(export('x', --x) + 1)'
write ( ` ( ${ exportFunctionForFile } (" ` ) ;
2015-04-11 12:59:03 +02:00
emitNodeWithoutSourceMap ( node . operand ) ;
write ( ` ", ` ) ;
2015-04-14 06:32:27 +02:00
write ( tokenToString ( node . operator ) ) ;
emit ( node . operand ) ;
2015-04-11 12:59:03 +02:00
2015-04-14 06:32:27 +02:00
if ( node . operator === SyntaxKind . PlusPlusToken ) {
write ( ") - 1)" ) ;
}
else {
write ( ") + 1)" ) ;
}
}
else {
emit ( node . operand ) ;
write ( tokenToString ( node . operator ) ) ;
2015-04-11 12:59:03 +02:00
}
}
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
2015-04-21 01:56:36 +02:00
* we we emit variable statement 'var' should be dropped .
* /
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 ( ) ) {
2015-04-11 12:59:03 +02:00
return false ;
}
2015-04-13 10:32:16 +02:00
let current : Node = node ;
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 ( isFunctionLike ( current ) || current . kind === SyntaxKind . ModuleBlock ) {
return false ;
}
else {
current = current . parent ;
}
}
2014-07-17 00:39:14 +02:00
}
2014-07-13 01:04:16 +02:00
2015-10-03 03:50:45 +02:00
/ * *
2015-10-06 03:57:11 +02:00
* Emit ES7 exponentiation operator downlevel using Math . pow
2015-10-20 19:59:23 +02:00
* @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 ;
2015-10-09 01:59:49 +02:00
let shouldEmitParentheses = false ;
2015-10-03 03:50:45 +02:00
if ( isElementAccessExpression ( leftHandSideExpression ) ) {
2015-10-09 01:59:49 +02:00
shouldEmitParentheses = true ;
write ( "(" ) ;
2015-10-06 03:57:11 +02:00
2015-10-03 03:50:45 +02:00
synthesizedLHS = < ElementAccessExpression > createSynthesizedNode ( SyntaxKind . ElementAccessExpression , /*startsOnNewLine*/ false ) ;
2015-10-09 01:59:49 +02:00
2015-11-14 02:43:53 +01:00
const identifier = emitTempVariableAssignment ( leftHandSideExpression . expression , /*canDefineTempVariablesInPlace*/ false , /*shouldEmitCommaBeforeAssignment*/ false ) ;
2015-10-09 01:59:49 +02:00
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 ) ) {
2015-10-09 01:59:49 +02:00
shouldEmitParentheses = true ;
write ( "(" ) ;
synthesizedLHS = < PropertyAccessExpression > createSynthesizedNode ( SyntaxKind . PropertyAccessExpression , /*startsOnNewLine*/ false ) ;
2015-10-06 03:57:11 +02:00
2015-11-14 02:43:53 +01:00
const identifier = emitTempVariableAssignment ( leftHandSideExpression . expression , /*canDefineTempVariablesInPlace*/ false , /*shouldEmitCommaBeforeAssignment*/ false ) ;
2015-10-09 01:59:49 +02:00
synthesizedLHS . expression = identifier ;
2015-10-06 03:57:11 +02:00
2015-10-03 03:50:45 +02:00
( < PropertyAccessExpression > synthesizedLHS ) . dotToken = leftHandSideExpression . dotToken ;
( < PropertyAccessExpression > synthesizedLHS ) . name = leftHandSideExpression . name ;
write ( ", " ) ;
}
emit ( synthesizedLHS || leftHandSideExpression ) ;
write ( " = " ) ;
write ( "Math.pow(" ) ;
emit ( synthesizedLHS || leftHandSideExpression ) ;
write ( ", " ) ;
emit ( node . right ) ;
write ( ")" ) ;
2015-10-09 01:59:49 +02:00
if ( shouldEmitParentheses ) {
2015-10-03 03:50:45 +02:00
write ( ")" ) ;
}
}
else {
write ( "Math.pow(" ) ;
emit ( leftHandSideExpression ) ;
write ( ", " ) ;
emit ( node . right ) ;
write ( ")" ) ;
}
}
2014-10-11 01:44:14 +02:00
function emitBinaryExpression ( node : BinaryExpression ) {
2015-02-21 03:56:42 +01:00
if ( languageVersion < ScriptTarget . ES6 && node . operatorToken . kind === SyntaxKind . EqualsToken &&
2014-12-14 18:43:14 +01:00
( 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 ) ;
2014-12-04 01:43:01 +01:00
}
else {
2015-04-20 19:47:17 +02:00
const exportChanged =
2015-04-21 15:49:31 +02:00
node . operatorToken . kind >= SyntaxKind . FirstAssignment &&
2015-04-13 10:32:16 +02:00
node . operatorToken . kind <= SyntaxKind . LastAssignment &&
isNameOfExportedSourceLevelDeclarationInSystemExternalModule ( node . left ) ;
2015-04-11 05:29:20 +02:00
2015-04-20 19:47:17 +02:00
if ( exportChanged ) {
2015-04-21 15:49:31 +02:00
// emit assignment 'x <op> y' as 'exports("x", x <op> y)'
2015-04-11 05:29:20 +02:00
write ( ` ${ exportFunctionForFile } (" ` ) ;
emitNodeWithoutSourceMap ( node . left ) ;
write ( ` ", ` ) ;
}
2015-09-19 03:31:52 +02:00
2015-10-09 02:12:22 +02:00
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 ) ;
2015-09-19 03:31:52 +02:00
}
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 ) ;
2015-09-19 03:31:52 +02:00
write ( tokenToString ( node . operatorToken . kind ) ) ;
2015-11-04 23:02:33 +01:00
const indentedAfterOperator = indentIfOnDifferentLines ( node , node . operatorToken , node . right , " " ) ;
2015-09-19 03:31:52 +02:00
emit ( node . right ) ;
decreaseIndentIf ( indentedBeforeOperator , indentedAfterOperator ) ;
}
2015-04-20 19:47:17 +02:00
if ( exportChanged ) {
2015-04-11 05:29:20 +02:00
write ( ")" ) ;
}
2014-12-04 01:43:01 +01:00
}
2014-07-13 01:04:16 +02:00
}
2015-02-27 00:35:17 +01:00
function synthesizedNodeStartsOnNewLine ( node : Node ) {
2015-02-27 20:28:14 +01:00
return nodeIsSynthesized ( node ) && ( < SynthesizedNode > node ) . startsOnNewLine ;
2015-02-27 00:35:17 +01:00
}
2014-10-11 01:44:14 +02:00
function emitConditionalExpression ( node : ConditionalExpression ) {
emit ( node . condition ) ;
2015-11-04 23:02:33 +01:00
const indentedBeforeQuestion = indentIfOnDifferentLines ( node , node . condition , node . questionToken , " " ) ;
2015-03-07 22:50:26 +01:00
write ( "?" ) ;
2015-11-04 23:02:33 +01:00
const indentedAfterQuestion = indentIfOnDifferentLines ( node , node . questionToken , node . whenTrue , " " ) ;
2014-10-11 01:44:14 +02:00
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 , " " ) ;
2015-03-07 22:50:26 +01:00
write ( ":" ) ;
2015-11-04 23:02:33 +01:00
const indentedAfterColon = indentIfOnDifferentLines ( node , node . colonToken , node . whenFalse , " " ) ;
2014-10-11 01:44:14 +02:00
emit ( node . whenFalse ) ;
2015-03-09 09:07:02 +01:00
decreaseIndentIf ( indentedBeforeColon , indentedAfterColon ) ;
2015-03-09 07:07:03 +01:00
}
2015-03-07 22:50:26 +01:00
2015-07-09 23:44:47 +02:00
// Helper function to decrease the indent if we previously indented. Allows multiple
2015-03-09 23:01:02 +01:00
// 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 ) {
2015-03-07 22:50:26 +01:00
decreaseIndent ( ) ;
}
2015-03-09 09:07:02 +01:00
if ( value2 ) {
2015-03-07 22:50:26 +01:00
decreaseIndent ( ) ;
}
2014-07-17 00:39:14 +02:00
}
2015-02-07 05:16:35 +01:00
function isSingleLineEmptyBlock ( node : Node ) {
2015-02-07 03:45:09 +01:00
if ( node && node . kind === SyntaxKind . Block ) {
2015-11-04 23:02:33 +01:00
const block = < Block > node ;
2015-02-07 03:45:09 +01:00
return block . statements . length === 0 && nodeEndIsOnSameLineAsNodeStart ( block , block ) ;
}
}
2014-10-11 01:44:14 +02:00
function emitBlock ( node : Block ) {
2015-03-26 21:46:35 +01:00
if ( isSingleLineEmptyBlock ( node ) ) {
2015-02-07 03:45:09 +01:00
emitToken ( SyntaxKind . OpenBraceToken , node . pos ) ;
write ( " " ) ;
emitToken ( SyntaxKind . CloseBraceToken , node . statements . end ) ;
return ;
}
2014-10-11 01:44:14 +02:00
emitToken ( SyntaxKind . OpenBraceToken , node . pos ) ;
2014-07-13 01:04:16 +02:00
increaseIndent ( ) ;
2014-10-11 01:44:14 +02:00
if ( node . kind === SyntaxKind . ModuleBlock ) {
Debug . assert ( node . parent . kind === SyntaxKind . ModuleDeclaration ) ;
emitCaptureThisForNodeIfNecessary ( node . parent ) ;
2014-07-17 00:39:14 +02:00
}
2014-10-11 01:44:14 +02:00
emitLines ( node . statements ) ;
2014-12-04 01:43:01 +01:00
if ( node . kind === SyntaxKind . ModuleBlock ) {
emitTempDeclarations ( /*newLine*/ true ) ;
}
2014-07-13 01:04:16 +02:00
decreaseIndent ( ) ;
writeLine ( ) ;
2014-10-11 01:44:14 +02:00
emitToken ( SyntaxKind . CloseBraceToken , node . statements . end ) ;
2014-08-22 02:17:02 +02:00
}
2014-10-11 01:44:14 +02:00
function emitEmbeddedStatement ( node : Node ) {
if ( node . kind === SyntaxKind . Block ) {
write ( " " ) ;
emit ( < Block > node ) ;
2014-08-22 02:17:02 +02:00
}
else {
2014-10-11 01:44:14 +02:00
increaseIndent ( ) ;
2014-07-17 00:39:14 +02:00
writeLine ( ) ;
2014-10-11 01:44:14 +02:00
emit ( node ) ;
decreaseIndent ( ) ;
2014-08-22 02:17:02 +02:00
}
2014-07-17 00:39:14 +02:00
}
2014-08-22 02:17:02 +02:00
2014-10-11 01:44:14 +02:00
function emitExpressionStatement ( node : ExpressionStatement ) {
2015-01-23 15:46:32 +01:00
emitParenthesizedIf ( node . expression , /*parenthesized*/ node . expression . kind === SyntaxKind . ArrowFunction ) ;
2014-10-11 01:44:14 +02:00
write ( ";" ) ;
2014-08-22 02:17:02 +02:00
}
2014-10-11 01:44:14 +02:00
function emitIfStatement ( node : IfStatement ) {
2015-03-13 20:34:12 +01:00
let endPos = emitToken ( SyntaxKind . IfKeyword , node . pos ) ;
2014-10-11 01:44:14 +02:00
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 ) ;
2014-08-19 19:43:13 +02:00
}
else {
2014-10-11 01:44:14 +02:00
emitEmbeddedStatement ( node . elseStatement ) ;
2014-08-19 19:43:13 +02:00
}
2014-08-16 00:52:30 +02:00
}
2014-08-12 00:54:12 +02:00
}
2014-10-11 01:44:14 +02:00
function emitDoStatement ( node : DoStatement ) {
2015-10-11 10:12:26 +02:00
emitLoop ( node , emitDoStatementWorker ) ;
}
function emitDoStatementWorker ( node : DoStatement , loop : ConvertedLoop ) {
2014-10-11 01:44:14 +02:00
write ( "do" ) ;
2015-10-11 10:12:26 +02:00
if ( loop ) {
2015-11-14 02:43:53 +01:00
emitConvertedLoopCall ( loop , /*emitAsBlock*/ true ) ;
2015-10-11 10:12:26 +02:00
}
else {
2015-11-14 02:43:53 +01:00
emitNormalLoopBody ( node , /*emitAsEmbeddedStatement*/ true ) ;
2015-10-11 10:12:26 +02:00
}
2014-10-11 01:44:14 +02:00
if ( node . statement . kind === SyntaxKind . Block ) {
write ( " " ) ;
}
else {
2014-07-17 00:39:14 +02:00
writeLine ( ) ;
}
2014-10-11 01:44:14 +02:00
write ( "while (" ) ;
emit ( node . expression ) ;
write ( ");" ) ;
2014-08-22 23:40:47 +02:00
}
2014-10-11 01:44:14 +02:00
function emitWhileStatement ( node : WhileStatement ) {
2015-10-11 10:12:26 +02:00
emitLoop ( node , emitWhileStatementWorker ) ;
}
function emitWhileStatementWorker ( node : WhileStatement , loop : ConvertedLoop ) {
2014-10-11 01:44:14 +02:00
write ( "while (" ) ;
emit ( node . expression ) ;
write ( ")" ) ;
2015-10-11 10:12:26 +02:00
if ( loop ) {
2015-11-14 02:43:53 +01:00
emitConvertedLoopCall ( loop , /*emitAsBlock*/ true ) ;
2015-10-11 10:12:26 +02:00
}
else {
2015-11-14 02:43:53 +01:00
emitNormalLoopBody ( node , /*emitAsEmbeddedStatement*/ true ) ;
2015-10-11 10:12:26 +02:00
}
2014-10-11 01:44:14 +02:00
}
2015-05-16 01:32:11 +02:00
/ * *
* 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 module s where such variable declarations are hoisted .
2015-04-22 07:27:33 +02:00
* /
2015-12-10 21:29:14 +01:00
function tryEmitStartOfVariableDeclarationList ( decl : VariableDeclarationList ) : boolean {
2015-04-21 17:31:14 +02:00
if ( shouldHoistVariable ( decl , /*checkIfSourceFileLevelDecl*/ true ) ) {
// variables in variable declaration list were already hoisted
2015-04-11 17:23:25 +02:00
return false ;
2015-04-10 21:10:38 +02:00
}
2015-04-13 10:32:16 +02:00
2015-10-11 10:12:26 +02:00
if ( convertedLoopState && ( getCombinedNodeFlags ( decl ) & NodeFlags . BlockScoped ) === 0 ) {
2015-11-17 19:53:29 +01:00
// we are inside a converted loop - this can only happen in downlevel scenarios
2015-10-11 10:12:26 +02:00
// record names for all variable declarations
for ( const varDecl of decl . declarations ) {
hoistVariableDeclarationFromLoop ( convertedLoopState , varDecl ) ;
}
return false ;
}
2015-12-10 21:29:14 +01:00
emitStart ( decl ) ;
2015-02-14 09:48:46 +01:00
if ( decl && languageVersion >= ScriptTarget . ES6 ) {
if ( isLet ( decl ) ) {
2015-12-10 21:29:14 +01:00
write ( "let " ) ;
2015-02-14 09:48:46 +01:00
}
else if ( isConst ( decl ) ) {
2015-12-10 21:29:14 +01:00
write ( "const " ) ;
}
else {
write ( "var " ) ;
2015-02-14 09:48:46 +01:00
}
}
else {
2015-12-10 21:29:14 +01:00
write ( "var " ) ;
2015-02-14 09:48:46 +01:00
}
2015-04-11 17:23:25 +02:00
return true ;
}
function emitVariableDeclarationListSkippingUninitializedEntries ( list : VariableDeclarationList ) : boolean {
let started = false ;
2015-11-04 23:02:33 +01:00
for ( const decl of list . declarations ) {
2015-04-11 17:23:25 +02:00
if ( ! decl . initializer ) {
continue ;
}
if ( ! started ) {
started = true ;
2015-02-14 09:48:46 +01:00
}
2015-04-11 17:23:25 +02:00
else {
write ( ", " ) ;
}
emit ( decl ) ;
2015-02-14 09:48:46 +01:00
}
2015-04-11 17:23:25 +02:00
return started ;
2015-02-14 09:48:46 +01:00
}
2015-10-11 10:12:26 +02:00
interface ConvertedLoop {
functionName : string ;
paramList : string ;
state : ConvertedLoopState ;
}
function shouldConvertLoopBody ( node : IterationStatement ) : boolean {
return languageVersion < ScriptTarget . ES6 &&
( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . LoopWithBlockScopedBindingCapturedInFunction ) !== 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 :
if ( ( < ForStatement | ForInStatement | ForOfStatement > node ) . initializer . kind === SyntaxKind . VariableDeclarationList ) {
loopInitializer = < VariableDeclarationList > ( < ForStatement | ForInStatement | ForOfStatement > node ) . initializer ;
}
break ;
}
let loopParameters : string [ ] ;
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 ) {
collectNames ( varDeclaration . name ) ;
}
}
const bodyIsBlock = node . statement . kind === SyntaxKind . Block ;
const paramList = loopParameters ? loopParameters . join ( ", " ) : "" ;
writeLine ( ) ;
write ( ` var ${ functionName } = function( ${ paramList } ) ` ) ;
if ( ! bodyIsBlock ) {
write ( " {" ) ;
writeLine ( ) ;
increaseIndent ( ) ;
}
const convertedOuterLoopState = convertedLoopState ;
convertedLoopState = { } ;
if ( convertedOuterLoopState ) {
2015-10-19 23:10:27 +02:00
// convertedOuterLoopState !== undefined means that this converted loop is nested in another converted loop.
2015-10-11 10:12:26 +02:00
// 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 ;
}
2015-11-22 05:11:39 +01:00
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 ;
}
2015-10-11 10:12:26 +02:00
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 ;
}
}
emitEmbeddedStatement ( node . statement ) ;
if ( ! bodyIsBlock ) {
decreaseIndent ( ) ;
writeLine ( ) ;
write ( "}" ) ;
}
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
2015-10-11 10:12:26 +02:00
write ( ` var ${ convertedLoopState . argumentsName } = arguments; ` ) ;
writeLine ( ) ;
}
}
2015-11-22 05:11:39 +01:00
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:
2015-11-22 05:11:39 +01:00
// 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 ( ) ;
}
}
2015-10-11 10:12:26 +02:00
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 ) {
2015-10-19 23:10:27 +02:00
// Don't initialize seen unless we have at least one element.
// Emit a comma to separate for all but the first element.
2015-10-11 10:12:26 +02:00
if ( ! seen ) {
seen = { } ;
}
else {
write ( ", " ) ;
}
2015-10-19 23:10:27 +02:00
2015-10-11 10:12:26 +02:00
if ( ! hasProperty ( seen , id . text ) ) {
emit ( id ) ;
seen [ id . text ] = id . text ;
}
}
write ( ";" ) ;
writeLine ( ) ;
}
}
const currentLoopState = convertedLoopState ;
convertedLoopState = convertedOuterLoopState ;
return { functionName , paramList , state : currentLoopState } ;
function collectNames ( name : Identifier | BindingPattern ) : void {
if ( name . kind === SyntaxKind . Identifier ) {
const nameText = isNameOfNestedRedeclaration ( < Identifier > name ) ? getGeneratedNameForNode ( name ) : ( < Identifier > name ) . text ;
loopParameters . push ( nameText ) ;
}
else {
for ( const element of ( < BindingPattern > name ) . elements ) {
collectNames ( 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 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()';
const isSimpleLoop =
! loop . state . nonLocalJumps &&
! loop . state . labeledNonLocalBreaks &&
! loop . state . labeledNonLocalContinues ;
const loopResult = makeUniqueName ( "state" ) ;
if ( ! isSimpleLoop ) {
write ( ` var ${ loopResult } = ` ) ;
}
write ( ` ${ loop . functionName } ( ${ loop . paramList } ); ` ) ;
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 ( ) ;
}
if ( loop . state . nonLocalJumps & Jump . Continue ) {
write ( ` if ( ${ loopResult } === "continue") continue; ` ) ;
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 ) ;
}
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 ) ;
2015-10-11 10:12:26 +02:00
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 ] ;
2015-10-11 10:12:26 +02:00
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
2015-11-17 19:53:29 +01:00
// otherwise propagate pair 'label -> marker' to outer converted loop and emit 'return labelMarker' so outer loop can later decide what to do
2015-10-11 10:12:26 +02:00
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 } ; ` ) ;
}
}
}
}
2014-10-11 01:44:14 +02:00
function emitForStatement ( node : ForStatement ) {
2015-10-11 10:12:26 +02:00
emitLoop ( node , emitForStatementWorker ) ;
}
function emitForStatementWorker ( node : ForStatement , loop : ConvertedLoop ) {
2015-03-13 20:34:12 +01:00
let endPos = emitToken ( SyntaxKind . ForKeyword , node . pos ) ;
2014-10-11 01:44:14 +02:00
write ( " " ) ;
endPos = emitToken ( SyntaxKind . OpenParenToken , endPos ) ;
2014-12-16 10:09:42 +01:00
if ( node . initializer && node . initializer . kind === SyntaxKind . VariableDeclarationList ) {
2015-11-04 23:02:33 +01:00
const variableDeclarationList = < VariableDeclarationList > node . initializer ;
2015-12-10 21:29:14 +01:00
const startIsEmitted = tryEmitStartOfVariableDeclarationList ( variableDeclarationList ) ;
2015-04-11 17:23:25 +02:00
if ( startIsEmitted ) {
2015-04-21 17:31:14 +02:00
emitCommaList ( variableDeclarationList . declarations ) ;
2015-04-11 17:23:25 +02:00
}
else {
emitVariableDeclarationListSkippingUninitializedEntries ( variableDeclarationList ) ;
}
2014-10-11 01:44:14 +02:00
}
2014-12-16 10:09:42 +01:00
else if ( node . initializer ) {
2014-10-11 01:44:14 +02:00
emit ( node . initializer ) ;
2014-08-15 01:32:21 +02:00
}
2014-10-11 01:44:14 +02:00
write ( ";" ) ;
emitOptional ( " " , node . condition ) ;
write ( ";" ) ;
2015-04-22 22:29:50 +02:00
emitOptional ( " " , node . incrementor ) ;
2014-10-11 01:44:14 +02:00
write ( ")" ) ;
2015-10-11 10:12:26 +02:00
if ( loop ) {
2015-11-14 02:43:53 +01:00
emitConvertedLoopCall ( loop , /*emitAsBlock*/ true ) ;
2015-10-11 10:12:26 +02:00
}
else {
2015-11-14 02:43:53 +01:00
emitNormalLoopBody ( node , /*emitAsEmbeddedStatement*/ true ) ;
2015-10-11 10:12:26 +02:00
}
2014-08-12 00:54:12 +02:00
}
2015-02-06 01:19:04 +01:00
function emitForInOrForOfStatement ( node : ForInStatement | ForOfStatement ) {
2015-03-03 20:43:32 +01:00
if ( languageVersion < ScriptTarget . ES6 && node . kind === SyntaxKind . ForOfStatement ) {
2015-10-11 10:12:26 +02:00
emitLoop ( node , emitDownLevelForOfStatementWorker ) ;
2015-03-03 20:43:32 +01:00
}
2015-10-11 10:12:26 +02:00
else {
emitLoop ( node , emitForInOrForOfStatementWorker ) ;
2015-03-03 20:43:32 +01:00
}
2015-10-11 10:12:26 +02:00
}
2015-03-23 19:06:44 +01:00
2015-10-11 10:12:26 +02:00
function emitForInOrForOfStatementWorker ( node : ForInStatement | ForOfStatement , loop : ConvertedLoop ) {
2015-03-13 20:34:12 +01:00
let endPos = emitToken ( SyntaxKind . ForKeyword , node . pos ) ;
2014-10-11 01:44:14 +02:00
write ( " " ) ;
endPos = emitToken ( SyntaxKind . OpenParenToken , endPos ) ;
2014-12-16 10:09:42 +01:00
if ( node . initializer . kind === SyntaxKind . VariableDeclarationList ) {
2015-11-04 23:02:33 +01:00
const variableDeclarationList = < VariableDeclarationList > node . initializer ;
2014-12-16 10:09:42 +01:00
if ( variableDeclarationList . declarations . length >= 1 ) {
2015-12-10 21:29:14 +01:00
tryEmitStartOfVariableDeclarationList ( variableDeclarationList ) ;
2015-04-21 17:31:14 +02:00
emit ( variableDeclarationList . declarations [ 0 ] ) ;
2014-11-21 05:24:08 +01:00
}
2014-08-22 02:17:02 +02:00
}
else {
2014-12-16 10:09:42 +01:00
emit ( node . initializer ) ;
2014-08-22 02:17:02 +02:00
}
2015-02-06 01:19:04 +01:00
if ( node . kind === SyntaxKind . ForInStatement ) {
write ( " in " ) ;
}
else {
write ( " of " ) ;
}
2014-10-11 01:44:14 +02:00
emit ( node . expression ) ;
emitToken ( SyntaxKind . CloseParenToken , node . expression . end ) ;
2015-10-11 10:12:26 +02:00
if ( loop ) {
2015-11-14 02:43:53 +01:00
emitConvertedLoopCall ( loop , /*emitAsBlock*/ true ) ;
2015-10-11 10:12:26 +02:00
}
else {
2015-11-14 02:43:53 +01:00
emitNormalLoopBody ( node , /*emitAsEmbeddedStatement*/ true ) ;
2015-10-11 10:12:26 +02:00
}
2014-08-22 02:17:02 +02:00
}
2015-03-23 19:06:44 +01:00
2015-03-03 20:43:32 +01:00
function emitDownLevelForOfStatement ( node : ForOfStatement ) {
2015-10-11 10:12:26 +02:00
emitLoop ( node , emitDownLevelForOfStatementWorker ) ;
}
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.
2015-03-05 01:06:37 +01:00
// 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) { }
2015-03-05 01:06:37 +01:00
//
2015-10-30 23:59:16 +01:00
// 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 ;
2015-10-30 23:59:16 +01:00
rhsReference . text = node . expression . kind === SyntaxKind . Identifier ?
makeUniqueName ( ( < Identifier > node . expression ) . text ) :
makeTempVariableName ( TempFlags . Auto ) ;
2015-03-05 20:47:40 +01:00
2015-03-13 20:34:12 +01:00
// This is the let keyword for the counter and rhsReference. The let keyword for
2015-03-05 20:47:40 +01:00
// the LHS will be emitted inside the body.
emitStart ( node . expression ) ;
write ( "var " ) ;
2015-06-26 01:24:41 +02:00
2015-03-05 01:06:37 +01:00
// _i = 0
2015-03-10 18:41:26 +01:00
emitNodeWithoutSourceMap ( counter ) ;
2015-03-05 01:06:37 +01:00
write ( " = 0" ) ;
2015-03-05 20:47:40 +01:00
emitEnd ( node . expression ) ;
2015-03-23 19:06:44 +01:00
2015-10-30 23:59:16 +01:00
// , _a = expr
write ( ", " ) ;
emitStart ( node . expression ) ;
emitNodeWithoutSourceMap ( rhsReference ) ;
write ( " = " ) ;
emitNodeWithoutSourceMap ( node . expression ) ;
emitEnd ( node . expression ) ;
2015-03-14 00:15:25 +01:00
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;
2015-12-08 22:27:17 +01:00
emitStart ( node . expression ) ;
2015-03-10 18:41:26 +01:00
emitNodeWithoutSourceMap ( counter ) ;
2015-03-03 20:43:32 +01:00
write ( " < " ) ;
2015-03-14 00:15:25 +01:00
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( rhsReference ) ;
2015-03-29 08:50:52 +02:00
write ( ".length" ) ;
2015-03-14 00:15:25 +01:00
2015-12-08 22:27:17 +01:00
emitEnd ( node . expression ) ;
2015-03-05 20:47:40 +01:00
write ( "; " ) ;
2015-06-26 01:24:41 +02:00
2015-03-03 20:43:32 +01:00
// _i++)
2015-12-08 22:27:17 +01:00
emitStart ( node . expression ) ;
2015-03-10 18:41:26 +01:00
emitNodeWithoutSourceMap ( counter ) ;
2015-03-03 20:43:32 +01:00
write ( "++" ) ;
2015-12-08 22:27:17 +01:00
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-12-08 22:27:17 +01:00
const rhsIterationValue = createElementAccessExpression ( rhsReference , counter , node . initializer ) ;
2015-03-05 20:47:40 +01:00
emitStart ( node . initializer ) ;
2015-03-04 21:10:20 +01:00
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 ] ;
2015-03-04 21:10:20 +01:00
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 ) ;
2015-03-04 21:10:20 +01:00
}
else {
// The following call does not include the initializer, so we have
// to emit it separately.
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( declaration ) ;
2015-03-04 21:10:20 +01:00
write ( " = " ) ;
2015-03-10 18:41:26 +01:00
emitNodeWithoutSourceMap ( rhsIterationValue ) ;
2015-03-04 21:10:20 +01:00
}
}
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 []) {}
2015-03-26 18:51:07 +01:00
emitNodeWithoutSourceMap ( createTempVariable ( TempFlags . Auto ) ) ;
2015-03-04 21:10:20 +01:00
write ( " = " ) ;
2015-03-10 18:41:26 +01:00
emitNodeWithoutSourceMap ( rhsIterationValue ) ;
2015-03-04 21:10:20 +01:00
}
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 ) ;
2015-03-04 21:10:20 +01:00
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.
2015-04-11 03:12:48 +02:00
emitDestructuring ( assignmentExpression , /*isAssignmentExpressionStatement*/ true , /*value*/ undefined ) ;
2015-03-04 21:10:20 +01:00
}
else {
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( assignmentExpression ) ;
2015-03-04 21:10:20 +01:00
}
2015-03-03 23:45:16 +01:00
}
2015-03-05 20:47:40 +01:00
emitEnd ( node . initializer ) ;
2015-03-04 21:10:20 +01:00
write ( ";" ) ;
2015-03-23 19:06:44 +01:00
2015-10-11 10:12:26 +02:00
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-23 19:06:44 +01:00
2015-03-03 20:43:32 +01:00
writeLine ( ) ;
decreaseIndent ( ) ;
write ( "}" ) ;
}
2014-08-22 02:17:02 +02:00
2014-10-11 01:44:14 +02:00
function emitBreakOrContinueStatement ( node : BreakOrContinueStatement ) {
2015-10-11 10:12:26 +02:00
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 ) {
if ( ! node . label ) {
if ( node . kind === SyntaxKind . BreakStatement ) {
convertedLoopState . nonLocalJumps |= Jump . Break ;
write ( ` return "break"; ` ) ;
}
else {
convertedLoopState . nonLocalJumps |= Jump . Continue ;
write ( ` return "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 ) ;
2015-10-11 10:12:26 +02:00
}
else {
labelMarker = ` continue- ${ node . label . text } ` ;
2015-11-14 02:43:53 +01:00
setLabeledJump ( convertedLoopState , /*isBreak*/ false , node . label . text , labelMarker ) ;
2015-10-11 10:12:26 +02:00
}
write ( ` return " ${ labelMarker } "; ` ) ;
}
return ;
}
}
2014-10-11 01:44:14 +02:00
emitToken ( node . kind === SyntaxKind . BreakStatement ? SyntaxKind.BreakKeyword : SyntaxKind.ContinueKeyword , node . pos ) ;
emitOptional ( " " , node . label ) ;
write ( ";" ) ;
}
2014-08-19 19:43:13 +02:00
2014-10-11 01:44:14 +02:00
function emitReturnStatement ( node : ReturnStatement ) {
2015-10-11 10:12:26 +02:00
if ( convertedLoopState ) {
convertedLoopState . nonLocalJumps |= Jump . Return ;
2015-10-19 23:10:27 +02:00
write ( "return { value: " ) ;
2015-10-11 10:12:26 +02:00
if ( node . expression ) {
emit ( node . expression ) ;
}
else {
2015-10-19 23:10:27 +02:00
write ( "void 0" ) ;
2015-10-11 10:12:26 +02:00
}
write ( " };" ) ;
return ;
}
2014-10-11 01:44:14 +02:00
emitToken ( SyntaxKind . ReturnKeyword , node . pos ) ;
emitOptional ( " " , node . expression ) ;
write ( ";" ) ;
}
2014-08-19 19:43:13 +02:00
2015-05-21 22:02:02 +02:00
function emitWithStatement ( node : WithStatement ) {
2014-10-11 01:44:14 +02:00
write ( "with (" ) ;
emit ( node . expression ) ;
write ( ")" ) ;
emitEmbeddedStatement ( node . statement ) ;
}
2014-08-19 19:43:13 +02:00
2014-10-11 01:44:14 +02:00
function emitSwitchStatement ( node : SwitchStatement ) {
2015-03-13 20:34:12 +01:00
let endPos = emitToken ( SyntaxKind . SwitchKeyword , node . pos ) ;
2014-10-11 01:44:14 +02:00
write ( " " ) ;
emitToken ( SyntaxKind . OpenParenToken , endPos ) ;
emit ( node . expression ) ;
endPos = emitToken ( SyntaxKind . CloseParenToken , node . expression . end ) ;
write ( " " ) ;
2015-10-11 10:12:26 +02:00
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 ) ;
2015-10-11 10:12:26 +02:00
if ( convertedLoopState ) {
convertedLoopState . allowedNonLabeledJumps = saveAllowedNonLabeledJumps ;
}
2015-03-11 02:17:52 +01:00
}
function emitCaseBlock ( node : CaseBlock , startPos : number ) : void {
emitToken ( SyntaxKind . OpenBraceToken , startPos ) ;
2014-10-11 01:44:14 +02:00
increaseIndent ( ) ;
emitLines ( node . clauses ) ;
decreaseIndent ( ) ;
2014-08-21 22:44:26 +02:00
writeLine ( ) ;
2014-10-11 01:44:14 +02:00
emitToken ( SyntaxKind . CloseBraceToken , node . clauses . end ) ;
}
2014-08-19 19:43:13 +02:00
2015-02-07 05:16:35 +01:00
function nodeStartPositionsAreOnSameLine ( node1 : Node , node2 : Node ) {
2015-11-07 01:21:43 +01:00
return getLineOfLocalPositionFromLineMap ( currentLineMap , skipTrivia ( currentText , node1 . pos ) ) ===
getLineOfLocalPositionFromLineMap ( currentLineMap , skipTrivia ( currentText , node2 . pos ) ) ;
2015-02-07 05:16:35 +01:00
}
function nodeEndPositionsAreOnSameLine ( node1 : Node , node2 : Node ) {
2015-11-07 01:21:43 +01:00
return getLineOfLocalPositionFromLineMap ( currentLineMap , node1 . end ) ===
getLineOfLocalPositionFromLineMap ( currentLineMap , node2 . end ) ;
2014-08-19 19:43:13 +02:00
}
2015-02-07 03:45:09 +01:00
function nodeEndIsOnSameLineAsNodeStart ( node1 : Node , node2 : Node ) {
2015-11-07 01:21:43 +01:00
return getLineOfLocalPositionFromLineMap ( currentLineMap , node1 . end ) ===
getLineOfLocalPositionFromLineMap ( currentLineMap , skipTrivia ( currentText , node2 . pos ) ) ;
2015-02-07 03:45:09 +01:00
}
2014-10-11 01:44:14 +02:00
function emitCaseOrDefaultClause ( node : CaseOrDefaultClause ) {
if ( node . kind === SyntaxKind . CaseClause ) {
write ( "case " ) ;
2014-12-02 07:50:03 +01:00
emit ( ( < CaseClause > node ) . expression ) ;
2014-10-11 01:44:14 +02:00
write ( ":" ) ;
}
else {
write ( "default:" ) ;
2014-08-22 23:35:07 +02:00
}
2015-03-09 02:23:23 +01:00
2015-03-26 21:46:35 +01:00
if ( node . statements . length === 1 && nodeStartPositionsAreOnSameLine ( node , node . statements [ 0 ] ) ) {
2014-11-21 05:24:08 +01:00
write ( " " ) ;
emit ( node . statements [ 0 ] ) ;
}
else {
increaseIndent ( ) ;
emitLines ( node . statements ) ;
decreaseIndent ( ) ;
2014-08-19 19:43:13 +02:00
}
}
2014-10-11 01:44:14 +02:00
function emitThrowStatement ( node : ThrowStatement ) {
write ( "throw " ) ;
emit ( node . expression ) ;
write ( ";" ) ;
}
2014-08-22 23:40:47 +02:00
2014-10-11 01:44:14 +02:00
function emitTryStatement ( node : TryStatement ) {
write ( "try " ) ;
emit ( node . tryBlock ) ;
2014-12-02 08:17:34 +01:00
emit ( node . catchClause ) ;
2014-10-11 01:44:14 +02:00
if ( node . finallyBlock ) {
writeLine ( ) ;
write ( "finally " ) ;
emit ( node . finallyBlock ) ;
2014-08-22 23:40:47 +02:00
}
}
2014-12-02 08:17:34 +01:00
function emitCatchClause ( node : CatchClause ) {
2014-10-11 01:44:14 +02:00
writeLine ( ) ;
2015-11-04 23:02:33 +01:00
const endPos = emitToken ( SyntaxKind . CatchKeyword , node . pos ) ;
2014-10-11 01:44:14 +02:00
write ( " " ) ;
emitToken ( SyntaxKind . OpenParenToken , endPos ) ;
2015-02-27 02:19:19 +01:00
emit ( node . variableDeclaration ) ;
emitToken ( SyntaxKind . CloseParenToken , node . variableDeclaration ? node.variableDeclaration.end : endPos ) ;
2014-10-11 01:44:14 +02:00
write ( " " ) ;
2014-12-02 08:17:34 +01:00
emitBlock ( node . block ) ;
2014-07-17 00:39:14 +02:00
}
2014-10-11 01:44:14 +02:00
function emitDebuggerStatement ( node : Node ) {
emitToken ( SyntaxKind . DebuggerKeyword , node . pos ) ;
write ( ";" ) ;
2014-07-13 01:04:16 +02:00
}
2014-07-17 00:39:14 +02:00
2015-10-11 10:12:26 +02:00
function emitLabelAndColon ( node : LabeledStatement ) : void {
2014-10-11 01:44:14 +02:00
emit ( node . label ) ;
write ( ": " ) ;
2015-10-11 10:12:26 +02:00
}
2015-10-20 00:51:20 +02:00
function emitLabeledStatement ( node : LabeledStatement ) {
2015-10-11 10:12:26 +02:00
if ( ! isIterationStatement ( node . statement , /* lookInLabeledStatements */ false ) || ! shouldConvertLoopBody ( < IterationStatement > node . statement ) ) {
emitLabelAndColon ( node ) ;
}
if ( convertedLoopState ) {
if ( ! convertedLoopState . labels ) {
convertedLoopState . labels = { } ;
}
convertedLoopState . labels [ node . label . text ] = node . label . text ;
}
2014-10-11 01:44:14 +02:00
emit ( node . statement ) ;
2015-10-11 10:12:26 +02:00
if ( convertedLoopState ) {
convertedLoopState . labels [ node . label . text ] = undefined ;
}
2014-07-13 01:04:16 +02:00
}
2014-10-11 01:44:14 +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
}
2014-10-11 01:44:14 +02: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 ) ;
2015-02-17 22:47:15 +01:00
if ( container ) {
2015-03-24 00:16:29 +01:00
write ( getGeneratedNameForNode ( container ) ) ;
2015-02-17 22:47:15 +01:00
write ( "." ) ;
}
2015-09-17 22:26:04 +02:00
else if ( module kind !== ModuleKind . ES6 && module kind !== ModuleKind . System ) {
2015-02-17 22:47:15 +01:00
write ( "exports." ) ;
}
2014-07-17 00:39:14 +02:00
}
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( node . name ) ;
2014-10-11 01:44:14 +02:00
emitEnd ( node . name ) ;
2014-07-17 00:39:14 +02:00
}
2015-03-23 19:06:44 +01:00
2015-02-16 22:39:32 +01:00
function createVoidZero ( ) : Expression {
2015-11-04 23:02:33 +01:00
const zero = < LiteralExpression > createSynthesizedNode ( SyntaxKind . NumericLiteral ) ;
2015-02-16 22:39:32 +01:00
zero . text = "0" ;
2015-11-04 23:02:33 +01:00
const result = < VoidExpression > createSynthesizedNode ( SyntaxKind . VoidExpression ) ;
2015-02-16 22:39:32 +01:00
result . expression = zero ;
return result ;
}
2014-08-08 22:56:53 +02:00
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 ( module kind === ModuleKind . CommonJS || module kind === ModuleKind . AMD || module kind === ModuleKind . UMD ) {
2015-11-07 01:21:43 +01:00
if ( ! isEs6Module ) {
2015-12-03 05:50:24 +01:00
if ( languageVersion !== ScriptTarget . ES3 ) {
2015-07-27 13:52:57 +02:00
// default value of configurable, enumerable, writable are `false`.
2015-07-11 13:55:27 +02:00
write ( "Object.defineProperty(exports, \"__esModule\", { value: true });" ) ;
writeLine ( ) ;
}
2015-12-03 05:50:24 +01:00
else {
2015-07-11 13:55:27 +02:00
write ( "exports.__esModule = true;" ) ;
writeLine ( ) ;
}
2015-06-24 19:45:46 +02:00
}
}
}
}
2015-03-21 21:12:39 +01: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 ( module kind === 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-17 19:29:05 +02:00
}
2015-04-10 21:10:38 +02:00
else {
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( node . name ) ;
2015-04-10 21:10:38 +02:00
}
write ( ` ", ` ) ;
emitDeclarationName ( node ) ;
2015-06-26 01:24:41 +02:00
write ( ")" ) ;
2015-03-21 21:12:39 +01:00
}
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 ) ;
2015-03-21 21:12:39 +01:00
}
emitEnd ( node ) ;
write ( ";" ) ;
}
}
2015-02-13 19:07:10 +01:00
function emitExportMemberAssignments ( name : Identifier ) {
2015-09-17 22:26:04 +02:00
if ( module kind === ModuleKind . System ) {
2015-08-06 06:45:04 +02:00
return ;
}
2015-10-01 00:45:23 +02:00
2015-03-21 21:12:39 +01:00
if ( ! exportEquals && exportSpecifiers && hasProperty ( exportSpecifiers , name . text ) ) {
2015-11-04 23:02:33 +01:00
for ( const specifier of exportSpecifiers [ name . text ] ) {
2015-02-13 19:07:10 +01:00
writeLine ( ) ;
2015-08-06 06:45:04 +02:00
emitStart ( specifier . name ) ;
emitContainingModuleName ( specifier ) ;
write ( "." ) ;
2015-08-17 20:26:49 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( specifier . name ) ;
2015-08-06 06:45:04 +02:00
emitEnd ( specifier . name ) ;
write ( " = " ) ;
emitExpressionIdentifier ( name ) ;
2015-02-13 19:07:10 +01:00
write ( ";" ) ;
2015-03-21 21:12:39 +01:00
}
2015-02-13 03:05:02 +01:00
}
}
2015-10-01 00:45:23 +02:00
2015-08-06 06:45:04 +02:00
function emitExportSpecifierInSystemModule ( specifier : ExportSpecifier ) : void {
2015-09-17 22:26:04 +02:00
Debug . assert ( module kind === ModuleKind . System ) ;
2015-09-11 18:36:17 +02:00
if ( ! resolver . getReferencedValueDeclaration ( specifier . propertyName || specifier . name ) && ! resolver . isValueAliasDeclaration ( specifier ) ) {
return ;
}
2015-10-01 00:45:23 +02:00
2015-08-06 06:45:04 +02:00
writeLine ( ) ;
emitStart ( specifier . name ) ;
write ( ` ${ exportFunctionForFile } (" ` ) ;
2015-08-17 20:26:49 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( specifier . name ) ;
2015-08-06 06:45:04 +02:00
write ( ` ", ` ) ;
emitExpressionIdentifier ( specifier . propertyName || specifier . name ) ;
write ( ")" ) ;
emitEnd ( specifier . name ) ;
write ( ";" ) ;
}
2015-02-16 22:39:32 +01:00
2015-10-09 01:59:49 +02:00
/ * *
* 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
* /
2015-12-03 23:20:25 +01:00
function emitAssignment ( name : Identifier , value : Expression , shouldEmitCommaBeforeAssignment : boolean , nodeForSourceMap : TextRange ) {
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 ) ;
2015-12-03 23:20:25 +01:00
emitStart ( nodeForSourceMap ) ;
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 ( ")" ) ;
}
}
2015-10-09 01:59:49 +02:00
/ * *
* 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 ) : Identifier {
2015-11-04 23:02:33 +01:00
const identifier = createTempVariable ( TempFlags . Auto ) ;
2015-10-09 01:59:49 +02:00
if ( ! canDefineTempVariablesInPlace ) {
recordTempDeclaration ( identifier ) ;
}
2015-12-09 01:36:19 +01:00
emitAssignment ( identifier , expression , shouldEmitCommaBeforeAssignment , expression . parent || expression ) ;
2015-10-09 01:59:49 +02:00
return identifier ;
2015-10-12 20:48:38 +02:00
}
2015-10-09 01:59:49 +02:00
2015-04-11 03:12:48 +02:00
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
2014-12-06 22:53:06 +01:00
// 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 ) ;
2014-12-04 01:43:01 +01:00
}
else {
2015-03-09 21:32:02 +01:00
Debug . assert ( ! isAssignmentExpressionStatement ) ;
2015-12-10 21:29:14 +01:00
// If first variable declaration of variable statement correct the start location
if ( root . kind === SyntaxKind . VariableDeclaration &&
root . parent . kind === SyntaxKind . VariableDeclarationList &&
( < VariableDeclarationList > root . parent ) . declarations [ 0 ] === root ) {
// Use emit location of "var " as next emit start entry
sourceMap . changeEmitSourcePos ( ) ;
}
2014-12-04 01:43:01 +01:00
emitBindingElement ( < BindingElement > root , value ) ;
}
2015-09-05 02:53:56 +02:00
/ * *
* 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 ) : Expression {
if ( expr . kind === SyntaxKind . Identifier && reuseIdentifierExpressions ) {
return expr ;
2014-12-04 01:43:01 +01:00
}
2015-09-05 02:53:56 +02:00
2015-11-04 23:02:33 +01:00
const identifier = emitTempVariableAssignment ( expr , canDefineTempVariablesInPlace , emitCount > 0 ) ;
2015-10-08 00:26:09 +02:00
emitCount ++ ;
2015-09-05 02:53:56 +02:00
return identifier ;
2014-12-04 01:43:01 +01:00
}
function createDefaultValueCheck ( value : Expression , defaultValue : Expression ) : Expression {
// The value expression will be evaluated twice, so for anything but a simple identifier
// we need to generate a temporary variable
2015-09-05 02:53:56 +02:00
value = ensureIdentifier ( value , /*reuseIdentifierExpressions*/ true ) ;
2014-12-04 01:43:01 +01:00
// Return the expression 'value === void 0 ? defaultValue : value'
2015-11-04 23:02:33 +01:00
const equals = < BinaryExpression > createSynthesizedNode ( SyntaxKind . BinaryExpression ) ;
2014-12-04 01:43:01 +01:00
equals . left = value ;
2015-02-26 09:04:44 +01:00
equals . operatorToken = createSynthesizedNode ( SyntaxKind . EqualsEqualsEqualsToken ) ;
2014-12-04 01:43:01 +01:00
equals . right = createVoidZero ( ) ;
2015-03-07 22:50:26 +01:00
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 ) ;
2015-03-07 22:50:26 +01:00
cond . condition = condition ;
cond . questionToken = createSynthesizedNode ( SyntaxKind . QuestionToken ) ;
cond . whenTrue = whenTrue ;
cond . colonToken = createSynthesizedNode ( SyntaxKind . ColonToken ) ;
cond . whenFalse = whenFalse ;
2014-12-04 01:43:01 +01:00
return cond ;
}
function createNumericLiteral ( value : number ) {
2015-11-04 23:02:33 +01:00
const node = < LiteralExpression > createSynthesizedNode ( SyntaxKind . NumericLiteral ) ;
2014-12-04 01:43:01 +01:00
node . text = "" + value ;
return node ;
}
2015-12-10 21:29:14 +01:00
function createPropertyAccessForDestructuringProperty ( object : Expression , propName : PropertyName , sourceMapNode : Node ) : Expression {
2015-11-05 00:35:21 +01:00
let index : Expression ;
const nameIsComputed = propName . kind === SyntaxKind . ComputedPropertyName ;
if ( nameIsComputed ) {
2015-11-14 02:43:53 +01:00
index = ensureIdentifier ( ( < ComputedPropertyName > propName ) . expression , /*reuseIdentifierExpressions*/ false ) ;
2015-11-05 00:35:21 +01:00
}
else {
// We create a synthetic copy of the identifier in order to avoid the rewriting that might
// otherwise occur when the identifier is emitted.
2015-12-10 21:29:14 +01:00
index = < Identifier | LiteralExpression > createSourceMappedSynthesizedNode ( propName . kind , sourceMapNode ) ;
2015-11-05 00:35:21 +01:00
( < Identifier | LiteralExpression > index ) . text = ( < Identifier | LiteralExpression > propName ) . text ;
2014-12-04 01:43:01 +01:00
}
2015-11-05 00:35:21 +01:00
return ! nameIsComputed && index . kind === SyntaxKind . Identifier
? createPropertyAccessExpression ( object , < Identifier > index )
2015-12-09 01:36:19 +01:00
: createElementAccessExpression ( object , index , index ) ;
2014-12-04 01:43:01 +01:00
}
2015-04-13 23:10:03 +02:00
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 ) ;
2015-04-13 23:10:03 +02:00
sliceIdentifier . text = "slice" ;
call . expression = createPropertyAccessExpression ( value , sliceIdentifier ) ;
call . arguments = < NodeArray < LiteralExpression > > createSynthesizedNodeArray ( ) ;
call . arguments [ 0 ] = createNumericLiteral ( sliceIndex ) ;
return call ;
2014-12-04 01:43:01 +01:00
}
2015-12-09 02:19:35 +01:00
function emitObjectLiteralAssignment ( target : ObjectLiteralExpression , value : Expression , sourceMapNode : Node ) {
2015-11-04 23:02:33 +01:00
const properties = target . properties ;
2014-12-04 01:43:01 +01:00
if ( properties . length !== 1 ) {
// For anything but a single element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once.
2015-09-05 02:53:56 +02:00
value = ensureIdentifier ( value , /*reuseIdentifierExpressions*/ true ) ;
2014-12-04 01:43:01 +01:00
}
2015-11-04 23:02:33 +01:00
for ( const p of properties ) {
2014-12-04 01:43:01 +01:00
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 ;
2015-12-10 21:29:14 +01:00
emitDestructuringAssignment ( target , createPropertyAccessForDestructuringProperty ( value , propName , target ) , properties . length === 1 ? sourceMapNode : p ) ;
2014-12-04 01:43:01 +01:00
}
}
}
2015-12-09 01:36:19 +01:00
function emitArrayLiteralAssignment ( target : ArrayLiteralExpression , value : Expression , sourceMapNode : Node ) {
2015-11-04 23:02:33 +01:00
const elements = target . elements ;
2014-12-04 01:43:01 +01:00
if ( elements . length !== 1 ) {
// For anything but a single element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once.
2015-09-05 02:53:56 +02:00
value = ensureIdentifier ( value , /*reuseIdentifierExpressions*/ true ) ;
2014-12-04 01:43:01 +01:00
}
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 ] ;
2014-12-04 01:43:01 +01:00
if ( e . kind !== SyntaxKind . OmittedExpression ) {
2014-12-12 02:41:24 +01:00
if ( e . kind !== SyntaxKind . SpreadElementExpression ) {
2015-12-09 01:36:19 +01:00
emitDestructuringAssignment ( e , createElementAccessExpression ( value , createNumericLiteral ( i ) , e ) , elements . length === 1 ? sourceMapNode : e ) ;
2014-12-12 02:41:24 +01:00
}
2015-04-11 03:56:11 +02:00
else if ( i === elements . length - 1 ) {
2015-12-09 01:36:19 +01:00
emitDestructuringAssignment ( ( < SpreadElementExpression > e ) . expression , createSliceCall ( value , i ) , elements . length === 1 ? sourceMapNode : e ) ;
2014-12-12 02:41:24 +01:00
}
2014-12-04 01:43:01 +01:00
}
}
}
2015-12-09 01:36:19 +01:00
function emitDestructuringAssignment ( target : Expression | ShorthandPropertyAssignment , value : Expression , sourceMapNode : Node ) {
2015-10-05 22:46:56 +02:00
if ( target . kind === SyntaxKind . ShorthandPropertyAssignment ) {
if ( ( < ShorthandPropertyAssignment > target ) . objectAssignmentInitializer ) {
value = createDefaultValueCheck ( value , ( < ShorthandPropertyAssignment > target ) . objectAssignmentInitializer ) ;
}
target = ( < ShorthandPropertyAssignment > target ) . name ;
}
else if ( target . kind === SyntaxKind . BinaryExpression && ( < BinaryExpression > target ) . operatorToken . kind === SyntaxKind . EqualsToken ) {
2015-03-23 19:06:44 +01:00
value = createDefaultValueCheck ( value , ( < BinaryExpression > target ) . right ) ;
2014-12-04 01:43:01 +01:00
target = ( < BinaryExpression > target ) . left ;
}
if ( target . kind === SyntaxKind . ObjectLiteralExpression ) {
2015-12-09 02:19:35 +01:00
emitObjectLiteralAssignment ( < ObjectLiteralExpression > target , value , sourceMapNode ) ;
2014-12-04 01:43:01 +01:00
}
else if ( target . kind === SyntaxKind . ArrayLiteralExpression ) {
2015-12-09 01:36:19 +01:00
emitArrayLiteralAssignment ( < ArrayLiteralExpression > target , value , sourceMapNode ) ;
2014-12-04 01:43:01 +01:00
}
else {
2015-12-03 23:20:25 +01:00
// TODO
2015-12-09 01:36:19 +01:00
emitAssignment ( < Identifier > target , value , /*shouldEmitCommaBeforeAssignment*/ emitCount > 0 , sourceMapNode ) ;
2015-10-08 00:26:09 +02:00
emitCount ++ ;
2014-12-04 01:43:01 +01:00
}
}
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 ;
2015-07-17 02:40:07 +02:00
if ( isEmptyObjectLiteralOrArrayLiteral ( target ) ) {
emit ( value ) ;
}
else if ( isAssignmentExpressionStatement ) {
2015-12-09 01:36:19 +01:00
emitDestructuringAssignment ( target , value , root ) ;
2015-03-09 21:32:02 +01:00
}
else {
2014-12-04 01:43:01 +01:00
if ( root . parent . kind !== SyntaxKind . ParenthesizedExpression ) {
write ( "(" ) ;
}
2015-09-05 02:53:56 +02:00
value = ensureIdentifier ( value , /*reuseIdentifierExpressions*/ true ) ;
2015-12-09 01:36:19 +01:00
emitDestructuringAssignment ( target , value , root ) ;
2014-12-04 01:43:01 +01:00
write ( ", " ) ;
emit ( value ) ;
if ( root . parent . kind !== SyntaxKind . ParenthesizedExpression ) {
write ( ")" ) ;
}
}
}
2015-09-05 02:53:56 +02:00
function emitBindingElement ( target : BindingElement | VariableDeclaration , value : Expression ) {
2014-12-04 01:43:01 +01:00
if ( target . initializer ) {
// Combine value and initializer
value = value ? createDefaultValueCheck ( value , target . initializer ) : target . initializer ;
}
else if ( ! value ) {
// Use 'void 0' in absence of value and initializer
value = createVoidZero ( ) ;
}
if ( isBindingPattern ( target . name ) ) {
2015-09-05 02:53:56 +02:00
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 ) ;
2014-12-04 01:43:01 +01:00
}
2015-09-05 02:53:56 +02:00
for ( let i = 0 ; i < numElements ; i ++ ) {
2015-11-04 23:02:33 +01:00
const element = elements [ i ] ;
2014-12-04 01:43:01 +01:00
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-12-10 21:29:14 +01:00
emitBindingElement ( element , createPropertyAccessForDestructuringProperty ( value , propName , element ) ) ;
2014-12-04 01:43:01 +01:00
}
else if ( element . kind !== SyntaxKind . OmittedExpression ) {
2014-12-12 03:27:08 +01:00
if ( ! element . dotDotDotToken ) {
// Rewrite element to a declaration that accesses array element at index i
2015-12-04 22:41:10 +01:00
emitBindingElement ( element , createElementAccessExpression ( value , createNumericLiteral ( i ) , element ) ) ;
2014-12-12 03:27:08 +01:00
}
2015-09-05 02:53:56 +02:00
else if ( i === numElements - 1 ) {
2015-04-13 23:10:03 +02:00
emitBindingElement ( element , createSliceCall ( value , i ) ) ;
2014-12-12 03:27:08 +01:00
}
2014-12-04 01:43:01 +01:00
}
}
}
else {
2015-12-10 21:29:14 +01:00
emitAssignment ( < Identifier > target . name , value , /*shouldEmitCommaBeforeAssignment*/ emitCount > 0 , target ) ;
2015-10-08 00:26:09 +02:00
emitCount ++ ;
2014-12-04 01:43:01 +01:00
}
}
}
2014-10-11 01:44:14 +02:00
function emitVariableDeclaration ( node : VariableDeclaration ) {
2014-12-04 01:43:01 +01:00
if ( isBindingPattern ( node . name ) ) {
2015-01-16 16:15:31 +01:00
if ( languageVersion < ScriptTarget . ES6 ) {
2015-03-09 21:32:02 +01:00
emitDestructuring ( node , /*isAssignmentExpressionStatement*/ false ) ;
2014-12-14 18:43:14 +01:00
}
else {
emit ( node . name ) ;
emitOptional ( " = " , node . initializer ) ;
}
2014-12-04 01:43:01 +01:00
}
else {
2015-03-13 20:34:12 +01:00
let initializer = node . initializer ;
2015-02-26 02:44:09 +01:00
if ( ! initializer && languageVersion < ScriptTarget . ES6 ) {
2015-02-18 01:26:32 +01:00
// downlevel emit for non-initialized let bindings defined in loops
// for (...) { let x; }
// should be
// for (...) { var <some-uniqie-name> = void 0; }
// this is necessary to preserve ES6 semantic in scenarios like
// for (...) { let x; console.log(x); x = 1 } // assignment on one iteration should not affect other iterations
2015-11-04 23:02:33 +01:00
const isLetDefinedInLoop =
2015-02-18 01:26:32 +01:00
( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . BlockScopedBindingInLoop ) &&
2015-02-26 02:44:09 +01:00
( getCombinedFlagsForIdentifier ( < Identifier > node . name ) & NodeFlags . Let ) ;
// NOTE: default initialization should not be added to let bindings in for-in\for-of statements
2015-10-11 10:12:26 +02:00
if ( isLetDefinedInLoop &&
2015-02-26 02:44:09 +01:00
node . parent . parent . kind !== SyntaxKind . ForInStatement &&
node . parent . parent . kind !== SyntaxKind . ForOfStatement ) {
initializer = createVoidZero ( ) ;
}
2015-02-18 01:26:32 +01:00
}
2015-02-16 22:39:32 +01:00
2015-11-04 23:02:33 +01:00
const exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule ( node . name ) ;
2015-04-10 21:31:10 +02:00
2015-04-13 10:32:16 +02:00
if ( exportChanged ) {
2015-04-10 21:31:10 +02:00
write ( ` ${ exportFunctionForFile } (" ` ) ;
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( node . name ) ;
2015-04-10 21:31:10 +02:00
write ( ` ", ` ) ;
}
emitModuleMemberName ( node ) ;
2015-02-16 22:39:32 +01:00
emitOptional ( " = " , initializer ) ;
2015-04-10 21:31:10 +02:00
2015-04-13 10:32:16 +02:00
if ( exportChanged ) {
2015-06-26 01:24:41 +02:00
write ( ")" ) ;
2015-04-10 21:31:10 +02:00
}
2014-12-04 01:43:01 +01:00
}
2014-07-19 03:06:37 +02:00
}
2014-07-16 19:49:11 +02:00
2015-02-13 03:05:02 +01:00
function emitExportVariableAssignments ( node : VariableDeclaration | BindingElement ) {
2015-02-25 08:51:12 +01:00
if ( node . kind === SyntaxKind . OmittedExpression ) {
return ;
}
2015-11-04 23:02:33 +01:00
const name = node . name ;
2015-02-13 03:05:02 +01:00
if ( name . kind === SyntaxKind . Identifier ) {
2015-02-13 19:07:10 +01:00
emitExportMemberAssignments ( < Identifier > name ) ;
2015-02-13 03:05:02 +01:00
}
else if ( isBindingPattern ( name ) ) {
forEach ( ( < BindingPattern > name ) . elements , emitExportVariableAssignments ) ;
}
}
2015-03-23 19:06:44 +01:00
2015-02-18 01:26:32 +01:00
function getCombinedFlagsForIdentifier ( node : Identifier ) : NodeFlags {
if ( ! node . parent || ( node . parent . kind !== SyntaxKind . VariableDeclaration && node . parent . kind !== SyntaxKind . BindingElement ) ) {
return 0 ;
}
return getCombinedNodeFlags ( node . parent ) ;
}
2015-03-25 04:36:02 +01:00
function isES6ExportedDeclaration ( node : Node ) {
2015-03-12 09:38:35 +01:00
return ! ! ( node . flags & NodeFlags . Export ) &&
2015-09-17 22:26:04 +02:00
module kind === ModuleKind . ES6 &&
2015-03-12 09:38:35 +01:00
node . parent . kind === SyntaxKind . SourceFile ;
}
2015-02-14 09:48:46 +01:00
function emitVariableStatement ( node : VariableStatement ) {
2015-05-13 04:58:12 +02:00
let startIsEmitted = false ;
2015-05-16 01:32:11 +02:00
2015-05-13 04:58:12 +02:00
if ( node . flags & NodeFlags . Export ) {
if ( isES6ExportedDeclaration ( node ) ) {
// Exported ES6 module member
write ( "export " ) ;
startIsEmitted = tryEmitStartOfVariableDeclarationList ( node . declarationList ) ;
}
2014-07-19 03:06:37 +02:00
}
2015-05-13 04:58:12 +02:00
else {
2015-04-22 07:27:33 +02:00
startIsEmitted = tryEmitStartOfVariableDeclarationList ( node . declarationList ) ;
2015-04-11 17:23:25 +02:00
}
2015-05-16 01:32:11 +02:00
2015-04-11 17:23:25 +02:00
if ( startIsEmitted ) {
emitCommaList ( node . declarationList . declarations ) ;
write ( ";" ) ;
}
else {
2015-11-04 23:02:33 +01:00
const atLeastOneItem = emitVariableDeclarationListSkippingUninitializedEntries ( node . declarationList ) ;
2015-04-11 17:23:25 +02:00
if ( atLeastOneItem ) {
write ( ";" ) ;
}
2015-02-17 23:12:28 +01:00
}
2015-10-08 19:18:14 +02:00
if ( module kind !== ModuleKind . ES6 && node . parent === currentSourceFile ) {
2015-02-13 03:05:02 +01:00
forEach ( node . declarationList . declarations , emitExportVariableAssignments ) ;
}
2014-07-18 23:40:47 +02:00
}
2014-07-16 19:49:11 +02:00
2015-05-16 01:32:11 +02:00
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 ) {
2015-05-16 01:32:11 +02:00
if ( declaration . initializer ) {
return true ;
}
}
return false ;
}
2014-10-11 01:44:14 +02:00
function emitParameter ( node : ParameterDeclaration ) {
2015-01-16 16:15:31 +01:00
if ( languageVersion < ScriptTarget . ES6 ) {
2014-12-14 18:43:14 +01:00
if ( isBindingPattern ( node . name ) ) {
2015-11-04 23:02:33 +01:00
const name = createTempVariable ( TempFlags . Auto ) ;
2014-12-14 18:43:14 +01:00
if ( ! tempParameters ) {
tempParameters = [ ] ;
}
tempParameters . push ( name ) ;
emit ( name ) ;
}
else {
emit ( node . name ) ;
2014-12-04 01:43:01 +01:00
}
}
else {
2014-12-14 18:43:14 +01:00
if ( node . dotDotDotToken ) {
write ( "..." ) ;
}
2014-12-04 01:43:01 +01:00
emit ( node . name ) ;
2014-12-14 18:43:14 +01:00
emitOptional ( " = " , node . initializer ) ;
2014-12-04 01:43:01 +01:00
}
2014-11-11 00:36:10 +01:00
}
2014-11-21 05:24:08 +01:00
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 = > {
2015-04-14 02:04:53 +02:00
// 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 ) {
2015-04-14 02:04:53 +02:00
return ;
}
2015-11-04 23:02:33 +01:00
const { name : paramName , initializer } = parameter ;
2015-06-23 01:44:00 +02:00
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 ) {
2015-06-23 01:44:00 +02:00
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 ) ;
}
2015-06-23 01:44:00 +02:00
write ( ";" ) ;
tempIndex ++ ;
}
2014-12-14 18:43:14 +01:00
}
2015-06-24 00:05:33 +02:00
else if ( initializer ) {
2014-12-14 18:43:14 +01:00
writeLine ( ) ;
2015-06-22 23:41:35 +02:00
emitStart ( parameter ) ;
2014-12-14 18:43:14 +01:00
write ( "if (" ) ;
2015-06-24 00:05:33 +02:00
emitNodeWithoutSourceMap ( paramName ) ;
2014-12-14 18:43:14 +01:00
write ( " === void 0)" ) ;
2015-06-22 23:41:35 +02:00
emitEnd ( parameter ) ;
2014-12-14 18:43:14 +01:00
write ( " { " ) ;
2015-06-22 23:41:35 +02:00
emitStart ( parameter ) ;
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( paramName ) ;
2014-12-14 18:43:14 +01:00
write ( " = " ) ;
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( initializer ) ;
2015-06-22 23:41:35 +02:00
emitEnd ( parameter ) ;
2014-12-14 18:43:14 +01:00
write ( "; }" ) ;
}
} ) ;
}
2014-11-11 00:36:10 +01:00
}
2014-11-21 05:24:08 +01:00
function emitRestParameter ( node : FunctionLikeDeclaration ) {
2015-06-03 01:05:01 +02:00
if ( languageVersion < ScriptTarget . ES6 && hasRestParameter ( node ) ) {
2015-11-04 23:02:33 +01:00
const restIndex = node . parameters . length - 1 ;
const restParam = node . parameters [ restIndex ] ;
2015-04-14 02:04:53 +02:00
// A rest parameter cannot have a binding pattern, so let's just ignore it if it does.
if ( isBindingPattern ( restParam . name ) ) {
return ;
}
2015-11-04 23:02:33 +01:00
const tempName = createTempVariable ( TempFlags . _i ) . text ;
2014-07-17 00:39:14 +02:00
writeLine ( ) ;
2014-10-11 01:44:14 +02:00
emitLeadingComments ( restParam ) ;
emitStart ( restParam ) ;
write ( "var " ) ;
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( restParam . name ) ;
2014-10-11 01:44:14 +02:00
write ( " = [];" ) ;
emitEnd ( restParam ) ;
emitTrailingComments ( restParam ) ;
2014-07-17 00:39:14 +02:00
writeLine ( ) ;
2014-10-11 01:44:14 +02:00
write ( "for (" ) ;
emitStart ( restParam ) ;
2014-12-04 01:43:01 +01:00
write ( "var " + tempName + " = " + restIndex + ";" ) ;
2014-10-11 01:44:14 +02:00
emitEnd ( restParam ) ;
write ( " " ) ;
emitStart ( restParam ) ;
2014-12-04 01:43:01 +01:00
write ( tempName + " < arguments.length;" ) ;
2014-10-11 01:44:14 +02:00
emitEnd ( restParam ) ;
write ( " " ) ;
emitStart ( restParam ) ;
2014-12-04 01:43:01 +01:00
write ( tempName + "++" ) ;
2014-10-11 01:44:14 +02:00
emitEnd ( restParam ) ;
write ( ") {" ) ;
increaseIndent ( ) ;
2014-07-17 00:39:14 +02:00
writeLine ( ) ;
2014-10-11 01:44:14 +02:00
emitStart ( restParam ) ;
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( restParam . name ) ;
2014-12-04 01:43:01 +01:00
write ( "[" + tempName + " - " + restIndex + "] = arguments[" + tempName + "];" ) ;
2014-10-11 01:44:14 +02:00
emitEnd ( restParam ) ;
decreaseIndent ( ) ;
2014-07-17 00:39:14 +02:00
writeLine ( ) ;
2014-10-11 01:44:14 +02:00
write ( "}" ) ;
2014-11-11 00:36:10 +01:00
}
}
2014-10-11 01:44:14 +02:00
function emitAccessor ( node : AccessorDeclaration ) {
write ( node . kind === SyntaxKind . GetAccessor ? "get " : "set " ) ;
2015-06-03 15:57:20 +02:00
emit ( node . name ) ;
2014-10-11 01:44:14 +02:00
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
}
2015-03-04 00:56:40 +01:00
function emitDeclarationName ( node : Declaration ) {
if ( node . name ) {
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( node . name ) ;
2015-03-04 00:56:40 +01:00
}
else {
2015-03-24 00:16:29 +01:00
write ( getGeneratedNameForNode ( node ) ) ;
2015-03-04 00:56:40 +01:00
}
}
2015-03-30 11:25:09 +02:00
function shouldEmitFunctionName ( node : FunctionLikeDeclaration ) {
2015-03-13 01:14:33 +01:00
if ( node . kind === SyntaxKind . FunctionExpression ) {
2015-03-30 11:25:09 +02:00
// Emit name if one is present
2015-03-13 01:14:33 +01:00
return ! ! node . name ;
}
2015-03-30 11:25:09 +02:00
if ( node . kind === SyntaxKind . FunctionDeclaration ) {
// Emit name if one is present, or emit generated name in down-level case (for export default case)
2015-12-01 23:05:46 +01:00
return ! ! node . name || module kind !== ModuleKind . ES6 ;
2015-03-13 01:14:33 +01:00
}
}
2014-11-21 05:24:08 +01:00
function emitFunctionDeclaration ( node : FunctionLikeDeclaration ) {
2014-12-16 12:19:13 +01:00
if ( nodeIsMissing ( node . body ) ) {
2015-08-20 01:19:17 +02:00
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-08-07 02:20:35 +02:00
if ( node . kind !== SyntaxKind . MethodDeclaration && node . kind !== SyntaxKind . MethodSignature &&
node . parent && node . parent . kind !== SyntaxKind . PropertyAssignment &&
node . parent . kind !== SyntaxKind . CallExpression ) {
// 1. Methods will emit the comments as part of emitting method declaration
// 2. If the function is a property of object literal, emitting leading-comments
// is done by emitNodeWithoutSourceMap which then call this function.
// In particular, we would like to avoid emit comments twice in following case:
// For example:
// var obj = {
// id:
// /*comment*/ () => void
// }
// 3. If the function is an argument in call expression, emitting of comments will be
// taken care of in emit list of arguments inside of emitCallexpression
2014-10-11 01:44:14 +02:00
emitLeadingComments ( node ) ;
2014-08-22 02:17:02 +02:00
}
2015-01-07 03:18:37 +01:00
2015-07-30 20:11:36 +02: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 ) ) {
2015-03-25 04:36:02 +01:00
if ( isES6ExportedDeclaration ( node ) ) {
2015-03-12 18:11:37 +01:00
write ( "export " ) ;
2015-03-13 01:14:33 +01:00
if ( node . flags & NodeFlags . Default ) {
write ( "default " ) ;
}
2015-03-12 18:11:37 +01:00
}
2015-04-10 07:57:13 +02:00
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 ) ) {
2015-03-04 00:56:40 +01:00
emitDeclarationName ( node ) ;
2014-10-11 01:44:14 +02:00
}
2015-03-13 01:14:33 +01:00
2014-10-11 01:44:14 +02:00
emitSignatureAndBody ( node ) ;
2015-10-08 19:18:14 +02:00
if ( module kind !== ModuleKind . ES6 && node . kind === SyntaxKind . FunctionDeclaration && node . parent === currentSourceFile && node . name ) {
2015-02-13 19:07:10 +01:00
emitExportMemberAssignments ( ( < FunctionDeclaration > node ) . name ) ;
2015-02-13 03:05:02 +01:00
}
2015-07-30 20:11:36 +02:00
emitEnd ( node ) ;
2014-12-09 01:16:11 +01:00
if ( node . kind !== SyntaxKind . MethodDeclaration && node . kind !== SyntaxKind . MethodSignature ) {
2014-10-11 01:44:14 +02:00
emitTrailingComments ( node ) ;
2014-08-22 02:17:02 +02:00
}
2014-11-11 00:36:10 +01:00
}
2014-10-11 01:44:14 +02:00
function emitCaptureThisForNodeIfNecessary ( node : Node ) : void {
if ( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . CaptureThis ) {
2014-07-17 00:39:14 +02:00
writeLine ( ) ;
2014-10-11 01:44:14 +02:00
emitStart ( node ) ;
write ( "var _this = this;" ) ;
emitEnd ( node ) ;
2014-08-14 18:01:38 +02:00
}
2014-08-14 16:48:14 +02:00
}
2014-11-21 05:24:08 +01:00
function emitSignatureParameters ( node : FunctionLikeDeclaration ) {
2014-10-11 01:44:14 +02:00
increaseIndent ( ) ;
write ( "(" ) ;
if ( node ) {
2015-11-04 23:02:33 +01:00
const parameters = node . parameters ;
const omitCount = languageVersion < ScriptTarget . ES6 && hasRestParameter ( node ) ? 1 : 0 ;
2014-12-14 18:43:14 +01:00
emitList ( parameters , 0 , parameters . length - omitCount , /*multiLine*/ false , /*trailingComma*/ false ) ;
2014-10-11 01:44:14 +02:00
}
write ( ")" ) ;
decreaseIndent ( ) ;
2014-11-11 00:36:10 +01:00
}
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
}
2015-01-27 04:00:38 +01:00
2015-06-04 21:08:09 +02: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 ;
2015-05-13 20:36:44 +02:00
// An async function is emit as an outer function that calls an inner
2015-06-04 21:08:09 +02:00
// 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
2015-06-04 21:08:09 +02:00
// promise constructor.
2015-05-13 20:36:44 +02:00
//
2015-06-04 21:08:09 +02:00
// The emit for an async arrow without a lexical `arguments` binding might be:
2015-05-13 20:36:44 +02:00
//
2015-06-04 21:08:09 +02:00
// // input
// let a = async (b) => { await b; }
2015-05-13 20:36:44 +02:00
//
2015-06-04 21:08:09 +02:00
// // output
2015-06-20 01:01:12 +02:00
// let a = (b) => __awaiter(this, void 0, void 0, function* () {
2015-06-04 21:08:09 +02:00
// yield b;
2015-06-20 01:01:12 +02:00
// });
2015-05-13 20:36:44 +02:00
//
2015-06-04 21:08:09 +02:00
// The emit for an async arrow with a lexical `arguments` binding might be:
2015-05-13 20:36:44 +02:00
//
2015-06-04 21:08:09 +02:00
// // input
// let a = async (b) => { await arguments[0]; }
2015-05-13 20:36:44 +02:00
//
2015-06-04 21:08:09 +02:00
// // output
2015-06-20 01:01:12 +02:00
// let a = (b) => __awaiter(this, arguments, void 0, function* (arguments) {
2015-06-04 21:08:09 +02:00
// yield arguments[0];
2015-06-18 20:31:03 +02:00
// });
2015-05-13 20:36:44 +02:00
//
2015-06-04 21:08:09 +02:00
// The emit for an async function expression without a lexical `arguments` binding
// might be:
2015-05-13 20:36:44 +02:00
//
2015-06-04 21:08:09 +02:00
// // input
// let a = async function (b) {
// await b;
// }
2015-05-13 20:36:44 +02:00
//
2015-06-04 21:08:09 +02:00
// // output
// let a = function (b) {
2015-06-20 01:01:12 +02:00
// return __awaiter(this, void 0, void 0, function* () {
2015-06-04 21:08:09 +02:00
// yield b;
2015-06-20 01:01:12 +02:00
// });
2015-06-04 21:08:09 +02:00
// }
2015-05-13 20:36:44 +02:00
//
2015-06-04 21:08:09 +02:00
// The emit for an async function expression with a lexical `arguments` binding
// might be:
2015-05-13 20:36:44 +02:00
//
2015-06-04 21:08:09 +02:00
// // input
// let a = async function (b) {
// await arguments[0];
2015-05-13 20:36:44 +02:00
// }
//
2015-06-04 21:08:09 +02:00
// // output
// let a = function (b) {
2015-06-20 01:01:12 +02:00
// 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) {
2015-06-20 01:01:12 +02:00
// return __awaiter(this, arguments, MyPromise, function* (_arguments) {
// yield _arguments[0];
2015-06-18 20:31:03 +02:00
// });
2015-05-13 20:36:44 +02:00
// }
//
2015-07-09 23:44:47 +02:00
2015-06-04 21:08:09 +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 ( ) ;
2015-06-04 21:08:09 +02:00
write ( "return" ) ;
2015-01-07 03:18:37 +01:00
}
2015-07-09 23:44:47 +02:00
2015-06-20 01:01:12 +02:00
write ( " __awaiter(this" ) ;
if ( hasLexicalArguments ) {
2015-12-01 21:12:31 +01:00
write ( ", arguments, " ) ;
2015-06-20 01:01:12 +02:00
}
else {
2015-12-01 21:12:31 +01:00
write ( ", void 0, " ) ;
2015-06-20 01:01:12 +02:00
}
if ( promiseConstructor ) {
2015-12-01 21:12:31 +01:00
emitEntityNameAsExpression ( promiseConstructor , /*useFallback*/ false ) ;
2015-06-20 01:01:12 +02:00
}
else {
2015-12-01 21:12:31 +01:00
write ( "Promise" ) ;
2015-06-18 20:31:03 +02:00
}
2015-07-09 23:44:47 +02:00
2015-06-04 21:08:09 +02:00
// Emit the call to __awaiter.
if ( hasLexicalArguments ) {
2015-06-20 01:01:12 +02:00
write ( ", function* (_arguments)" ) ;
2015-06-04 21:08:09 +02:00
}
else {
2015-06-20 01:01:12 +02:00
write ( ", function* ()" ) ;
2015-06-04 21:08:09 +02:00
}
2015-07-09 23:44:47 +02:00
2015-06-04 21:08:09 +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
2015-06-04 21:08:09 +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-13 20:36:44 +02:00
}
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 ) {
2015-02-23 02:59:24 +01:00
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
2015-02-23 02:59:24 +01:00
// in that case.
2015-02-07 03:45:09 +01:00
write ( " { }" ) ;
2014-07-17 00:39:14 +02:00
}
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
}
2015-01-27 04:00:38 +01:00
2014-11-21 05:24:08 +01:00
function emitSignatureAndBody ( node : FunctionLikeDeclaration ) {
2015-10-11 10:12:26 +02:00
const saveConvertedLoopState = convertedLoopState ;
2015-11-04 23:02:33 +01:00
const saveTempFlags = tempFlags ;
const saveTempVariables = tempVariables ;
const saveTempParameters = tempParameters ;
2015-10-11 10:12:26 +02:00
convertedLoopState = undefined ;
2015-03-26 18:51:07 +01:00
tempFlags = 0 ;
2014-12-04 01:43:01 +01:00
tempVariables = undefined ;
tempParameters = undefined ;
2015-02-14 09:48:46 +01:00
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 ) ;
2015-12-01 20:26:20 +01:00
if ( isAsync ) {
2015-06-04 21:08:09 +02:00
emitAsyncFunctionBodyForES6 ( node ) ;
2015-02-07 04:53:52 +01:00
}
2014-10-11 01:44:14 +02:00
else {
2015-05-07 02:33:58 +02:00
emitFunctionBody ( node ) ;
2014-07-13 01:04:16 +02:00
}
2015-02-03 00:14:19 +01:00
2015-03-25 04:36:02 +01:00
if ( ! isES6ExportedDeclaration ( node ) ) {
2015-03-23 19:06:44 +01:00
emitExportMemberAssignment ( node ) ;
2014-07-17 00:39:14 +02:00
}
2015-02-14 09:48:46 +01:00
2015-10-11 10:12:26 +02:00
Debug . assert ( convertedLoopState === undefined ) ;
convertedLoopState = saveConvertedLoopState ;
2015-03-26 18:51:07 +01:00
tempFlags = saveTempFlags ;
2014-12-04 01:43:01 +01:00
tempVariables = saveTempVariables ;
tempParameters = saveTempParameters ;
2014-07-17 00:39:14 +02:00
}
2015-02-07 04:53:52 +01:00
// 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 ) {
2015-02-22 02:16:04 +01:00
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
2015-03-07 10:30:45 +01:00
// also wrap it in parentheses first. For example if you have: a => <foo>{}
// then we need to generate: a => ({})
2015-02-22 02:16:04 +01:00
write ( " " ) ;
2015-03-07 10:30:45 +01:00
// Unwrap all type assertions.
2015-03-13 20:34:12 +01:00
let current = body ;
2015-03-07 10:30:45 +01:00
while ( current . kind === SyntaxKind . TypeAssertionExpression ) {
current = ( < TypeAssertion > current ) . expression ;
}
emitParenthesizedIf ( body , current . kind === SyntaxKind . ObjectLiteralExpression ) ;
2015-02-22 02:16:04 +01:00
}
function emitDownLevelExpressionFunctionBody ( node : FunctionLikeDeclaration , body : Expression ) {
2015-02-07 04:53:52 +01:00
write ( " {" ) ;
increaseIndent ( ) ;
2015-11-04 23:02:33 +01:00
const outPos = writer . getTextPos ( ) ;
2015-10-30 20:43:22 +01:00
emitDetachedCommentsAndUpdateCommentsInfo ( node . body ) ;
2015-02-07 04:53:52 +01:00
emitFunctionBodyPreamble ( node ) ;
2015-11-04 23:02:33 +01:00
const preambleEmitted = writer . getTextPos ( ) !== outPos ;
2015-02-07 04:53:52 +01:00
decreaseIndent ( ) ;
// If we didn't have to emit any preamble code, then attempt to keep the arrow
// function on one line.
2015-03-26 21:46:35 +01:00
if ( ! preambleEmitted && nodeStartPositionsAreOnSameLine ( node , body ) ) {
2015-02-07 04:53:52 +01:00
write ( " " ) ;
emitStart ( body ) ;
write ( "return " ) ;
2015-03-18 02:34:42 +01:00
emit ( body ) ;
2015-02-07 04:53:52 +01:00
emitEnd ( body ) ;
write ( ";" ) ;
emitTempDeclarations ( /*newLine*/ false ) ;
write ( " " ) ;
}
else {
increaseIndent ( ) ;
writeLine ( ) ;
emitLeadingComments ( node . body ) ;
2015-11-12 23:15:33 +01:00
emitStart ( body ) ;
2015-02-07 04:53:52 +01:00
write ( "return " ) ;
2015-03-18 02:34:42 +01:00
emit ( body ) ;
2015-11-12 23:15:33 +01:00
emitEnd ( body ) ;
2015-02-07 04:53:52 +01:00
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 ( ) ;
2015-02-23 02:59:24 +01:00
2015-02-07 04:53:52 +01:00
increaseIndent ( ) ;
2015-10-30 20:43:22 +01:00
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 ) ;
2015-02-07 04:53:52 +01:00
emitFunctionBodyPreamble ( node ) ;
decreaseIndent ( ) ;
2015-02-23 02:59:24 +01:00
2015-11-04 23:02:33 +01:00
const preambleEmitted = writer . getTextPos ( ) !== initialTextPos ;
2015-02-07 05:03:24 +01:00
2015-03-26 21:46:35 +01:00
if ( ! preambleEmitted && nodeEndIsOnSameLineAsNodeStart ( body , body ) ) {
2015-11-04 23:02:33 +01:00
for ( const statement of body . statements ) {
2015-02-07 05:03:24 +01:00
write ( " " ) ;
2015-03-13 17:08:27 +01:00
emit ( statement ) ;
2015-02-07 05:03:24 +01:00
}
emitTempDeclarations ( /*newLine*/ false ) ;
write ( " " ) ;
emitLeadingCommentsOfPosition ( body . statements . end ) ;
}
else {
increaseIndent ( ) ;
emitLinesStartingAt ( body . statements , startIndex ) ;
emitTempDeclarations ( /*newLine*/ true ) ;
2015-02-07 04:53:52 +01:00
2015-02-07 05:03:24 +01:00
writeLine ( ) ;
emitLeadingCommentsOfPosition ( body . statements . end ) ;
decreaseIndent ( ) ;
}
emitToken ( SyntaxKind . CloseBraceToken , body . statements . end ) ;
2015-02-07 04:53:52 +01:00
}
2014-10-11 01:44:14 +02:00
function findInitialSuperCall ( ctor : ConstructorDeclaration ) : ExpressionStatement {
if ( ctor . body ) {
2015-11-04 23:02:33 +01:00
const statement = ( < Block > ctor . body ) . statements [ 0 ] ;
2014-10-11 01:44:14 +02:00
if ( statement && statement . kind === SyntaxKind . ExpressionStatement ) {
2015-11-04 23:02:33 +01:00
const expr = ( < ExpressionStatement > statement ) . expression ;
2014-10-11 01:44:14 +02:00
if ( expr && expr . kind === SyntaxKind . CallExpression ) {
2015-11-04 23:02:33 +01:00
const func = ( < CallExpression > expr ) . expression ;
2014-10-11 01:44:14 +02:00
if ( func && func . kind === SyntaxKind . SuperKeyword ) {
return < ExpressionStatement > statement ;
}
2014-11-11 00:36:10 +01:00
}
}
2014-08-22 23:35:07 +02:00
}
2014-08-22 23:40:47 +02:00
}
2014-11-11 00:36:10 +01:00
2014-10-11 01:44:14 +02:00
function emitParameterPropertyAssignments ( node : ConstructorDeclaration ) {
forEach ( node . parameters , param = > {
if ( param . flags & NodeFlags . AccessibilityModifier ) {
writeLine ( ) ;
emitStart ( param ) ;
emitStart ( param . name ) ;
write ( "this." ) ;
2015-03-09 23:01:02 +01:00
emitNodeWithoutSourceMap ( param . name ) ;
2014-10-11 01:44:14 +02:00
emitEnd ( param . name ) ;
write ( " = " ) ;
emit ( param . name ) ;
write ( ";" ) ;
emitEnd ( param ) ;
2014-11-11 00:36:10 +01:00
}
2014-10-11 01:44:14 +02:00
} ) ;
2014-07-17 00:39:14 +02:00
}
2014-11-11 00:36:10 +01:00
2014-11-18 00:53:03 +01:00
function emitMemberAccessForPropertyName ( memberName : DeclarationName ) {
2015-07-30 20:11:36 +02:00
// 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
2014-10-11 01:44:14 +02:00
if ( memberName . kind === SyntaxKind . StringLiteral || memberName . kind === SyntaxKind . NumericLiteral ) {
2014-11-11 00:36:10 +01:00
write ( "[" ) ;
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( memberName ) ;
2014-11-11 00:36:10 +01:00
write ( "]" ) ;
2014-08-22 23:35:07 +02:00
}
2014-11-18 00:53:03 +01:00
else if ( memberName . kind === SyntaxKind . ComputedPropertyName ) {
emitComputedPropertyName ( < ComputedPropertyName > memberName ) ;
}
2014-10-11 01:44:14 +02:00
else {
write ( "." ) ;
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( memberName ) ;
2014-07-17 00:39:14 +02:00
}
}
2015-05-03 20:14:09 +02:00
function getInitializedProperties ( node : ClassLikeDeclaration , isStatic : boolean ) {
2015-11-04 23:02:33 +01:00
const properties : PropertyDeclaration [ ] = [ ] ;
for ( const member of node . members ) {
2015-05-03 20:14:09 +02:00
if ( member . kind === SyntaxKind . PropertyDeclaration && isStatic === ( ( member . flags & NodeFlags . Static ) !== 0 ) && ( < PropertyDeclaration > member ) . initializer ) {
2015-04-03 02:02:26 +02:00
properties . push ( < PropertyDeclaration > member ) ;
2014-11-11 00:36:10 +01:00
}
2015-04-03 02:02:26 +02:00
}
return properties ;
}
function emitPropertyDeclarations ( node : ClassLikeDeclaration , properties : PropertyDeclaration [ ] ) {
2015-11-04 23:02:33 +01:00
for ( const property of properties ) {
2015-04-03 02:02:26 +02:00
emitPropertyDeclaration ( node , property ) ;
}
}
2015-04-03 02:23:25 +02:00
function emitPropertyDeclaration ( node : ClassLikeDeclaration , property : PropertyDeclaration , receiver? : Identifier , isExpression? : boolean ) {
2015-04-03 02:02:26 +02:00
writeLine ( ) ;
emitLeadingComments ( property ) ;
emitStart ( property ) ;
emitStart ( property . name ) ;
2015-04-03 02:23:25 +02:00
if ( receiver ) {
emit ( receiver ) ;
2015-04-03 02:02:26 +02:00
}
else {
2015-04-03 02:23:25 +02:00
if ( property . flags & NodeFlags . Static ) {
emitDeclarationName ( node ) ;
}
else {
write ( "this" ) ;
}
2015-04-03 02:02:26 +02:00
}
emitMemberAccessForPropertyName ( property . name ) ;
emitEnd ( property . name ) ;
write ( " = " ) ;
emit ( property . initializer ) ;
2015-04-03 02:23:25 +02:00
if ( ! isExpression ) {
write ( ";" ) ;
}
2015-04-03 02:02:26 +02:00
emitEnd ( property ) ;
emitTrailingComments ( property ) ;
2014-07-17 00:39:14 +02:00
}
2014-07-13 01:04:16 +02:00
2015-03-31 22:23:46 +02:00
function emitMemberFunctionsForES5AndLower ( node : ClassLikeDeclaration ) {
2014-10-11 01:44:14 +02:00
forEach ( node . members , member = > {
2015-03-31 23:12:35 +02:00
if ( member . kind === SyntaxKind . SemicolonClassElement ) {
writeLine ( ) ;
write ( ";" ) ;
}
else if ( member . kind === SyntaxKind . MethodDeclaration || node . kind === SyntaxKind . MethodSignature ) {
2014-10-11 01:44:14 +02:00
if ( ! ( < MethodDeclaration > member ) . body ) {
2015-08-20 01:19:17 +02:00
return emitCommentsOnNotEmittedNode ( member ) ;
2014-10-11 01:44:14 +02:00
}
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +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 ) ;
2014-10-11 01:44:14 +02:00
emitEnd ( ( < MethodDeclaration > member ) . name ) ;
write ( " = " ) ;
emitFunctionDeclaration ( < MethodDeclaration > member ) ;
emitEnd ( member ) ;
write ( ";" ) ;
emitTrailingComments ( member ) ;
2014-07-17 00:39:14 +02:00
}
2014-10-11 01:44:14 +02:00
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 ) ;
2014-10-11 01:44:14 +02:00
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 ) ;
2014-10-11 01:44:14 +02:00
write ( ", " ) ;
2014-11-21 05:24:08 +01:00
emitExpressionForPropertyName ( ( < AccessorDeclaration > member ) . name ) ;
2014-10-11 01:44:14 +02:00
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 ) ;
}
2014-09-17 19:36:32 +02:00
}
2014-08-08 22:56:53 +02:00
} ) ;
2014-08-22 02:17:02 +02:00
}
2015-03-31 22:23:46 +02:00
function emitMemberFunctionsForES6AndHigher ( node : ClassLikeDeclaration ) {
2015-11-04 23:02:33 +01:00
for ( const member of node . members ) {
2015-03-17 00:24:40 +01:00
if ( ( member . kind === SyntaxKind . MethodDeclaration || node . kind === SyntaxKind . MethodSignature ) && ! ( < MethodDeclaration > member ) . body ) {
2015-08-20 01:19:17 +02:00
emitCommentsOnNotEmittedNode ( member ) ;
2015-03-16 23:41:51 +01:00
}
2015-03-31 23:12:35 +02: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 " ) ;
}
2015-04-10 07:57:13 +02:00
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 ) ;
}
2015-03-31 23:12:35 +02:00
else if ( member . kind === SyntaxKind . SemicolonClassElement ) {
writeLine ( ) ;
write ( ";" ) ;
}
2015-03-17 00:24:40 +01:00
}
2015-03-10 23:41:41 +01:00
}
2015-04-29 23:49:03 +02:00
function emitConstructor ( node : ClassLikeDeclaration , baseTypeElement : ExpressionWithTypeArguments ) {
2015-10-11 10:12:26 +02:00
const saveConvertedLoopState = convertedLoopState ;
2015-11-04 23:02:33 +01:00
const saveTempFlags = tempFlags ;
const saveTempVariables = tempVariables ;
const saveTempParameters = tempParameters ;
2015-10-11 10:12:26 +02:00
convertedLoopState = undefined ;
2015-03-26 18:51:07 +01:00
tempFlags = 0 ;
2015-03-10 20:12:41 +01:00
tempVariables = undefined ;
tempParameters = undefined ;
2015-04-03 02:23:25 +02:00
emitConstructorWorker ( node , baseTypeElement ) ;
2015-10-11 10:12:26 +02:00
Debug . assert ( convertedLoopState === undefined ) ;
convertedLoopState = saveConvertedLoopState ;
2015-04-03 02:23:25 +02:00
tempFlags = saveTempFlags ;
tempVariables = saveTempVariables ;
tempParameters = saveTempParameters ;
}
2015-04-29 23:49:03 +02:00
function emitConstructorWorker ( node : ClassLikeDeclaration , baseTypeElement : ExpressionWithTypeArguments ) {
2015-03-15 20:33:29 +01:00
// 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 ;
2015-03-10 20:12:41 +01:00
// Emit the constructor overload pinned comments
forEach ( node . members , member = > {
if ( member . kind === SyntaxKind . Constructor && ! ( < ConstructorDeclaration > member ) . body ) {
2015-08-20 01:19:17 +02:00
emitCommentsOnNotEmittedNode ( member ) ;
2015-03-10 20:12:41 +01:00
}
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-03-15 20:33:29 +01:00
}
2015-03-10 20:12:41 +01:00
} ) ;
2015-11-04 23:02:33 +01:00
const ctor = getFirstConstructorWithBody ( node ) ;
2015-03-15 20:33:29 +01:00
// 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 ) {
2015-03-15 20:33:29 +01:00
return ;
}
2015-03-10 20:12:41 +01:00
if ( ctor ) {
emitLeadingComments ( ctor ) ;
}
2015-03-16 23:20:40 +01:00
emitStart ( ctor || node ) ;
2015-03-10 20:12:41 +01:00
if ( languageVersion < ScriptTarget . ES6 ) {
write ( "function " ) ;
emitDeclarationName ( node ) ;
emitSignatureParameters ( ctor ) ;
}
else {
write ( "constructor" ) ;
if ( ctor ) {
emitSignatureParameters ( ctor ) ;
}
else {
2015-03-11 01:22:33 +01:00
// Based on EcmaScript6 section 14.5.14: Runtime Semantics: ClassDefinitionEvaluation.
2015-03-10 20:12:41 +01:00
// If constructor is empty, then,
// If ClassHeritageopt is present, then
// Let constructor be the result of parsing the String "constructor(... args){ super (...args);}" using the syntactic grammar with the goal symbol MethodDefinition.
// Else,
// Let constructor be the result of parsing the String "constructor( ){ }" using the syntactic grammar with the goal symbol MethodDefinition
2015-03-31 21:29:02 +02:00
if ( baseTypeElement ) {
2015-03-10 20:12:41 +01:00
write ( "(...args)" ) ;
}
else {
write ( "()" ) ;
}
}
}
2015-03-15 20:33:29 +01:00
2015-07-16 01:43:13 +02:00
let startIndex = 0 ;
2015-03-10 20:12:41 +01:00
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 ) ;
2015-10-30 20:43:22 +01:00
emitDetachedCommentsAndUpdateCommentsInfo ( ctor . body . statements ) ;
2015-03-10 20:12:41 +01:00
}
emitCaptureThisForNodeIfNecessary ( node ) ;
2015-07-24 00:18:48 +02:00
let superCall : ExpressionStatement ;
2015-03-10 20:12:41 +01:00
if ( ctor ) {
emitDefaultValueAssignments ( ctor ) ;
emitRestParameter ( ctor ) ;
2015-03-31 21:29:02 +02:00
if ( baseTypeElement ) {
2015-07-24 00:18:48 +02:00
superCall = findInitialSuperCall ( ctor ) ;
2015-03-10 20:12:41 +01:00
if ( superCall ) {
writeLine ( ) ;
emit ( superCall ) ;
}
}
emitParameterPropertyAssignments ( ctor ) ;
}
else {
2015-03-31 21:29:02 +02:00
if ( baseTypeElement ) {
2015-03-10 20:12:41 +01:00
writeLine ( ) ;
2015-03-31 21:29:02 +02:00
emitStart ( baseTypeElement ) ;
2015-03-17 00:24:40 +01:00
if ( languageVersion < ScriptTarget . ES6 ) {
write ( "_super.apply(this, arguments);" ) ;
}
else {
write ( "super(...args);" ) ;
}
2015-03-31 21:29:02 +02:00
emitEnd ( baseTypeElement ) ;
2015-03-10 20:12:41 +01:00
}
}
2015-11-14 02:43:53 +01:00
emitPropertyDeclarations ( node , getInitializedProperties ( node , /*isStatic*/ false ) ) ;
2015-03-10 20:12:41 +01:00
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 ) ;
2015-03-10 20:12:41 +01:00
}
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 ) ;
}
}
2015-03-31 22:23:46 +02:00
function emitClassExpression ( node : ClassExpression ) {
return emitClassLikeDeclaration ( node ) ;
}
2015-03-17 21:41:29 +01:00
function emitClassDeclaration ( node : ClassDeclaration ) {
2015-03-31 22:23:46 +02:00
return emitClassLikeDeclaration ( node ) ;
}
function emitClassLikeDeclaration ( node : ClassLikeDeclaration ) {
2015-03-17 21:41:29 +01:00
if ( languageVersion < ScriptTarget . ES6 ) {
2015-03-31 22:23:46 +02:00
emitClassLikeDeclarationBelowES6 ( node ) ;
2015-03-17 21:41:29 +01:00
}
else {
2015-03-31 22:23:46 +02:00
emitClassLikeDeclarationForES6AndHigher ( node ) ;
2015-03-17 21:41:29 +01:00
}
2015-10-08 19:18:14 +02:00
if ( module kind !== ModuleKind . ES6 && node . parent === currentSourceFile && node . name ) {
emitExportMemberAssignments ( node . name ) ;
}
2015-03-17 21:41:29 +01:00
}
2015-06-18 23:01:49 +02:00
2015-03-31 22:23:46 +02:00
function emitClassLikeDeclarationForES6AndHigher ( node : ClassLikeDeclaration ) {
2015-11-04 23:02:33 +01:00
const thisNodeIsDecorated = nodeIsDecorated ( node ) ;
2015-03-31 22:23:46 +02:00
if ( node . kind === SyntaxKind . ClassDeclaration ) {
if ( thisNodeIsDecorated ) {
// To preserve the correct runtime semantics when decorators are applied to the class,
// the emit needs to follow one of the following rules:
//
// * For a local class declaration:
//
// @dec class C {
// }
//
// The emit should be:
//
// let C = class {
// };
// C = __decorate([dec], C);
//
// * For an exported class declaration:
//
// @dec export class C {
// }
//
// The emit should be:
//
// export let C = class {
// };
// C = __decorate([dec], C);
//
// * For a default export of a class declaration with a name:
//
// @dec default export class C {
// }
//
// The emit should be:
//
// let C = class {
// }
// C = __decorate([dec], C);
// export default C;
//
// * For a default export of a class declaration without a name:
//
// @dec default export class {
// }
//
// The emit should be:
//
// let _default = class {
// }
// _default = __decorate([dec], _default);
// export default _default;
//
if ( isES6ExportedDeclaration ( node ) && ! ( node . flags & NodeFlags . Default ) ) {
write ( "export " ) ;
}
2015-03-12 00:45:55 +01:00
2015-03-31 22:23:46 +02:00
write ( "let " ) ;
emitDeclarationName ( node ) ;
write ( " = " ) ;
}
else if ( isES6ExportedDeclaration ( node ) ) {
write ( "export " ) ;
if ( node . flags & NodeFlags . Default ) {
write ( "default " ) ;
}
2015-03-12 00:45:55 +01:00
}
}
2015-03-16 23:20:40 +01:00
2015-04-03 02:23:25 +02: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:
2015-04-03 02:23:25 +02:00
//
2015-07-09 23:44:47 +02:00
// class C { static a = 1; static b = 2; ... }
2015-04-03 02:23:25 +02:00
//
// We'll emit:
//
// (_temp = class C { ... }, _temp.a = 1, _temp.b = 2, _temp)
//
// 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 ;
2015-04-03 02:23:25 +02:00
let tempVariable : Identifier ;
if ( isClassExpressionWithStaticProperties ) {
tempVariable = createAndRecordTempVariable ( TempFlags . Auto ) ;
write ( "(" ) ;
increaseIndent ( ) ;
emit ( tempVariable ) ;
2015-06-26 01:24:41 +02:00
write ( " = " ) ;
2015-04-03 02:23:25 +02:00
}
2015-03-23 22:09:38 +01:00
write ( "class" ) ;
2015-03-25 08:06:38 +01:00
2015-09-27 22:51:49 +02:00
// emit name if
// - node has a name
2015-10-06 21:02:55 +02:00
// - this is default export with static initializers
2015-12-01 23:05:46 +01:00
if ( ( node . name || ( node . flags & NodeFlags . Default && ( staticProperties . length > 0 || module kind !== ModuleKind . ES6 ) ) ) && ! thisNodeIsDecorated ) {
2015-03-23 22:09:38 +01:00
write ( " " ) ;
2015-03-17 04:54:28 +01:00
emitDeclarationName ( node ) ;
}
2015-03-23 22:09:38 +01:00
2015-11-04 23:02:33 +01:00
const baseTypeNode = getClassExtendsHeritageClauseElement ( node ) ;
2015-03-10 20:12:41 +01:00
if ( baseTypeNode ) {
write ( " extends " ) ;
2015-03-31 21:29:02 +02:00
emit ( baseTypeNode . expression ) ;
2015-03-10 20:12:41 +01:00
}
2015-03-17 21:41:29 +01:00
2015-03-10 20:12:41 +01:00
write ( " {" ) ;
increaseIndent ( ) ;
writeLine ( ) ;
2015-03-15 00:53:33 +01:00
emitConstructor ( node , baseTypeNode ) ;
emitMemberFunctionsForES6AndHigher ( node ) ;
2015-03-12 00:13:08 +01:00
decreaseIndent ( ) ;
2015-03-10 20:12:41 +01:00
writeLine ( ) ;
2015-03-12 00:13:08 +01:00
emitToken ( SyntaxKind . CloseBraceToken , node . members . end ) ;
2015-05-07 02:23:04 +02:00
// TODO(rbuckton): Need to go back to `let _a = class C {}` approach, removing the defineProperty call for now.
2015-06-26 01:24:41 +02:00
2015-03-24 03:51:38 +01:00
// For a decorated class, we need to assign its name (if it has one). This is because we emit
// the class as a class expression to avoid the double-binding of the identifier:
//
// let C = class {
// }
// Object.defineProperty(C, "name", { value: "C", configurable: true });
//
2015-03-25 01:00:29 +01:00
if ( thisNodeIsDecorated ) {
write ( ";" ) ;
2015-03-23 22:09:38 +01:00
}
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
2015-03-12 00:13:08 +01:00
// 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.
2015-04-03 02:23:25 +02:00
if ( isClassExpressionWithStaticProperties ) {
for ( var property of staticProperties ) {
write ( "," ) ;
writeLine ( ) ;
2015-11-14 02:43:53 +01:00
emitPropertyDeclaration ( node , property , /*receiver*/ tempVariable , /*isExpression*/ true ) ;
2015-04-03 02:23:25 +02:00
}
write ( "," ) ;
writeLine ( ) ;
emit ( tempVariable ) ;
decreaseIndent ( ) ;
write ( ")" ) ;
}
else {
writeLine ( ) ;
emitPropertyDeclarations ( node , staticProperties ) ;
emitDecoratorsOfClass ( node ) ;
}
2015-03-17 05:18:31 +01:00
2015-11-13 03:27:47 +01:00
if ( ! ( node . flags & NodeFlags . Export ) ) {
return ;
2015-03-17 05:18:31 +01:00
}
2015-12-03 05:50:24 +01:00
if ( module kind !== 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 ( thisNodeIsDecorated ) {
writeLine ( ) ;
write ( "export default " ) ;
emitDeclarationName ( node ) ;
write ( ";" ) ;
}
2015-11-13 03:27:47 +01:00
}
2015-12-03 05:50:24 +01:00
else if ( node . parent . kind !== SyntaxKind . SourceFile ) {
writeLine ( ) ;
emitStart ( node ) ;
emitModuleMemberName ( node ) ;
write ( " = " ) ;
2015-11-13 03:27:47 +01:00
emitDeclarationName ( node ) ;
2015-12-03 05:50:24 +01:00
emitEnd ( node ) ;
2015-11-13 03:27:47 +01:00
write ( ";" ) ;
}
2015-03-25 01:00:29 +01:00
}
2014-08-22 02:17:02 +02:00
}
2015-03-31 22:23:46 +02:00
function emitClassLikeDeclarationBelowES6 ( node : ClassLikeDeclaration ) {
if ( node . kind === SyntaxKind . ClassDeclaration ) {
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 ) ) {
2015-04-11 15:14:19 +02:00
write ( "var " ) ;
}
2015-03-31 22:23:46 +02:00
emitDeclarationName ( node ) ;
write ( " = " ) ;
}
write ( "(function (" ) ;
2015-11-04 23:02:33 +01:00
const baseTypeNode = getClassExtendsHeritageClauseElement ( node ) ;
2014-12-01 00:38:45 +01:00
if ( baseTypeNode ) {
2014-10-11 01:44:14 +02:00
write ( "_super" ) ;
2014-08-22 02:17:02 +02:00
}
2014-10-11 01:44:14 +02:00
write ( ") {" ) ;
2015-11-04 23:02:33 +01:00
const saveTempFlags = tempFlags ;
const saveTempVariables = tempVariables ;
const saveTempParameters = tempParameters ;
const saveComputedPropertyNamesToGeneratedNames = computedPropertyNamesToGeneratedNames ;
const saveConvertedLoopState = convertedLoopState ;
2015-10-11 10:12:26 +02:00
convertedLoopState = undefined ;
2015-03-26 18:51:07 +01:00
tempFlags = 0 ;
2015-03-17 21:41:29 +01:00
tempVariables = undefined ;
tempParameters = undefined ;
2015-03-25 03:57:21 +01:00
computedPropertyNamesToGeneratedNames = undefined ;
2014-10-11 01:44:14 +02:00
increaseIndent ( ) ;
2014-12-01 00:38:45 +01:00
if ( baseTypeNode ) {
2014-10-11 01:44:14 +02:00
writeLine ( ) ;
2014-12-01 00:38:45 +01:00
emitStart ( baseTypeNode ) ;
2014-10-11 01:44:14 +02:00
write ( "__extends(" ) ;
2015-03-04 00:56:40 +01:00
emitDeclarationName ( node ) ;
2014-10-11 01:44:14 +02:00
write ( ", _super);" ) ;
2014-12-01 00:38:45 +01:00
emitEnd ( baseTypeNode ) ;
2014-08-16 00:52:30 +02:00
}
2014-10-11 01:44:14 +02:00
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 ) ) ;
2014-10-11 01:44:14 +02:00
writeLine ( ) ;
2015-03-17 21:41:29 +01:00
emitDecoratorsOfClass ( node ) ;
writeLine ( ) ;
2015-03-04 00:09:40 +01:00
emitToken ( SyntaxKind . CloseBraceToken , node . members . end , ( ) = > {
2014-10-11 01:44:14 +02:00
write ( "return " ) ;
2015-03-04 00:56:40 +01:00
emitDeclarationName ( node ) ;
2015-03-04 00:09:40 +01:00
} ) ;
2014-10-11 01:44:14 +02:00
write ( ";" ) ;
2015-03-18 02:15:02 +01:00
emitTempDeclarations ( /*newLine*/ true ) ;
2015-10-11 10:12:26 +02:00
Debug . assert ( convertedLoopState === undefined ) ;
convertedLoopState = saveConvertedLoopState ;
2015-03-26 18:51:07 +01:00
tempFlags = saveTempFlags ;
2015-03-18 02:15:02 +01:00
tempVariables = saveTempVariables ;
tempParameters = saveTempParameters ;
2015-03-25 03:57:21 +01:00
computedPropertyNamesToGeneratedNames = saveComputedPropertyNamesToGeneratedNames ;
2014-10-11 01:44:14 +02:00
decreaseIndent ( ) ;
writeLine ( ) ;
emitToken ( SyntaxKind . CloseBraceToken , node . members . end ) ;
emitStart ( node ) ;
write ( ")(" ) ;
2014-12-01 00:38:45 +01:00
if ( baseTypeNode ) {
2015-03-31 21:29:02 +02:00
emit ( baseTypeNode . expression ) ;
2014-08-22 02:17:02 +02:00
}
2015-03-31 22:23:46 +02:00
write ( ")" ) ;
if ( node . kind === SyntaxKind . ClassDeclaration ) {
write ( ";" ) ;
}
2014-10-11 01:44:14 +02:00
emitEnd ( node ) ;
2015-03-12 18:27:46 +01:00
2015-03-31 22:23:46 +02:00
if ( node . kind === SyntaxKind . ClassDeclaration ) {
emitExportMemberAssignment ( < ClassDeclaration > node ) ;
}
2014-08-19 19:43:13 +02:00
}
2015-03-31 22:23:46 +02:00
function emitClassMemberPrefix ( node : ClassLikeDeclaration , member : Node ) {
2015-03-17 21:41:29 +01:00
emitDeclarationName ( node ) ;
if ( ! ( member . flags & NodeFlags . Static ) ) {
write ( ".prototype" ) ;
}
}
2015-06-18 23:01:49 +02:00
2015-03-31 22:23:46 +02:00
function emitDecoratorsOfClass ( node : ClassLikeDeclaration ) {
2015-03-24 22:16:52 +01:00
emitDecoratorsOfMembers ( node , /*staticFlag*/ 0 ) ;
emitDecoratorsOfMembers ( node , NodeFlags . Static ) ;
emitDecoratorsOfConstructor ( node ) ;
2015-03-17 21:41:29 +01:00
}
2015-03-31 22:23:46 +02:00
function emitDecoratorsOfConstructor ( node : ClassLikeDeclaration ) {
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 ) ;
2015-03-23 22:09:38 +01:00
2015-04-02 02:58:28 +02:00
// skip decoration of the constructor if neither it nor its parameters are decorated
2015-11-30 23:03:28 +01:00
if ( ! decorators && ! firstParameterDecorator ) {
2015-03-17 21:41:29 +01:00
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 ) ;
2015-04-02 02:58:28 +02:00
write ( " = __decorate([" ) ;
increaseIndent ( ) ;
writeLine ( ) ;
2015-11-04 23:02:33 +01:00
const decoratorCount = decorators ? decorators.length : 0 ;
2015-11-20 00:43:35 +01:00
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 ) {
2015-11-20 00:43:35 +01:00
argumentsWritten += emitDecoratorsOfParameters ( constructor , /*leadingComma*/ argumentsWritten > 0 ) ;
}
2015-04-03 00:22:53 +02:00
emitSerializedTypeMetadata ( node , /*leadingComma*/ argumentsWritten >= 0 ) ;
2015-04-02 02:58:28 +02:00
decreaseIndent ( ) ;
writeLine ( ) ;
write ( "], " ) ;
2015-03-24 22:16:52 +01:00
emitDeclarationName ( node ) ;
2015-11-20 00:43:35 +01:00
write ( ")" ) ;
2015-11-30 23:03:28 +01:00
emitEnd ( node . decorators || firstParameterDecorator ) ;
2015-11-20 00:43:35 +01:00
write ( ";" ) ;
2015-03-17 21:41:29 +01:00
writeLine ( ) ;
}
2015-03-31 22:23:46 +02:00
function emitDecoratorsOfMembers ( node : ClassLikeDeclaration , staticFlag : NodeFlags ) {
2015-11-04 23:02:33 +01:00
for ( const member of node . members ) {
2015-04-02 02:58:28 +02:00
// only emit members in the correct group
2015-03-24 22:16:52 +01:00
if ( ( member . flags & NodeFlags . Static ) !== staticFlag ) {
2015-04-02 02:58:28 +02:00
continue ;
2015-03-24 22:16:52 +01:00
}
2015-03-17 21:41:29 +01:00
2015-04-02 02:58:28 +02:00
// skip members that cannot be decorated (such as the constructor)
if ( ! nodeCanBeDecorated ( member ) ) {
continue ;
}
2015-03-17 21:41:29 +01:00
2015-04-02 02:58:28 +02:00
// 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 ) ;
2015-04-02 02:58:28 +02:00
if ( member !== accessors . firstAccessor ) {
continue ;
}
2015-03-17 21:41:29 +01:00
2015-04-02 02:58:28 +02:00
// 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
2015-04-02 02:58:28 +02:00
// we only decorate parameters of the set accessor
functionLikeMember = accessors . setAccessor ;
2015-03-24 22:16:52 +01:00
}
2015-04-02 02:58:28 +02:00
else {
decorators = member . decorators ;
2015-03-24 22:16:52 +01:00
2015-04-02 02:58:28 +02: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 ) ;
2015-11-20 00:43:35 +01:00
// skip a member if it or any of its parameters are not decorated
2015-11-30 23:03:28 +01:00
if ( ! decorators && ! firstParameterDecorator ) {
2015-11-20 00:43:35 +01:00
continue ;
}
2015-03-17 21:41:29 +01:00
2015-03-24 22:16:52 +01:00
// Emit the call to __decorate. Given the following:
//
// class C {
2015-04-02 02:58:28 +02:00
// @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:
//
2015-04-02 02:58:28 +02:00
// __decorate([
// dec
// ], C.prototype, "prop");
2015-03-24 22:16:52 +01:00
//
2015-03-17 21:41:29 +01:00
2015-03-25 01:00:29 +01:00
writeLine ( ) ;
2015-11-30 23:03:28 +01:00
emitStart ( decorators || firstParameterDecorator ) ;
2015-04-02 02:58:28 +02:00
write ( "__decorate([" ) ;
increaseIndent ( ) ;
writeLine ( ) ;
2015-11-04 23:02:33 +01:00
const decoratorCount = decorators ? decorators.length : 0 ;
2015-11-20 00:43:35 +01:00
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 ) {
2015-11-20 00:43:35 +01:00
argumentsWritten += emitDecoratorsOfParameters ( functionLikeMember , argumentsWritten > 0 ) ;
}
2015-04-03 00:22:53 +02:00
emitSerializedTypeMetadata ( member , argumentsWritten > 0 ) ;
2015-04-02 02:58:28 +02:00
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
}
2015-11-20 00:43:35 +01:00
write ( ")" ) ;
2015-11-30 23:03:28 +01:00
emitEnd ( decorators || firstParameterDecorator ) ;
2015-11-20 00:43:35 +01:00
write ( ";" ) ;
2015-03-24 22:16:52 +01:00
writeLine ( ) ;
2015-04-02 02:58:28 +02:00
}
2015-03-17 21:41:29 +01:00
}
2015-04-03 00:22:53 +02:00
function emitDecoratorsOfParameters ( node : FunctionLikeDeclaration , leadingComma : boolean ) : number {
let argumentsWritten = 0 ;
2015-04-02 02:58:28 +02:00
if ( node ) {
let parameterIndex = 0 ;
2015-11-04 23:02:33 +01:00
for ( const parameter of node . parameters ) {
2015-04-02 02:58:28 +02:00
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-04-02 02:58:28 +02:00
}
++ parameterIndex ;
2015-03-24 22:16:52 +01:00
}
2015-04-02 02:58:28 +02:00
}
2015-04-03 00:22:53 +02:00
return argumentsWritten ;
2015-03-17 21:41:29 +01:00
}
2015-04-02 02:58:28 +02:00
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-04-02 02:58:28 +02:00
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
2015-06-19 19:32:10 +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
//
2015-06-19 19:32:10 +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 :
2015-06-19 19:32:10 +02:00
emitSerializedTypeNode ( ( < PropertyDeclaration > node ) . type ) ;
return ;
2015-07-27 13:52:57 +02:00
case SyntaxKind . Parameter :
2015-06-19 19:32:10 +02:00
emitSerializedTypeNode ( ( < ParameterDeclaration > node ) . type ) ;
return ;
2015-07-27 13:52:57 +02:00
case SyntaxKind . GetAccessor :
2015-06-19 19:32:10 +02:00
emitSerializedTypeNode ( ( < AccessorDeclaration > node ) . type ) ;
return ;
2015-07-27 13:52:57 +02:00
case SyntaxKind . SetAccessor :
2015-06-19 19:32:10 +02:00
emitSerializedTypeNode ( getSetAccessorTypeAnnotationNode ( < AccessorDeclaration > node ) ) ;
return ;
2015-07-27 13:52:57 +02:00
2015-06-19 19:32:10 +02:00
}
2015-07-27 13:52:57 +02:00
2015-06-19 19:32:10 +02:00
if ( isFunctionLike ( node ) ) {
write ( "Function" ) ;
return ;
}
2015-07-27 13:52:57 +02:00
2015-06-19 19:32:10 +02:00
write ( "void 0" ) ;
}
2015-07-27 13:52:57 +02:00
2015-06-19 19:32:10 +02:00
function emitSerializedTypeNode ( node : TypeNode ) {
2015-09-01 22:46:44 +02:00
if ( node ) {
switch ( node . kind ) {
case SyntaxKind . VoidKeyword :
write ( "void 0" ) ;
return ;
2015-07-27 13:52:57 +02:00
2015-09-01 22:46:44 +02:00
case SyntaxKind . ParenthesizedType :
emitSerializedTypeNode ( ( < ParenthesizedTypeNode > node ) . type ) ;
return ;
2015-07-27 13:52:57 +02:00
2015-09-01 22:46:44 +02:00
case SyntaxKind . FunctionType :
case SyntaxKind . ConstructorType :
write ( "Function" ) ;
return ;
2015-07-27 13:52:57 +02:00
2015-09-01 22:46:44 +02:00
case SyntaxKind . ArrayType :
case SyntaxKind . TupleType :
write ( "Array" ) ;
return ;
2015-03-18 01:09:39 +01:00
2015-09-01 22:46:44 +02:00
case SyntaxKind . TypePredicate :
case SyntaxKind . BooleanKeyword :
write ( "Boolean" ) ;
return ;
2015-07-27 13:52:57 +02:00
2015-09-01 22:46:44 +02:00
case SyntaxKind . StringKeyword :
2015-11-20 02:08:51 +01:00
case SyntaxKind . StringLiteralType :
2015-09-01 22:46:44 +02:00
write ( "String" ) ;
return ;
2015-07-27 13:52:57 +02:00
2015-09-01 22:46:44 +02:00
case SyntaxKind . NumberKeyword :
write ( "Number" ) ;
return ;
2015-06-19 19:32:10 +02:00
2015-09-01 22:46:44 +02:00
case SyntaxKind . SymbolKeyword :
write ( "Symbol" ) ;
return ;
2015-07-27 13:52:57 +02:00
2015-09-01 22:46:44 +02:00
case SyntaxKind . TypeReference :
emitSerializedTypeReferenceNode ( < TypeReferenceNode > node ) ;
return ;
2015-07-27 13:52:57 +02:00
2015-09-01 22:46:44 +02:00
case SyntaxKind . TypeQuery :
case SyntaxKind . TypeLiteral :
case SyntaxKind . UnionType :
case SyntaxKind . IntersectionType :
case SyntaxKind . AnyKeyword :
2015-11-19 18:53:32 +01:00
case SyntaxKind . ThisType :
2015-09-01 22:46:44 +02:00
break ;
2015-07-27 13:52:57 +02:00
2015-09-01 22:46:44 +02:00
default :
Debug . fail ( "Cannot serialize unexpected type node." ) ;
break ;
}
}
2015-06-19 19:32:10 +02:00
write ( "Object" ) ;
}
2015-07-27 13:52:57 +02:00
2015-06-19 19:32:10 +02:00
/** Serializes a TypeReferenceNode to an appropriate JS constructor value. Used by the __metadata decorator. */
function emitSerializedTypeReferenceNode ( node : TypeReferenceNode ) {
2015-07-17 00:13:31 +02:00
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.
2015-11-20 02:42:12 +01:00
const typeName = cloneEntityName ( node . typeName , location ) ;
2015-11-04 23:02:33 +01:00
const result = resolver . getTypeReferenceSerializationKind ( typeName ) ;
2015-07-01 05:19:25 +02:00
switch ( result ) {
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind . Unknown :
2015-07-01 05:19:25 +02:00
let temp = createAndRecordTempVariable ( TempFlags . Auto ) ;
write ( "(typeof (" ) ;
emitNodeWithoutSourceMap ( temp ) ;
2015-07-24 00:18:48 +02:00
write ( " = " ) ;
2015-07-01 05:19:25 +02:00
emitEntityNameAsExpression ( typeName , /*useFallback*/ true ) ;
write ( ") === 'function' && " ) ;
emitNodeWithoutSourceMap ( temp ) ;
write ( ") || Object" ) ;
break ;
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind . TypeWithConstructSignatureAndValue :
2015-07-01 05:19:25 +02:00
emitEntityNameAsExpression ( typeName , /*useFallback*/ false ) ;
break ;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind . VoidType :
2015-07-01 05:19:25 +02:00
write ( "void 0" ) ;
break ;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind . BooleanType :
2015-07-01 05:19:25 +02:00
write ( "Boolean" ) ;
break ;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind . NumberLikeType :
2015-07-01 05:19:25 +02:00
write ( "Number" ) ;
break ;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind . StringLikeType :
2015-07-01 05:19:25 +02:00
write ( "String" ) ;
break ;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind . ArrayLikeType :
2015-07-01 05:19:25 +02:00
write ( "Array" ) ;
break ;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind . ESSymbolType :
2015-07-01 05:19:25 +02:00
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 :
2015-07-01 05:19:25 +02:00
write ( "Function" ) ;
break ;
2015-07-27 13:52:57 +02:00
2015-07-10 22:13:42 +02:00
case TypeReferenceSerializationKind . ObjectType :
2015-07-01 05:19:25 +02:00
write ( "Object" ) ;
break ;
2015-06-19 19:32:10 +02:00
}
}
2015-07-27 13:52:57 +02:00
2015-06-19 19:32:10 +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
//
2015-06-19 19:32:10 +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 ;
2015-06-19 19:32:10 +02:00
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
2015-06-19 19:32:10 +02:00
if ( valueDeclaration ) {
2015-07-24 00:18:48 +02:00
const parameters = valueDeclaration . parameters ;
const parameterCount = parameters . length ;
2015-06-19 19:32:10 +02:00
if ( parameterCount > 0 ) {
for ( var i = 0 ; i < parameterCount ; i ++ ) {
if ( i > 0 ) {
write ( ", " ) ;
}
2015-07-27 13:52:57 +02:00
2015-06-19 19:32:10 +02:00
if ( parameters [ i ] . dotDotDotToken ) {
2015-07-24 00:18:48 +02:00
let parameterType = parameters [ i ] . type ;
2015-06-19 19:32:10 +02:00
if ( parameterType . kind === SyntaxKind . ArrayType ) {
parameterType = ( < ArrayTypeNode > parameterType ) . elementType ;
}
else if ( 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
2015-06-19 19:32:10 +02:00
emitSerializedTypeNode ( parameterType ) ;
}
else {
emitSerializedTypeOfNode ( parameters [ i ] ) ;
}
}
}
}
}
}
2015-07-27 13:52:57 +02:00
2015-06-19 19:32:10 +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 ) {
2015-07-20 20:58:35 +02:00
if ( node && isFunctionLike ( node ) && ( < FunctionLikeDeclaration > node ) . type ) {
2015-06-19 19:32:10 +02:00
emitSerializedTypeNode ( ( < FunctionLikeDeclaration > node ) . type ) ;
return ;
}
2015-07-27 13:52:57 +02:00
2015-06-19 19:32:10 +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 ) ) {
2015-06-19 19:32:10 +02:00
if ( writeComma ) {
write ( ", " ) ;
2015-04-02 02:58:28 +02:00
}
2015-06-19 19:32:10 +02:00
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 ) ) {
2015-06-19 19:32:10 +02:00
if ( writeComma || argumentsWritten ) {
write ( ", " ) ;
2015-03-18 01:09:39 +01:00
}
2015-06-19 19:32:10 +02: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 ) ) {
2015-06-19 19:32:10 +02:00
if ( writeComma || argumentsWritten ) {
write ( ", " ) ;
2015-04-02 02:58:28 +02:00
}
2015-07-27 13:52:57 +02:00
2015-06-19 19:32:10 +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
2014-10-11 01:44:14 +02:00
function emitInterfaceDeclaration ( node : InterfaceDeclaration ) {
2015-08-20 01:19:17 +02:00
emitCommentsOnNotEmittedNode ( node ) ;
2014-08-05 02:59:33 +02:00
}
2014-08-22 23:40:47 +02:00
2015-01-23 00:58:00 +01:00
function shouldEmitEnumDeclaration ( node : EnumDeclaration ) {
2015-11-04 23:02:33 +01:00
const isConstEnum = isConst ( node ) ;
2015-05-19 06:49:41 +02:00
return ! isConstEnum || compilerOptions . preserveConstEnums || compilerOptions . isolatedModules ;
2015-01-23 00:58:00 +01:00
}
2014-10-11 01:44:14 +02:00
function emitEnumDeclaration ( node : EnumDeclaration ) {
2014-11-21 05:24:08 +01:00
// const enums are completely erased during compilation.
2015-01-23 00:58:00 +01:00
if ( ! shouldEmitEnumDeclaration ( node ) ) {
2014-11-21 05:24:08 +01:00
return ;
2014-08-05 02:59:33 +02:00
}
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 ( ";" ) ;
2015-03-25 05:54:36 +01:00
}
2014-08-05 02:59:33 +02:00
}
2014-07-17 00:39:14 +02:00
writeLine ( ) ;
2014-10-11 01:44:14 +02:00
emitStart ( node ) ;
write ( "(function (" ) ;
emitStart ( node . name ) ;
2015-03-24 00:16:29 +01:00
write ( getGeneratedNameForNode ( node ) ) ;
2014-10-11 01:44:14 +02:00
emitEnd ( node . name ) ;
write ( ") {" ) ;
increaseIndent ( ) ;
2015-01-22 23:45:55 +01:00
emitLines ( node . members ) ;
2014-10-11 01:44:14 +02:00
decreaseIndent ( ) ;
writeLine ( ) ;
emitToken ( SyntaxKind . CloseBraceToken , node . members . end ) ;
write ( ")(" ) ;
emitModuleMemberName ( node ) ;
write ( " || (" ) ;
emitModuleMemberName ( node ) ;
write ( " = {}));" ) ;
emitEnd ( node ) ;
2015-05-11 09:07:49 +02:00
if ( ! isES6ExportedDeclaration ( node ) && node . flags & NodeFlags . Export && ! shouldHoistDeclarationInSystemJsModule ( node ) ) {
// do not emit var if variable was already hoisted
2014-10-11 01:44:14 +02:00
writeLine ( ) ;
emitStart ( node ) ;
write ( "var " ) ;
emit ( node . name ) ;
write ( " = " ) ;
emitModuleMemberName ( node ) ;
emitEnd ( node ) ;
write ( ";" ) ;
2014-08-22 23:40:47 +02:00
}
2015-10-08 19:18:14 +02:00
if ( module kind !== ModuleKind . ES6 && node . parent === currentSourceFile ) {
2015-09-17 22:26:04 +02:00
if ( module kind === ModuleKind . System && ( node . flags & NodeFlags . Export ) ) {
2015-05-11 09:09:06 +02:00
// write the call to exporter for enum
2015-05-11 09:07:49 +02:00
writeLine ( ) ;
write ( ` ${ exportFunctionForFile } (" ` ) ;
emitDeclarationName ( node ) ;
write ( ` ", ` ) ;
emitDeclarationName ( node ) ;
2015-06-04 19:52:43 +02:00
write ( ");" ) ;
2015-05-11 09:07:49 +02:00
}
2015-02-13 19:07:10 +01:00
emitExportMemberAssignments ( node . name ) ;
2015-02-13 03:05:02 +01:00
}
2015-01-22 23:45:55 +01:00
}
2014-08-22 23:40:47 +02: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 ( ";" ) ;
2014-07-18 23:40:47 +02:00
}
2014-07-16 19:49:11 +02:00
2015-02-03 00:14:19 +01:00
function writeEnumMemberDeclarationValue ( member : EnumMember ) {
2015-11-04 23:02:33 +01:00
const value = resolver . getConstantValue ( member ) ;
2015-03-04 17:17:18 +01:00
if ( value !== undefined ) {
write ( value . toString ( ) ) ;
return ;
2015-02-03 00:14:19 +01:00
}
2015-03-04 17:17:18 +01:00
else if ( member . initializer ) {
2015-02-03 00:14:19 +01:00
emit ( member . initializer ) ;
}
else {
write ( "undefined" ) ;
2014-07-19 03:06:37 +02:00
}
2014-07-18 23:40:47 +02:00
}
2014-07-16 19:49:11 +02:00
2014-10-11 01:44:14 +02:00
function getInnerMostModuleDeclarationFromDottedModule ( module Declaration : ModuleDeclaration ) : ModuleDeclaration {
if ( module Declaration.body.kind === SyntaxKind . ModuleDeclaration ) {
2015-11-04 23:02:33 +01:00
const recursiveInnerModule = getInnerMostModuleDeclarationFromDottedModule ( < ModuleDeclaration > module Declaration.body ) ;
2014-10-11 01:44:14 +02:00
return recursiveInnerModule || < ModuleDeclaration > module Declaration.body ;
2014-07-17 00:39:14 +02:00
}
2014-07-13 01:04:16 +02:00
}
2015-01-28 02:09:57 +01:00
function shouldEmitModuleDeclaration ( node : ModuleDeclaration ) {
2015-05-19 06:49:41 +02:00
return isInstantiatedModule ( node , compilerOptions . preserveConstEnums || compilerOptions . isolatedModules ) ;
2015-01-23 00:58:00 +01:00
}
2015-04-13 22:57:26 +02: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 ) ;
}
2014-10-11 01:44:14 +02:00
function emitModuleDeclaration ( node : ModuleDeclaration ) {
2014-12-18 02:00:42 +01:00
// 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
2014-11-26 10:06:10 +01:00
if ( ! shouldEmit ) {
2015-08-20 01:19:17 +02:00
return emitCommentsOnNotEmittedNode ( node ) ;
2014-08-14 18:01:38 +02:00
}
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 ) {
2015-11-23 21:43:28 +01:00
const isES6ExportedNamespace = isES6ExportedDeclaration ( node ) ;
2015-12-01 23:57:59 +01:00
if ( ! isES6ExportedNamespace || isFirstDeclarationOfKind ( node , node . symbol && node . symbol . declarations , SyntaxKind . ModuleDeclaration ) ) {
2015-11-23 21:43:28 +01:00
emitStart ( node ) ;
if ( isES6ExportedNamespace ) {
write ( "export " ) ;
}
write ( "var " ) ;
emit ( node . name ) ;
write ( ";" ) ;
emitEnd ( node ) ;
writeLine ( ) ;
2015-04-13 22:57:26 +02:00
}
2015-03-25 05:54:36 +01:00
}
2015-04-13 22:57:26 +02:00
2014-10-11 01:44:14 +02:00
emitStart ( node ) ;
write ( "(function (" ) ;
emitStart ( node . name ) ;
2015-03-24 00:16:29 +01:00
write ( getGeneratedNameForNode ( node ) ) ;
2014-10-11 01:44:14 +02:00
emitEnd ( node . name ) ;
write ( ") " ) ;
if ( node . body . kind === SyntaxKind . ModuleBlock ) {
2015-10-11 10:12:26 +02:00
const saveConvertedLoopState = convertedLoopState ;
2015-11-04 23:02:33 +01:00
const saveTempFlags = tempFlags ;
const saveTempVariables = tempVariables ;
2015-10-11 10:12:26 +02:00
convertedLoopState = undefined ;
2015-03-26 18:51:07 +01:00
tempFlags = 0 ;
2014-12-04 01:43:01 +01:00
tempVariables = undefined ;
2015-02-14 09:48:46 +01:00
2014-10-11 01:44:14 +02:00
emit ( node . body ) ;
2015-02-14 09:48:46 +01:00
2015-10-11 10:12:26 +02:00
Debug . assert ( convertedLoopState === undefined ) ;
convertedLoopState = saveConvertedLoopState ;
2015-03-26 18:51:07 +01:00
tempFlags = saveTempFlags ;
2014-12-04 01:43:01 +01:00
tempVariables = saveTempVariables ;
2014-07-13 01:04:16 +02:00
}
2014-07-17 00:39:14 +02:00
else {
2014-10-11 01:44:14 +02:00
write ( "{" ) ;
2014-07-17 00:39:14 +02:00
increaseIndent ( ) ;
2014-10-11 01:44:14 +02:00
emitCaptureThisForNodeIfNecessary ( node ) ;
2014-07-17 00:39:14 +02:00
writeLine ( ) ;
2014-10-11 01:44:14 +02:00
emit ( node . body ) ;
2014-07-17 00:39:14 +02:00
decreaseIndent ( ) ;
writeLine ( ) ;
2015-11-04 23:02:33 +01:00
const module Block = < ModuleBlock > getInnerMostModuleDeclarationFromDottedModule ( node ) . body ;
2014-10-11 01:44:14 +02:00
emitToken ( SyntaxKind . CloseBraceToken , module Block.statements.end ) ;
2014-07-13 01:04:16 +02:00
}
2014-10-11 01:44:14 +02:00
write ( ")(" ) ;
2015-03-12 09:38:35 +01:00
// write moduleDecl = containingModule.m only if it is not exported es6 module member
2015-03-25 04:36:02 +01:00
if ( ( node . flags & NodeFlags . Export ) && ! isES6ExportedDeclaration ( node ) ) {
2014-10-11 01:44:14 +02:00
emit ( node . name ) ;
2014-07-17 00:39:14 +02:00
write ( " = " ) ;
2014-07-13 01:04:16 +02:00
}
2014-10-11 01:44:14 +02:00
emitModuleMemberName ( node ) ;
write ( " || (" ) ;
emitModuleMemberName ( node ) ;
write ( " = {}));" ) ;
emitEnd ( node ) ;
2015-03-25 05:54:36 +01:00
if ( ! isES6ExportedDeclaration ( node ) && node . name . kind === SyntaxKind . Identifier && node . parent === currentSourceFile ) {
2015-09-17 22:26:04 +02:00
if ( module kind === ModuleKind . System && ( node . flags & NodeFlags . Export ) ) {
2015-04-11 15:33:09 +02:00
writeLine ( ) ;
write ( ` ${ exportFunctionForFile } (" ` ) ;
emitDeclarationName ( node ) ;
write ( ` ", ` ) ;
emitDeclarationName ( node ) ;
2015-06-04 19:52:43 +02:00
write ( ");" ) ;
2015-04-11 15:33:09 +02:00
}
2015-02-13 19:07:10 +01:00
emitExportMemberAssignments ( < Identifier > node . name ) ;
2015-02-13 03:05:02 +01:00
}
2014-07-17 00:39:14 +02:00
}
2015-10-01 00:45:23 +02:00
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 module Name and return it if possible .
2015-08-17 19:37:44 +02:00
* /
2015-08-14 22:00:41 +02:00
function tryRenameExternalModule ( module Name : LiteralExpression ) : string {
2015-11-07 01:21:43 +01:00
if ( renamedDependencies && hasProperty ( renamedDependencies , module Name.text ) ) {
return ` " ${ renamedDependencies [ module Name.text ] } " ` ;
2015-08-14 22:00:41 +02:00
}
return undefined ;
}
2014-07-17 00:39:14 +02:00
2015-02-08 17:03:15 +01:00
function emitRequire ( module Name : Expression ) {
2015-02-09 02:33:45 +01:00
if ( module Name.kind === SyntaxKind . StringLiteral ) {
2015-02-08 17:03:15 +01:00
write ( "require(" ) ;
2015-11-04 23:02:33 +01:00
const text = tryRenameExternalModule ( < LiteralExpression > module Name ) ;
2015-08-14 22:00:41 +02:00
if ( text ) {
write ( text ) ;
}
else {
emitStart ( module Name ) ;
emitLiteral ( < LiteralExpression > module Name ) ;
emitEnd ( module Name ) ;
}
2015-02-08 17:03:15 +01:00
emitToken ( SyntaxKind . CloseParenToken , module Name.end ) ;
}
else {
2015-03-21 23:46:16 +01:00
write ( "require()" ) ;
2015-02-08 17:03:15 +01:00
}
2014-07-17 00:39:14 +02:00
}
2015-03-21 23:09:54 +01:00
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 ;
2015-03-21 23:09:54 +01:00
if ( importClause && importClause . namedBindings && importClause . namedBindings . kind === SyntaxKind . NamespaceImport ) {
return < NamespaceImport > importClause . namedBindings ;
}
}
2015-03-24 00:56:29 +01:00
function isDefaultImport ( node : ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration ) {
return node . kind === SyntaxKind . ImportDeclaration && ( < ImportDeclaration > node ) . importClause && ! ! ( < ImportDeclaration > node ) . importClause . name ;
}
2015-03-22 17:10:10 +01:00
function emitExportImportAssignments ( node : Node ) {
if ( isAliasSymbolDeclaration ( node ) && resolver . isValueAliasDeclaration ( node ) ) {
emitExportMemberAssignments ( < Identifier > ( < Declaration > node ) . name ) ;
}
forEachChild ( node , emitExportImportAssignments ) ;
}
2015-03-12 08:17:40 +01:00
function emitImportDeclaration ( node : ImportDeclaration ) {
2015-09-17 22:26:04 +02:00
if ( module kind !== ModuleKind . ES6 ) {
2015-03-12 08:17:40 +01:00
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 ) ;
2015-03-12 08:17:40 +01:00
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 ( "{ " ) ;
2015-03-25 04:50:42 +01:00
emitExportOrImportSpecifierList ( ( < NamedImports > node . importClause . namedBindings ) . elements , resolver . isReferencedAliasDeclaration ) ;
2015-03-12 08:17:40 +01:00
write ( " }" ) ;
}
emitEnd ( node . importClause . namedBindings ) ;
emitTrailingComments ( node . importClause . namedBindings ) ;
}
emitEnd ( node . importClause ) ;
write ( " from " ) ;
emit ( node . module Specifier ) ;
write ( ";" ) ;
}
}
else {
write ( "import " ) ;
emit ( node . module Specifier ) ;
write ( ";" ) ;
}
2015-03-16 02:23:48 +01:00
}
2015-03-12 08:17:40 +01:00
function emitExternalImportDeclaration ( node : ImportDeclaration | ImportEqualsDeclaration ) {
2015-03-21 23:09:54 +01:00
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 ) ;
2015-03-23 19:06:44 +01:00
2015-09-17 22:26:04 +02:00
if ( module kind !== ModuleKind . AMD ) {
2015-02-08 21:13:56 +01:00
emitLeadingComments ( node ) ;
emitStart ( node ) ;
2015-03-24 00:56:29 +01:00
if ( namespaceDeclaration && ! isDefaultImport ( node ) ) {
2015-03-22 20:18:38 +01:00
// import x = require("foo")
// import * as x from "foo"
if ( ! isExportedImport ) write ( "var " ) ;
2015-03-21 23:09:54 +01:00
emitModuleMemberName ( namespaceDeclaration ) ;
2015-02-08 21:13:56 +01:00
write ( " = " ) ;
}
2015-03-22 20:18:38 +01:00
else {
// import "foo"
// import x from "foo"
// import { x, y } from "foo"
// import d, * as x from "foo"
// import d, { x, y } from "foo"
2015-11-04 23:02:33 +01:00
const isNakedImport = SyntaxKind . ImportDeclaration && ! ( < ImportDeclaration > node ) . importClause ;
2015-03-22 20:18:38 +01:00
if ( ! isNakedImport ) {
write ( "var " ) ;
2015-03-25 08:06:38 +01:00
write ( getGeneratedNameForNode ( < ImportDeclaration > node ) ) ;
2015-03-22 20:18:38 +01:00
write ( " = " ) ;
}
2015-02-08 17:03:15 +01:00
}
2015-03-21 23:09:54 +01:00
emitRequire ( getExternalModuleName ( node ) ) ;
2015-03-24 00:56:29 +01:00
if ( namespaceDeclaration && isDefaultImport ( node ) ) {
2015-03-22 20:18:38 +01:00
// import d, * as x from "foo"
write ( ", " ) ;
emitModuleMemberName ( namespaceDeclaration ) ;
write ( " = " ) ;
2015-03-25 08:06:38 +01:00
write ( getGeneratedNameForNode ( < ImportDeclaration > node ) ) ;
2015-03-22 20:18:38 +01:00
}
2015-03-21 23:46:16 +01:00
write ( ";" ) ;
2015-02-08 21:13:56 +01:00
emitEnd ( node ) ;
2015-03-22 17:10:10 +01:00
emitExportImportAssignments ( node ) ;
2015-02-08 21:13:56 +01:00
emitTrailingComments ( node ) ;
2015-02-08 17:03:15 +01:00
}
2015-02-08 21:13:56 +01:00
else {
2015-03-22 20:18:38 +01:00
if ( isExportedImport ) {
2015-03-21 23:09:54 +01:00
emitModuleMemberName ( namespaceDeclaration ) ;
write ( " = " ) ;
emit ( namespaceDeclaration . name ) ;
write ( ";" ) ;
2015-02-08 17:03:15 +01:00
}
2015-03-24 00:56:29 +01:00
else if ( namespaceDeclaration && isDefaultImport ( node ) ) {
// import d, * as x from "foo"
write ( "var " ) ;
emitModuleMemberName ( namespaceDeclaration ) ;
write ( " = " ) ;
2015-03-25 08:06:38 +01:00
write ( getGeneratedNameForNode ( < ImportDeclaration > node ) ) ;
2015-03-24 00:56:29 +01:00
write ( ";" ) ;
}
2015-03-22 17:10:10 +01:00
emitExportImportAssignments ( node ) ;
2015-02-08 17:03:15 +01:00
}
2014-07-13 01:04:16 +02:00
}
2015-02-08 17:03:15 +01:00
}
2014-08-05 02:59:33 +02:00
2015-01-27 23:42:20 +01:00
function emitImportEqualsDeclaration ( node : ImportEqualsDeclaration ) {
2015-02-08 21:13:56 +01:00
if ( isExternalModuleImportEqualsDeclaration ( node ) ) {
2015-03-12 08:17:40 +01:00
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
2015-03-04 19:26:38 +01:00
if ( resolver . isReferencedAliasDeclaration ( node ) ||
2015-11-07 01:21:43 +01:00
( ! isCurrentFileExternalModule && resolver . isTopLevelValueImportEqualsWithEntityName ( node ) ) ) {
2015-02-08 21:13:56 +01:00
emitLeadingComments ( node ) ;
emitStart ( node ) ;
2015-07-27 13:52:57 +02:00
2015-07-22 00:56:30 +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
2015-07-22 00:56:30 +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
2015-07-22 00:56:30 +02:00
if ( ! variableDeclarationIsHoisted ) {
Debug . assert ( ! isExported ) ;
2015-07-27 13:52:57 +02:00
2015-07-22 00:56:30 +02:00
if ( isES6ExportedDeclaration ( node ) ) {
write ( "export " ) ;
write ( "var " ) ;
}
else if ( ! ( node . flags & NodeFlags . Export ) ) {
write ( "var " ) ;
}
2015-03-12 18:34:39 +01:00
}
2015-07-27 13:52:57 +02:00
2015-07-22 00:56:30 +02:00
if ( isExported ) {
write ( ` ${ exportFunctionForFile } (" ` ) ;
emitNodeWithoutSourceMap ( node . name ) ;
write ( ` ", ` ) ;
2015-03-12 18:34:39 +01:00
}
2015-07-22 00:56:30 +02:00
2015-02-08 21:13:56 +01:00
emitModuleMemberName ( node ) ;
write ( " = " ) ;
emit ( node . module Reference ) ;
2015-07-22 00:56:30 +02:00
if ( isExported ) {
write ( ")" ) ;
}
2015-07-27 13:52:57 +02:00
write ( ";" ) ;
2015-02-08 21:13:56 +01:00
emitEnd ( node ) ;
2015-03-22 17:10:10 +01:00
emitExportImportAssignments ( node ) ;
2015-02-08 21:13:56 +01:00
emitTrailingComments ( node ) ;
2014-07-13 01:04:16 +02:00
}
}
2014-08-08 22:56:53 +02:00
2015-02-13 03:05:02 +01:00
function emitExportDeclaration ( node : ExportDeclaration ) {
2015-09-17 22:26:04 +02:00
Debug . assert ( module kind !== ModuleKind . System ) ;
2015-04-10 21:10:38 +02:00
2015-09-17 22:26:04 +02:00
if ( module kind !== ModuleKind . ES6 ) {
2015-03-23 19:06:44 +01:00
if ( node . module Specifier && ( ! node . exportClause || resolver . isValueAliasDeclaration ( node ) ) ) {
emitStart ( node ) ;
2015-11-04 23:02:33 +01:00
const generatedName = getGeneratedNameForNode ( node ) ;
2015-03-17 05:58:57 +01:00
if ( node . exportClause ) {
2015-03-23 19:06:44 +01:00
// export { x, y, ... } from "foo"
2015-09-17 22:26:04 +02:00
if ( module kind !== ModuleKind . AMD ) {
2015-03-23 19:06:44 +01:00
write ( "var " ) ;
write ( generatedName ) ;
2015-03-17 05:58:57 +01:00
write ( " = " ) ;
2015-03-23 19:06:44 +01:00
emitRequire ( getExternalModuleName ( node ) ) ;
2015-03-17 05:58:57 +01:00
write ( ";" ) ;
2015-03-23 19:06:44 +01:00
}
2015-11-04 23:02:33 +01:00
for ( const specifier of node . exportClause . elements ) {
2015-03-23 19:06:44 +01:00
if ( resolver . isValueAliasDeclaration ( specifier ) ) {
writeLine ( ) ;
emitStart ( specifier ) ;
emitContainingModuleName ( specifier ) ;
write ( "." ) ;
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( specifier . name ) ;
2015-03-23 19:06:44 +01:00
write ( " = " ) ;
write ( generatedName ) ;
write ( "." ) ;
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( specifier . propertyName || specifier . name ) ;
2015-03-23 19:06:44 +01:00
write ( ";" ) ;
emitEnd ( specifier ) ;
}
}
2015-03-21 23:46:16 +01:00
}
else {
2015-03-23 19:06:44 +01:00
// export * from "foo"
2015-10-05 07:00:57 +02:00
if ( hasExportStarsToExportValues && resolver . module ExportsSomeValue ( node . module Specifier ) ) {
writeLine ( ) ;
write ( "__export(" ) ;
if ( module kind !== ModuleKind . AMD ) {
emitRequire ( getExternalModuleName ( node ) ) ;
}
else {
write ( generatedName ) ;
}
write ( ");" ) ;
2015-03-23 19:06:44 +01:00
}
2015-03-21 23:46:16 +01:00
}
2015-03-23 20:37:22 +01:00
emitEnd ( node ) ;
2015-02-15 17:25:24 +01:00
}
2015-03-12 19:51:32 +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 ( "{ " ) ;
2015-03-25 04:50:42 +01:00
emitExportOrImportSpecifierList ( node . exportClause . elements , resolver . isValueAliasDeclaration ) ;
2015-03-23 20:37:22 +01:00
write ( " }" ) ;
}
else {
write ( "*" ) ;
}
if ( node . module Specifier ) {
write ( " from " ) ;
2015-07-30 20:11:36 +02:00
emit ( node . module Specifier ) ;
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
}
}
2015-03-25 04:50:42 +01:00
function emitExportOrImportSpecifierList ( specifiers : ImportOrExportSpecifier [ ] , shouldEmit : ( node : Node ) = > boolean ) {
2015-09-17 22:26:04 +02:00
Debug . assert ( module kind === 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 ) {
2015-03-25 04:50:42 +01:00
if ( shouldEmit ( specifier ) ) {
2015-03-23 20:37:22 +01:00
if ( needsComma ) {
write ( ", " ) ;
}
if ( specifier . propertyName ) {
2015-07-30 20:11:36 +02:00
emit ( specifier . propertyName ) ;
2015-03-23 20:37:22 +01:00
write ( " as " ) ;
}
2015-07-30 20:11:36 +02:00
emit ( specifier . name ) ;
2015-03-23 20:37:22 +01:00
needsComma = true ;
2015-03-12 19:51:32 +01:00
}
2015-02-13 03:05:02 +01:00
}
}
2015-03-21 21:12:39 +01:00
function emitExportAssignment ( node : ExportAssignment ) {
2015-03-23 19:17:40 +01:00
if ( ! node . isExportEquals && resolver . isValueAliasDeclaration ( node ) ) {
2015-09-17 22:26:04 +02:00
if ( module kind === ModuleKind . ES6 ) {
2015-03-23 19:17:40 +01:00
writeLine ( ) ;
emitStart ( node ) ;
write ( "export default " ) ;
2015-11-04 23:02:33 +01:00
const expression = node . expression ;
2015-03-23 19:17:40 +01:00
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 ( module kind === 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\"] = " ) ;
2015-10-01 00:45:23 +02:00
}
else {
2015-04-20 22:40:13 +02:00
write ( ".default = " ) ;
}
2015-04-10 21:10:38 +02:00
emit ( node . expression ) ;
2015-04-17 19:29:05 +02:00
}
2015-03-23 19:06:44 +01:00
write ( ";" ) ;
2015-03-23 19:17:40 +01:00
emitEnd ( node ) ;
2015-02-08 21:13:56 +01:00
}
2015-03-21 21:12:39 +01:00
}
}
2015-03-21 23:09:54 +01:00
function collectExternalModuleInfo ( sourceFile : SourceFile ) {
2015-02-08 21:13:56 +01:00
externalImports = [ ] ;
2015-02-13 03:05:02 +01:00
exportSpecifiers = { } ;
2015-03-21 21:12:39 +01:00
exportEquals = undefined ;
2015-10-05 07:00:57 +02:00
hasExportStarsToExportValues = false ;
2015-11-04 23:02:33 +01:00
for ( const node of sourceFile . statements ) {
2015-03-21 23:09:54 +01:00
switch ( node . kind ) {
case SyntaxKind . ImportDeclaration :
2015-03-23 19:06:44 +01:00
if ( ! ( < ImportDeclaration > node ) . importClause ||
2015-03-25 05:43:03 +01:00
resolver . isReferencedAliasDeclaration ( ( < ImportDeclaration > node ) . importClause , /*checkChildren*/ true ) ) {
2015-03-21 23:09:54 +01:00
// 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 ) ;
2015-02-13 03:05:02 +01:00
}
2015-03-21 23:09:54 +01:00
break ;
case SyntaxKind . ImportEqualsDeclaration :
if ( ( < ImportEqualsDeclaration > node ) . module Reference.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 ) . module Specifier ) {
if ( ! ( < ExportDeclaration > node ) . exportClause ) {
// export * from "mod"
2015-10-05 07:00:57 +02:00
if ( resolver . module ExportsSomeValue ( ( < ExportDeclaration > node ) . module Specifier ) ) {
externalImports . push ( < ExportDeclaration > node ) ;
hasExportStarsToExportValues = true ;
}
2015-03-21 23:09:54 +01:00
}
2015-03-22 17:10:10 +01:00
else if ( resolver . isValueAliasDeclaration ( node ) ) {
2015-03-21 23:09:54 +01:00
// 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 ;
2015-03-21 23:09:54 +01: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
}
}
2014-10-25 19:48:02 +02:00
}
2015-03-21 23:46:16 +01:00
function emitExportStarHelper() {
2015-10-05 07:00:57 +02:00
if ( hasExportStarsToExportValues ) {
2015-03-21 23:46:16 +01:00
writeLine ( ) ;
write ( "function __export(m) {" ) ;
increaseIndent ( ) ;
writeLine ( ) ;
write ( "for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];" ) ;
decreaseIndent ( ) ;
writeLine ( ) ;
write ( "}" ) ;
}
}
2015-06-02 19:45:50 +02:00
function getLocalNameForExternalImport ( node : ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration ) : string {
2015-11-04 23:02:33 +01:00
const namespaceDeclaration = getNamespaceDeclarationNode ( node ) ;
2015-06-02 19:45:50 +02:00
if ( namespaceDeclaration && ! isDefaultImport ( node ) ) {
2015-11-07 01:21:43 +01:00
return getTextOfNodeFromSourceText ( currentText , namespaceDeclaration . name ) ;
2015-03-21 23:09:54 +01:00
}
2015-06-02 19:45:50 +02:00
if ( node . kind === SyntaxKind . ImportDeclaration && ( < ImportDeclaration > node ) . importClause ) {
return getGeneratedNameForNode ( node ) ;
}
if ( node . kind === SyntaxKind . ExportDeclaration && ( < ExportDeclaration > node ) . module Specifier ) {
return getGeneratedNameForNode ( node ) ;
2015-04-10 21:10:38 +02:00
}
}
2015-11-14 00:26:38 +01: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 module Name = getExternalModuleName ( importNode ) ;
2015-04-20 22:40:13 +02:00
if ( module Name.kind === SyntaxKind . StringLiteral ) {
2015-10-01 00:45:23 +02:00
return tryRenameExternalModule ( < LiteralExpression > module Name ) || getLiteralText ( < LiteralExpression > module Name ) ;
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 =
2015-04-20 23:17:38 +02:00
importNode . kind === SyntaxKind . ExportDeclaration ||
2015-06-26 01:24:41 +02:00
( importNode . kind === SyntaxKind . ImportDeclaration && ! ( < ImportDeclaration > importNode ) . importClause ) ;
2015-04-20 23:17:38 +02:00
if ( skipNode ) {
2015-04-10 21:10:38 +02:00
continue ;
}
if ( ! started ) {
write ( "var " ) ;
started = true ;
2015-02-10 23:59:20 +01:00
}
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 ( ";" ) ;
}
}
2015-04-29 01:18:33 +02:00
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 expors.
2015-10-05 07:00:57 +02:00
if ( ! hasExportStarsToExportValues ) {
2015-04-29 01:18:33 +02:00
// 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
if ( ! exportedDeclarations && isEmpty ( 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 ) {
2015-04-29 01:18:33 +02:00
if ( externalImport . kind === SyntaxKind . ExportDeclaration && ( < ExportDeclaration > externalImport ) . exportClause ) {
hasExportDeclarationWithExportClause = true ;
break ;
}
}
if ( ! hasExportDeclarationWithExportClause ) {
2015-04-29 05:17:55 +02:00
// we still need to emit exportStar helper
return emitExportStarFunction ( /*localNames*/ undefined ) ;
2015-04-29 01:18:33 +02:00
}
}
const exportedNamesStorageRef = makeUniqueName ( "exportedNames" ) ;
writeLine ( ) ;
2015-04-29 03:51:29 +02:00
write ( ` var ${ exportedNamesStorageRef } = { ` ) ;
2015-04-29 01:18:33 +02:00
increaseIndent ( ) ;
let started = false ;
if ( exportedDeclarations ) {
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 ] ) {
2015-04-29 01:18:33 +02:00
// 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-04-29 01:18:33 +02:00
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-04-29 01:18:33 +02:00
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 ) ;
2015-04-29 01:18:33 +02:00
}
}
decreaseIndent ( ) ;
writeLine ( ) ;
write ( "};" ) ;
2015-04-29 05:17:55 +02:00
return emitExportStarFunction ( exportedNamesStorageRef ) ;
2015-06-18 23:01:49 +02:00
2015-04-29 05:17:55 +02:00
function emitExportStarFunction ( localNames : string ) : string {
const exportStarFunction = makeUniqueName ( "exportStar" ) ;
2015-04-29 03:51:29 +02:00
2015-04-29 05:17:55 +02:00
writeLine ( ) ;
2015-04-29 01:18:33 +02:00
2015-04-29 05:17:55 +02:00
// define an export star helper function
write ( ` function ${ exportStarFunction } (m) { ` ) ;
2015-04-29 22:56:17 +02:00
increaseIndent ( ) ;
2015-04-29 05:17:55 +02:00
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 ( ) ;
2015-04-29 05:17:55 +02:00
writeLine ( ) ;
2015-04-29 22:56:17 +02:00
write ( ` if (n !== "default" ` ) ;
2015-04-29 05:17:55 +02:00
if ( localNames ) {
2015-04-29 20:43:23 +02:00
write ( ` && ! ${ localNames } .hasOwnProperty(n) ` ) ;
2015-04-29 05:17:55 +02:00
}
2015-07-21 07:40:22 +02:00
write ( ` ) exports[n] = m[n]; ` ) ;
2015-04-29 22:56:17 +02:00
decreaseIndent ( ) ;
2015-04-29 05:17:55 +02:00
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 ( ) ;
2015-04-29 05:17:55 +02:00
writeLine ( ) ;
2015-06-26 01:24:41 +02:00
write ( "}" ) ;
2015-04-29 05:17:55 +02:00
return exportStarFunction ;
}
2015-06-18 23:01:49 +02:00
2015-04-29 01:18:33 +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 ;
}
2015-04-29 01:18:33 +02:00
if ( started ) {
write ( "," ) ;
}
else {
started = true ;
}
writeLine ( ) ;
write ( "'" ) ;
if ( node . kind === SyntaxKind . Identifier ) {
2015-07-30 20:11:36 +02:00
emitNodeWithCommentsAndWithoutSourcemap ( node ) ;
2015-04-29 01:18:33 +02:00
}
else {
emitDeclarationName ( < Declaration > node ) ;
}
2015-04-29 05:17:55 +02:00
write ( "': true" ) ;
2015-04-29 01:18:33 +02:00
}
}
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
2015-05-11 09:07:49 +02:00
let hoistedVars : ( Identifier | ClassDeclaration | ModuleDeclaration | EnumDeclaration ) [ ] ;
2015-04-10 21:10:38 +02:00
let hoistedFunctionDeclarations : FunctionDeclaration [ ] ;
2015-04-29 01:18:33 +02:00
let exportedDeclarations : ( Identifier | Declaration ) [ ] ;
2015-04-10 21:10:38 +02:00
visit ( node ) ;
2015-04-11 15:14:19 +02:00
if ( hoistedVars ) {
2015-04-10 21:10:38 +02:00
writeLine ( ) ;
write ( "var " ) ;
2015-11-04 23:02:33 +01:00
const seen : Map < string > = { } ;
2015-04-11 15:14:19 +02: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
2015-05-11 09:07:49 +02:00
? < 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 ) ;
2015-05-11 09:07:49 +02:00
if ( hasProperty ( seen , text ) ) {
continue ;
}
else {
seen [ text ] = text ;
}
}
2015-04-10 21:10:38 +02:00
if ( i !== 0 ) {
write ( ", " ) ;
}
2015-05-11 09:07:49 +02:00
if ( local . kind === SyntaxKind . ClassDeclaration || local . kind === SyntaxKind . ModuleDeclaration || local . kind === SyntaxKind . EnumDeclaration ) {
emitDeclarationName ( < ClassDeclaration | ModuleDeclaration | EnumDeclaration > local ) ;
2015-04-11 15:14:19 +02:00
}
else {
emit ( local ) ;
}
2015-04-29 01:18:33 +02:00
2015-11-04 23:02:33 +01:00
const flags = getCombinedNodeFlags ( local . kind === SyntaxKind . Identifier ? local.parent : local ) ;
2015-04-29 01:18:33 +02:00
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 ) ;
2015-04-29 01:18:33 +02:00
if ( f . flags & NodeFlags . Export ) {
if ( ! exportedDeclarations ) {
exportedDeclarations = [ ] ;
}
exportedDeclarations . push ( f ) ;
}
2015-04-10 21:10:38 +02:00
}
}
2015-04-29 01:18:33 +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 ;
}
2015-04-11 15:14:19 +02:00
if ( node . kind === SyntaxKind . ClassDeclaration ) {
2015-04-22 07:27:33 +02:00
if ( ! hoistedVars ) {
hoistedVars = [ ] ;
}
hoistedVars . push ( < ClassDeclaration > node ) ;
2015-04-11 15:14:19 +02:00
return ;
}
2015-05-11 09:07:49 +02:00
if ( node . kind === SyntaxKind . EnumDeclaration ) {
if ( shouldEmitEnumDeclaration ( < EnumDeclaration > node ) ) {
if ( ! hoistedVars ) {
hoistedVars = [ ] ;
}
hoistedVars . push ( < ModuleDeclaration > node ) ;
2015-04-22 07:27:33 +02:00
}
2015-05-11 09:07:49 +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 ) {
2015-04-21 17:31:14 +02:00
if ( shouldHoistVariable ( < VariableDeclaration | BindingElement > node , /*checkIfSourceFileLevelDecl*/ false ) ) {
2015-11-04 23:02:33 +01:00
const name = ( < VariableDeclaration | BindingElement > node ) . name ;
2015-04-21 17:31:14 +02:00
if ( name . kind === SyntaxKind . Identifier ) {
2015-04-22 07:27:33 +02:00
if ( ! hoistedVars ) {
hoistedVars = [ ] ;
}
hoistedVars . push ( < Identifier > name ) ;
2015-04-21 17:31:14 +02:00
}
else {
forEachChild ( name , visit ) ;
}
2015-04-10 21:10:38 +02:00
}
return ;
}
2015-07-27 13:52:57 +02:00
2015-09-11 18:36:17 +02:00
if ( isInternalModuleImportEqualsDeclaration ( node ) && resolver . isValueAliasDeclaration ( node ) ) {
2015-07-22 00:56:30 +02:00
if ( ! hoistedVars ) {
hoistedVars = [ ] ;
}
2015-07-27 13:52:57 +02:00
2015-07-22 00:56:30 +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 ) ;
}
}
}
2015-04-21 17:31:14 +02:00
function shouldHoistVariable ( node : VariableDeclaration | VariableDeclarationList | BindingElement , checkIfSourceFileLevelDecl : boolean ) : boolean {
2015-04-24 06:14:03 +02:00
if ( checkIfSourceFileLevelDecl && ! shouldHoistDeclarationInSystemJsModule ( node ) ) {
2015-04-21 17:31:14 +02:00
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
2015-04-21 17:31:14 +02:00
// 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() {
2015-11-07 01:21:43 +01:00
return module kind === 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-10-01 00:45:23 +02:00
2015-08-07 01:38:53 +02:00
for ( let i = 0 ; i < dependencyGroups . length ; ++ i ) {
2015-04-10 21:10:38 +02:00
if ( i !== 0 ) {
write ( "," ) ;
}
writeLine ( ) ;
increaseIndent ( ) ;
2015-10-01 00:45:23 +02:00
2015-11-04 23:02:33 +01:00
const group = dependencyGroups [ i ] ;
2015-10-01 00:45:23 +02:00
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-10-01 00:45:23 +02:00
2015-11-04 23:02:33 +01:00
for ( const entry of group ) {
const importVariableName = getLocalNameForExternalImport ( entry ) || "" ;
2015-10-01 00:45:23 +02:00
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 ( ) ;
for ( let i = 0 , len = ( < ExportDeclaration > entry ) . exportClause . elements . length ; i < len ; ++ i ) {
if ( i !== 0 ) {
write ( "," ) ;
writeLine ( ) ;
}
2015-10-01 00:45:23 +02:00
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 ( ) ;
2015-10-01 00:45:23 +02:00
write ( "});" ) ;
2015-04-13 22:00:00 +02:00
}
2015-08-07 01:38:53 +02:00
else {
2015-10-05 07:00:57 +02:00
// 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-02-10 23:59:20 +01: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 ( ) ;
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 ) {
2015-08-06 06:45:04 +02:00
// - 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
2015-10-01 00:45:23 +02:00
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 ) . module Specifier ) {
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-08-06 06:45:04 +02:00
}
}
2015-04-10 21:10:38 +02:00
continue ;
2015-07-22 00:56:30 +02:00
case SyntaxKind . ImportEqualsDeclaration :
if ( ! isInternalModuleImportEqualsDeclaration ( statement ) ) {
2015-08-06 06:45:04 +02:00
// - import equals declarations that import external modules are not emitted
2015-07-22 00:56:30 +02:00
continue ;
}
2015-08-06 06:45:04 +02:00
// fall-though for import declarations that import internal modules
default :
writeLine ( ) ;
emit ( statement ) ;
2015-10-01 00:45:23 +02:00
}
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-10-01 00:45:23 +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 module Name = node . module Name ;
2015-11-03 01:54:12 +01:00
if ( module Name || ( emitRelativePathAsModuleName && ( module Name = getResolvedExternalModuleName ( host , node ) ) ) ) {
2015-11-02 21:53:27 +01:00
write ( ` " ${ module Name } ", ` ) ;
}
}
2015-11-03 01:54:12 +01:00
function emitSystemModule ( node : SourceFile , emitRelativePathAsModuleName? : boolean ) : void {
2015-03-23 19:06:44 +01:00
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" ) ;
2015-07-01 05:19:25 +02:00
writeLine ( ) ;
2015-06-03 23:55:42 +02:00
write ( "System.register(" ) ;
2015-11-03 01:54:12 +01:00
writeModuleName ( node , emitRelativePathAsModuleName ) ;
2015-06-26 01:24:41 +02:00
write ( "[" ) ;
2015-10-01 00:45:23 +02:00
2015-11-04 23:02:33 +01:00
const groupIndices : Map < number > = { } ;
const dependencyGroups : DependencyGroup [ ] = [ ] ;
2015-08-07 01:38:53 +02:00
2015-04-20 22:40:13 +02:00
for ( let i = 0 ; i < externalImports . length ; ++ i ) {
2015-11-14 00:26:38 +01:00
const text = getExternalModuleNameText ( externalImports [ i ] , emitRelativePathAsModuleName ) ;
2015-08-07 01:38:53 +02:00
if ( hasProperty ( groupIndices , text ) ) {
// deduplicate/group entries in dependency list by the dependency name
2015-11-04 23:02:33 +01:00
const groupIndex = groupIndices [ text ] ;
2015-08-07 01:38:53 +02:00
dependencyGroups [ groupIndex ] . push ( externalImports [ i ] ) ;
continue ;
}
else {
groupIndices [ text ] = dependencyGroups . length ;
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-10-01 00:45:23 +02:00
2015-04-20 22:40:13 +02:00
write ( text ) ;
}
2015-04-10 21:10:38 +02:00
write ( ` ], function( ${ exportFunctionForFile } ) { ` ) ;
writeLine ( ) ;
increaseIndent ( ) ;
2015-11-24 22:31:30 +01:00
const startIndex = emitDirectivePrologues ( node . statements , /*startWithNewLine*/ true , /*ensureUseStrict*/ true ) ;
2015-07-31 20:54:03 +02:00
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 ( "});" ) ;
}
2015-09-26 00:22:01 +02:00
interface AMDDependencyNames {
aliasedModuleNames : string [ ] ;
unaliasedModuleNames : string [ ] ;
importAliasNames : string [ ] ;
}
2015-04-03 08:05:58 +02:00
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 + "\"" ) ;
importAliasNames . push ( amdDependency . name ) ;
}
else {
unaliasedModuleNames . push ( "\"" + amdDependency . path + "\"" ) ;
}
2014-07-17 00:39:14 +02:00
}
2015-04-17 02:32:40 +02:00
2015-11-04 23:02:33 +01:00
for ( const importNode of externalImports ) {
2015-04-16 23:56:22 +02:00
// Find the name of the external module
2015-11-14 00:26:38 +01:00
const externalModuleName = getExternalModuleNameText ( importNode , emitRelativePathAsModuleName ) ;
2015-04-16 23:56:22 +02:00
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 ) ;
2015-04-03 08:05:58 +02:00
if ( includeNonAmdDependencies && importAliasName ) {
2015-04-16 23:56:22 +02:00
aliasedModuleNames . push ( externalModuleName ) ;
2015-04-17 02:26:06 +02:00
importAliasNames . push ( importAliasName ) ;
2015-04-16 23:56:22 +02:00
}
else {
unaliasedModuleNames . push ( externalModuleName ) ;
2015-02-08 17:03:15 +01:00
}
2015-03-21 23:09:54 +01:00
}
2015-04-20 22:40:13 +02:00
2015-09-26 00:22:01 +02:00
return { aliasedModuleNames , unaliasedModuleNames , importAliasNames } ;
}
2015-11-03 01:54:12 +01:00
function emitAMDDependencies ( node : SourceFile , includeNonAmdDependencies : boolean , emitRelativePathAsModuleName? : boolean ) {
2015-09-26 00:22:01 +02:00
// 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
2015-11-09 21:38:23 +01:00
const dependencyNames = getAMDDependencyNames ( node , includeNonAmdDependencies , emitRelativePathAsModuleName ) ;
2015-09-26 00:22:01 +02:00
emitAMDDependencyList ( dependencyNames ) ;
write ( ", " ) ;
emitAMDFactoryHeader ( dependencyNames ) ;
}
function emitAMDDependencyList ( { aliasedModuleNames , unaliasedModuleNames } : AMDDependencyNames ) {
2015-04-16 23:56:22 +02:00
write ( "[\"require\", \"exports\"" ) ;
if ( aliasedModuleNames . length ) {
2014-10-11 01:44:14 +02:00
write ( ", " ) ;
2015-04-16 23:56:22 +02:00
write ( aliasedModuleNames . join ( ", " ) ) ;
2015-03-21 23:09:54 +01:00
}
2015-04-16 23:56:22 +02:00
if ( unaliasedModuleNames . length ) {
2014-10-11 01:44:14 +02:00
write ( ", " ) ;
2015-04-16 23:56:22 +02:00
write ( unaliasedModuleNames . join ( ", " ) ) ;
2015-03-21 23:09:54 +01:00
}
2015-09-26 00:22:01 +02:00
write ( "]" ) ;
}
function emitAMDFactoryHeader ( { importAliasNames } : AMDDependencyNames ) {
write ( "function (require, exports" ) ;
2015-04-17 02:26:06 +02:00
if ( importAliasNames . length ) {
2015-04-16 23:56:22 +02:00
write ( ", " ) ;
2015-04-17 02:26:06 +02:00
write ( importAliasNames . join ( ", " ) ) ;
2015-03-21 23:09:54 +01:00
}
2015-09-26 00:22:01 +02:00
write ( ") {" ) ;
2015-04-03 08:05:58 +02:00
}
2015-11-03 01:54:12 +01:00
function emitAMDModule ( node : SourceFile , emitRelativePathAsModuleName? : boolean ) {
2015-07-31 20:54:03 +02:00
emitEmitHelpers ( node ) ;
2015-04-03 08:05:58 +02:00
collectExternalModuleInfo ( node ) ;
writeLine ( ) ;
write ( "define(" ) ;
2015-11-03 01:54:12 +01:00
writeModuleName ( node , emitRelativePathAsModuleName ) ;
emitAMDDependencies ( node , /*includeNonAmdDependencies*/ true , emitRelativePathAsModuleName ) ;
2014-10-11 01:44:14 +02:00
increaseIndent ( ) ;
2015-11-24 22:31:30 +01:00
const startIndex = emitDirectivePrologues ( node . statements , /*startWithNewLine*/ true , /*ensureUseStrict*/ true ) ;
2015-03-21 23:46:16 +01:00
emitExportStarHelper ( ) ;
2014-10-11 01:44:14 +02:00
emitCaptureThisForNodeIfNecessary ( node ) ;
emitLinesStartingAt ( node . statements , startIndex ) ;
2014-12-12 02:41:24 +01:00
emitTempDeclarations ( /*newLine*/ true ) ;
2015-03-21 21:12:39 +01:00
emitExportEquals ( /*emitAsReturn*/ true ) ;
2014-10-11 01:44:14 +02:00
decreaseIndent ( ) ;
writeLine ( ) ;
write ( "});" ) ;
2014-07-12 00:13:01 +02:00
}
2015-10-05 20:22:16 +02:00
function emitCommonJSModule ( node : SourceFile ) {
2015-11-24 22:31:30 +01:00
const startIndex = emitDirectivePrologues ( node . statements , /*startWithNewLine*/ false , /*ensureUseStrict*/ true ) ;
2015-07-31 20:54:03 +02:00
emitEmitHelpers ( node ) ;
2015-03-23 19:06:44 +01:00
collectExternalModuleInfo ( node ) ;
2015-03-21 23:46:16 +01:00
emitExportStarHelper ( ) ;
2014-10-11 01:44:14 +02:00
emitCaptureThisForNodeIfNecessary ( node ) ;
emitLinesStartingAt ( node . statements , startIndex ) ;
2014-12-12 02:41:24 +01:00
emitTempDeclarations ( /*newLine*/ true ) ;
2015-03-21 21:12:39 +01:00
emitExportEquals ( /*emitAsReturn*/ false ) ;
2015-03-02 21:17:05 +01:00
}
2015-10-05 20:22:16 +02:00
function emitUMDModule ( node : SourceFile ) {
2015-07-31 20:54:03 +02:00
emitEmitHelpers ( node ) ;
2015-04-03 08:05:58 +02:00
collectExternalModuleInfo ( node ) ;
2015-11-04 23:02:33 +01:00
const dependencyNames = getAMDDependencyNames ( node , /*includeNonAmdDependencies*/ false ) ;
2015-09-26 00:22:01 +02:00
2015-04-03 08:05:58 +02:00
// Module is detected first to support Browserify users that load into a browser with an AMD loader
2015-09-26 00:22:01 +02:00
writeLines ( ` (function (factory) {
2015-04-03 08:05:58 +02:00
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 ) {
2015-09-26 00:22:01 +02:00
define ( ` );
emitAMDDependencyList ( dependencyNames ) ;
write ( ", factory);" ) ;
writeLines ( ` }
2015-04-03 08:05:58 +02:00
} ) ( ` );
2015-09-26 00:22:01 +02:00
emitAMDFactoryHeader ( dependencyNames ) ;
2015-04-03 08:05:58 +02:00
increaseIndent ( ) ;
2015-11-24 22:31:30 +01:00
const startIndex = emitDirectivePrologues ( node . statements , /*startWithNewLine*/ true , /*ensureUseStrict*/ true ) ;
2015-04-03 08:05:58 +02:00
emitExportStarHelper ( ) ;
emitCaptureThisForNodeIfNecessary ( node ) ;
emitLinesStartingAt ( node . statements , startIndex ) ;
emitTempDeclarations ( /*newLine*/ true ) ;
emitExportEquals ( /*emitAsReturn*/ true ) ;
decreaseIndent ( ) ;
writeLine ( ) ;
write ( "});" ) ;
}
2015-10-05 20:22:16 +02:00
function emitES6Module ( node : SourceFile ) {
2015-03-12 07:29:30 +01:00
externalImports = undefined ;
exportSpecifiers = undefined ;
2015-03-23 19:06:44 +01:00
exportEquals = undefined ;
2015-10-05 07:00:57 +02:00
hasExportStarsToExportValues = false ;
2015-11-04 23:02:33 +01:00
const startIndex = emitDirectivePrologues ( node . statements , /*startWithNewLine*/ false ) ;
2015-07-31 20:54:03 +02:00
emitEmitHelpers ( node ) ;
2014-10-11 01:44:14 +02:00
emitCaptureThisForNodeIfNecessary ( node ) ;
emitLinesStartingAt ( node . statements , startIndex ) ;
2014-12-12 02:41:24 +01:00
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
}
2015-03-21 21:12:39 +01:00
function emitExportEquals ( emitAsReturn : boolean ) {
2015-03-22 17:10:10 +01:00
if ( exportEquals && resolver . isValueAliasDeclaration ( exportEquals ) ) {
2014-10-11 01:44:14 +02:00
writeLine ( ) ;
2015-03-21 21:12:39 +01:00
emitStart ( exportEquals ) ;
2015-03-02 21:17:05 +01:00
write ( emitAsReturn ? "return " : "module.exports = " ) ;
2015-03-21 21:12:39 +01:00
emit ( ( < ExportAssignment > exportEquals ) . expression ) ;
2014-10-11 01:44:14 +02:00
write ( ";" ) ;
2015-03-21 21:12:39 +01:00
emitEnd ( exportEquals ) ;
2014-07-17 00:39:14 +02:00
}
2014-10-11 01:44:14 +02:00
}
2014-08-04 21:06:07 +02:00
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 ;
}
}
2015-08-20 21:20:28 +02:00
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 ) ;
2015-10-08 20:28:18 +02:00
result = ( result ? result + "\" + ' ' + \"" : "" ) + escapeString ( part ) ;
2015-06-18 23:01:49 +02:00
}
firstNonWhitespace = - 1 ;
}
else if ( ! isWhiteSpace ( c ) ) {
lastNonWhitespace = i ;
if ( firstNonWhitespace === - 1 ) {
firstNonWhitespace = i ;
}
}
}
2015-08-20 21:20:28 +02:00
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
}
2015-08-20 21:20:28 +02:00
if ( result ) {
// Replace entities like
result = result . replace ( /&(\w+);/g , function ( s : any , m : string ) {
2015-08-20 21:30:12 +02:00
if ( entities [ m ] !== undefined ) {
return String . fromCharCode ( entities [ m ] ) ;
}
else {
return s ;
}
2015-08-20 21:20:28 +02:00
} ) ;
}
2015-06-26 23:18:51 +02:00
return result ;
2015-06-18 23:01:49 +02:00
}
2015-06-26 23:18:51 +02:00
function getTextToEmit ( node : JsxText ) {
2015-06-22 22:55:09 +02:00
switch ( compilerOptions . jsx ) {
case JsxEmit . React :
2015-08-20 21:20:28 +02:00
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 :
2015-07-24 02:30:31 +02:00
write ( "\"" ) ;
2015-08-20 21:20:28 +02:00
write ( trimReactWhitespaceAndApplyEntities ( node ) ) ;
2015-07-24 02:30:31 +02:00
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 :
2015-07-24 02:30:31 +02:00
write ( "{" ) ;
2015-06-18 23:01:49 +02:00
emit ( node . expression ) ;
2015-07-24 02:30:31 +02:00
write ( "}" ) ;
2015-06-18 23:01:49 +02:00
break ;
case JsxEmit . React :
emit ( node . expression ) ;
break ;
}
}
}
2015-11-24 22:31:30 +01:00
function isUseStrictPrologue ( node : ExpressionStatement ) : boolean {
return ! ! ( node . expression as StringLiteral ) . text . match ( /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-03-13 20:34:12 +01:00
for ( let i = 0 ; i < statements . length ; ++ i ) {
2014-10-11 01:44:14 +02:00
if ( isPrologueDirective ( statements [ i ] ) ) {
2015-11-24 22:31:30 +01:00
if ( isUseStrictPrologue ( statements [ i ] as ExpressionStatement ) ) {
foundUseStrict = true ;
}
2014-10-11 01:44:14 +02:00
if ( startWithNewLine || i > 0 ) {
writeLine ( ) ;
2014-07-19 03:07:38 +02:00
}
2014-10-11 01:44:14 +02:00
emit ( statements [ i ] ) ;
2014-07-19 03:07:38 +02:00
}
2014-10-11 01:44:14 +02:00
else {
2015-11-24 22:31:30 +01:00
ensureUseStrictPrologue ( startWithNewLine || i > 0 , ! foundUseStrict && ensureUseStrict ) ;
2014-10-11 01:44:14 +02:00
// return index of the first non prologue directive
return i ;
2014-07-19 03:07:38 +02:00
}
2014-07-17 00:39:14 +02:00
}
2015-11-24 22:31:30 +01:00
ensureUseStrictPrologue ( startWithNewLine , ! foundUseStrict && ensureUseStrict ) ;
2014-10-11 01:44:14 +02:00
return statements . length ;
2014-07-12 01:36:06 +02:00
}
2015-04-02 02:58:28 +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-03-17 21:41:29 +01:00
for ( let i = 0 ; i < lines . length ; ++ i ) {
2015-11-04 23:02:33 +01:00
const line = lines [ i ] ;
2015-03-17 21:41:29 +01:00
if ( line . length ) {
writeLine ( ) ;
write ( line ) ;
}
}
}
2015-07-31 20:54:03 +02:00
function emitEmitHelpers ( node : SourceFile ) : void {
Add support for --noEmitHelpers flag
This PR is a Work In Progress that addresses multiple `__extends`
being output as described in #1350: Multiple `__extends` being output
when `--module amd` is set.
The issue still exists as of `v1.5.0 - f53e6a8`.
Apparently a fix was created for this in #1356 but according to #2009, a
[comment](https://github.com/Microsoft/TypeScript/issues/2009#issuecomment-74136291)
later indicated that this was never merged in.
Further conversation continued in #2487 but did not yield any result. I
refer to my earlier recommendation in #1350.
> My question is this, would the TypeScript team be open to a flag that
> can be passed to tsc that will generate something like the following
> ```ts
> define(["require", "exports", "__extends", './mammal'], function (require, exports, __extends, Mammal) {
> var Human = (function (_super) {
> __extends(Human, _super);
> function Human() {
> _super.apply(this, arguments);
> }
> return Human;
> })(Mammal);
> return Human;
> });
> ```
To continue with the naming convention I have chosen the flag
`--noEmitHelpers`.
2015-04-24 04:16:11 +02:00
// 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 && resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . EmitExtends ) ) {
writeLines ( extendsHelper ) ;
extendsEmitted = true ;
}
if ( ! decorateEmitted && resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . EmitDecorate ) {
writeLines ( decorateHelper ) ;
if ( compilerOptions . emitDecoratorMetadata ) {
writeLines ( metadataHelper ) ;
}
decorateEmitted = true ;
2015-04-02 02:58:28 +02:00
}
Add support for --noEmitHelpers flag
This PR is a Work In Progress that addresses multiple `__extends`
being output as described in #1350: Multiple `__extends` being output
when `--module amd` is set.
The issue still exists as of `v1.5.0 - f53e6a8`.
Apparently a fix was created for this in #1356 but according to #2009, a
[comment](https://github.com/Microsoft/TypeScript/issues/2009#issuecomment-74136291)
later indicated that this was never merged in.
Further conversation continued in #2487 but did not yield any result. I
refer to my earlier recommendation in #1350.
> My question is this, would the TypeScript team be open to a flag that
> can be passed to tsc that will generate something like the following
> ```ts
> define(["require", "exports", "__extends", './mammal'], function (require, exports, __extends, Mammal) {
> var Human = (function (_super) {
> __extends(Human, _super);
> function Human() {
> _super.apply(this, arguments);
> }
> return Human;
> })(Mammal);
> return Human;
> });
> ```
To continue with the naming convention I have chosen the flag
`--noEmitHelpers`.
2015-04-24 04:16:11 +02:00
if ( ! paramEmitted && resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . EmitParam ) {
writeLines ( paramHelper ) ;
paramEmitted = true ;
2015-04-02 02:58:28 +02:00
}
2015-07-09 23:44:47 +02:00
2015-05-07 02:33:58 +02:00
if ( ! awaiterEmitted && resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . EmitAwaiter ) {
writeLines ( awaiterHelper ) ;
awaiterEmitted = true ;
}
2015-04-02 02:58:28 +02:00
}
2015-07-31 20:54:03 +02:00
}
function emitSourceFileNode ( node : SourceFile ) {
// Start new file on new line
writeLine ( ) ;
2015-08-02 04:24:18 +02:00
emitShebang ( ) ;
2015-10-30 20:43:22 +01:00
emitDetachedCommentsAndUpdateCommentsInfo ( node ) ;
2015-07-31 20:54:03 +02:00
2015-05-19 06:49:41 +02:00
if ( isExternalModule ( node ) || compilerOptions . isolatedModules ) {
2015-11-13 22:44:57 +01:00
if ( isOwnFileEmit || ( ! isExternalModule ( node ) && compilerOptions . isolatedModules ) ) {
2015-11-09 21:38:23 +01:00
const emitModule = module EmitDelegates [ module kind ] || module EmitDelegates [ 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 [ module kind ] ( node , /*emitRelativePathAsModuleName*/ true ) ;
2015-10-02 04:23:12 +02:00
}
2014-07-12 00:13:01 +02:00
}
2014-10-11 01:44:14 +02:00
else {
2015-10-05 20:22:16 +02:00
// emit prologue directives prior to __extends
2015-11-04 23:02:33 +01:00
const startIndex = emitDirectivePrologues ( node . statements , /*startWithNewLine*/ false ) ;
2015-02-13 03:05:02 +01:00
externalImports = undefined ;
exportSpecifiers = undefined ;
2015-03-21 21:12:39 +01:00
exportEquals = undefined ;
2015-10-05 07:00:57 +02:00
hasExportStarsToExportValues = false ;
2015-07-31 20:54:03 +02:00
emitEmitHelpers ( node ) ;
2014-10-11 01:44:14 +02:00
emitCaptureThisForNodeIfNecessary ( node ) ;
emitLinesStartingAt ( node . statements , startIndex ) ;
2014-12-12 02:41:24 +01:00
emitTempDeclarations ( /*newLine*/ true ) ;
2014-07-12 00:13:01 +02:00
}
2014-07-13 01:04:16 +02:00
2014-12-03 01:09:41 +01:00
emitLeadingComments ( node . endOfFileToken ) ;
2014-07-13 01:04:16 +02:00
}
2015-11-24 07:38:05 +01:00
function emit ( node : Node ) : void {
emitNodeConsideringCommentsOption ( node , emitNodeWithSourceMap ) ;
}
2015-07-30 20:11:36 +02:00
function emitNodeWithCommentsAndWithoutSourcemap ( node : Node ) : void {
emitNodeConsideringCommentsOption ( node , emitNodeWithoutSourceMap ) ;
}
2014-12-16 10:09:42 +01:00
2015-07-30 20:11:36 +02:00
function emitNodeConsideringCommentsOption ( node : Node , emitNodeConsideringSourcemap : ( node : Node ) = > void ) : void {
if ( node ) {
if ( node . flags & NodeFlags . Ambient ) {
2015-08-25 23:03:21 +02:00
return emitCommentsOnNotEmittedNode ( node ) ;
2015-07-30 20:11:36 +02:00
}
2015-01-23 00:58:00 +01:00
2015-07-30 20:11:36 +02: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 ) ;
2015-07-30 20:11:36 +02:00
if ( emitComments ) {
emitLeadingComments ( node ) ;
}
2015-01-23 00:58:00 +01:00
2015-07-30 20:11:36 +02:00
emitNodeConsideringSourcemap ( node ) ;
2015-01-23 00:58:00 +01:00
2015-07-30 20:11:36 +02:00
if ( emitComments ) {
emitTrailingComments ( node ) ;
}
2015-01-23 00:58:00 +01:00
}
}
2015-11-24 07:38:05 +01:00
function emitNodeWithSourceMap ( node : Node ) : void {
if ( node ) {
emitStart ( node ) ;
emitNodeWithoutSourceMap ( node ) ;
emitEnd ( node ) ;
}
}
2015-07-30 20:11:36 +02:00
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 ) ;
}
2015-07-30 20:11:36 +02:00
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 :
2015-02-08 17:03:15 +01:00
case SyntaxKind . ImportEqualsDeclaration :
2015-01-23 00:58:00 +01:00
case SyntaxKind . TypeAliasDeclaration :
case SyntaxKind . ExportAssignment :
2015-07-30 20:11:36 +02:00
return true ;
}
}
2015-01-23 00:58:00 +01:00
2015-07-30 20:11:36 +02:00
function shouldEmitLeadingAndTrailingComments ( node : Node ) {
switch ( node . kind ) {
2015-05-16 01:32:11 +02:00
case SyntaxKind . VariableStatement :
return shouldEmitLeadingAndTrailingCommentsForVariableStatement ( < VariableStatement > node ) ;
2015-01-23 00:58:00 +01:00
case SyntaxKind . ModuleDeclaration :
2015-02-05 22:57:27 +01:00
// 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 :
2015-02-05 22:57:27 +01:00
// 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
2015-07-30 20:11:36 +02:00
// 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.
2015-03-18 02:34:42 +01:00
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 &&
2015-03-18 03:13:00 +01:00
compilerOptions . target <= ScriptTarget . ES5 ) {
2015-03-18 02:34:42 +01:00
return false ;
}
2015-01-23 00:58:00 +01:00
// Emit comments for everything else.
return true ;
}
2015-06-03 15:57:20 +02:00
function emitJavaScriptWorker ( node : Node ) {
2014-12-02 22:29:49 +01:00
// Check if the node can be emitted regardless of the ScriptTarget
2014-10-11 01:44:14 +02:00
switch ( node . kind ) {
case SyntaxKind . Identifier :
2015-06-03 15:57:20 +02:00
return emitIdentifier ( < Identifier > node ) ;
2014-10-11 01:44:14 +02:00
case SyntaxKind . Parameter :
return emitParameter ( < ParameterDeclaration > node ) ;
2014-12-09 20:26:43 +01:00
case SyntaxKind . MethodDeclaration :
case SyntaxKind . MethodSignature :
2014-12-08 23:34:00 +01:00
return emitMethod ( < MethodDeclaration > node ) ;
2014-10-11 01:44:14 +02:00
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 :
2014-11-21 05:24:08 +01:00
case SyntaxKind . NoSubstitutionTemplateLiteral :
case SyntaxKind . TemplateHead :
case SyntaxKind . TemplateMiddle :
case SyntaxKind . TemplateTail :
2014-10-11 01:44:14 +02:00
return emitLiteral ( < LiteralExpression > node ) ;
2014-11-21 05:24:08 +01:00
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 :
2015-10-22 20:35:48 +02:00
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 ) ;
2014-10-11 01:44:14 +02:00
case SyntaxKind . QualifiedName :
2014-11-30 01:48:28 +01:00
return emitQualifiedName ( < QualifiedName > node ) ;
2014-12-04 01:43:01 +01:00
case SyntaxKind . ObjectBindingPattern :
return emitObjectBindingPattern ( < BindingPattern > node ) ;
case SyntaxKind . ArrayBindingPattern :
return emitArrayBindingPattern ( < BindingPattern > node ) ;
2014-12-14 18:43:14 +01:00
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 ) ;
2014-10-11 01:44:14 +02:00
case SyntaxKind . PropertyAssignment :
return emitPropertyAssignment ( < PropertyDeclaration > node ) ;
2014-12-08 23:34:00 +01:00
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 ) ;
2014-11-30 00:47:02 +01:00
case SyntaxKind . ElementAccessExpression :
return emitIndexedAccess ( < ElementAccessExpression > node ) ;
2014-10-11 01:44:14 +02:00
case SyntaxKind . CallExpression :
return emitCallExpression ( < CallExpression > node ) ;
case SyntaxKind . NewExpression :
return emitNewExpression ( < NewExpression > node ) ;
2014-11-21 05:24:08 +01:00
case SyntaxKind . TaggedTemplateExpression :
return emitTaggedTemplateExpression ( < TaggedTemplateExpression > node ) ;
2014-11-30 00:58:55 +01:00
case SyntaxKind . TypeAssertionExpression :
2014-11-30 00:47:02 +01:00
return emit ( ( < TypeAssertion > node ) . expression ) ;
2015-06-18 23:01:49 +02:00
case SyntaxKind . AsExpression :
return emit ( ( < AsExpression > node ) . expression ) ;
2014-11-30 00:58:55 +01:00
case SyntaxKind . ParenthesizedExpression :
return emitParenExpression ( < ParenthesizedExpression > node ) ;
2014-10-11 01:44:14 +02:00
case SyntaxKind . FunctionDeclaration :
case SyntaxKind . FunctionExpression :
case SyntaxKind . ArrowFunction :
2014-11-21 05:24:08 +01:00
return emitFunctionDeclaration ( < FunctionLikeDeclaration > node ) ;
2014-11-29 22:43:30 +01:00
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 ) ;
2014-11-29 22:43:30 +01:00
case SyntaxKind . PrefixUnaryExpression :
2014-11-30 01:48:28 +01:00
return emitPrefixUnaryExpression ( < PrefixUnaryExpression > node ) ;
2014-11-30 00:58:55 +01:00
case SyntaxKind . PostfixUnaryExpression :
2014-11-30 01:48:28 +01:00
return emitPostfixUnaryExpression ( < PostfixUnaryExpression > node ) ;
2014-10-11 01:44:14 +02:00
case SyntaxKind . BinaryExpression :
return emitBinaryExpression ( < BinaryExpression > node ) ;
case SyntaxKind . ConditionalExpression :
return emitConditionalExpression ( < ConditionalExpression > node ) ;
2014-12-12 01:23:57 +01:00
case SyntaxKind . SpreadElementExpression :
return emitSpreadElementExpression ( < SpreadElementExpression > node ) ;
2015-04-10 07:57:13 +02:00
case SyntaxKind . YieldExpression :
return emitYieldExpression ( < YieldExpression > node ) ;
2014-10-11 01:44:14 +02:00
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 ) ;
2015-02-06 01:19:04 +01:00
case SyntaxKind . ForOfStatement :
2014-10-11 01:44:14 +02:00
case SyntaxKind . ForInStatement :
2015-02-06 01:19:04 +01:00
return emitForInOrForOfStatement ( < ForInStatement > node ) ;
2014-10-11 01:44:14 +02:00
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 ) ;
2014-10-11 01:44:14 +02:00
case SyntaxKind . ThrowStatement :
return emitThrowStatement ( < ThrowStatement > node ) ;
case SyntaxKind . TryStatement :
return emitTryStatement ( < TryStatement > node ) ;
2014-12-02 08:17:34 +01:00
case SyntaxKind . CatchClause :
return emitCatchClause ( < CatchClause > node ) ;
2014-10-11 01:44:14 +02:00
case SyntaxKind . DebuggerStatement :
return emitDebuggerStatement ( node ) ;
case SyntaxKind . VariableDeclaration :
return emitVariableDeclaration ( < VariableDeclaration > node ) ;
2015-03-31 22:23:46 +02:00
case SyntaxKind . ClassExpression :
return emitClassExpression ( < ClassExpression > node ) ;
2014-10-11 01:44:14 +02:00
case SyntaxKind . ClassDeclaration :
2015-03-17 21:41:29 +01:00
return emitClassDeclaration ( < ClassDeclaration > node ) ;
2014-10-11 01:44:14 +02:00
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 ) ;
2014-10-11 01:44:14 +02:00
case SyntaxKind . ModuleDeclaration :
return emitModuleDeclaration ( < ModuleDeclaration > node ) ;
case SyntaxKind . ImportDeclaration :
return emitImportDeclaration ( < ImportDeclaration > node ) ;
2015-01-27 23:42:20 +01:00
case SyntaxKind . ImportEqualsDeclaration :
return emitImportEqualsDeclaration ( < ImportEqualsDeclaration > node ) ;
2015-02-13 03:05:02 +01:00
case SyntaxKind . ExportDeclaration :
return emitExportDeclaration ( < ExportDeclaration > node ) ;
2015-03-21 21:12:39 +01:00
case SyntaxKind . ExportAssignment :
return emitExportAssignment ( < ExportAssignment > node ) ;
2014-10-11 01:44:14 +02:00
case SyntaxKind . SourceFile :
2015-03-09 21:32:02 +01:00
return emitSourceFileNode ( < SourceFile > node ) ;
2014-08-07 03:42:14 +02:00
}
2014-07-17 00:39:14 +02:00
}
2014-10-11 01:44:14 +02:00
function hasDetachedComments ( pos : number ) {
2015-05-02 03:22:35 +02:00
return detachedCommentsInfo !== undefined && lastOrUndefined ( detachedCommentsInfo ) . nodePos === pos ;
2014-07-13 01:04:16 +02:00
}
2014-10-11 01:44:14 +02:00
function getLeadingCommentsWithoutDetachedComments() {
// get the leading comments from detachedPos
2015-11-07 01:21:43 +01:00
const leadingComments = getLeadingCommentRanges ( currentText ,
2015-05-02 03:22:35 +02:00
lastOrUndefined ( detachedCommentsInfo ) . detachedCommentEndPos ) ;
2014-10-11 01:44:14 +02:00
if ( detachedCommentsInfo . length - 1 ) {
detachedCommentsInfo . pop ( ) ;
2014-07-17 00:39:14 +02:00
}
else {
2014-10-11 01:44:14 +02:00
detachedCommentsInfo = undefined ;
2014-07-17 00:39:14 +02:00
}
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
return leadingComments ;
2014-07-17 00:39:14 +02:00
}
2014-07-13 01:04:16 +02:00
2015-07-17 19:33:07 +02:00
/ * *
2015-08-25 22:47:02 +02:00
* Determine if the given comment is a triple - slash
2015-07-20 18:55:04 +02:00
*
2015-08-25 22:47:02 +02:00
* @return true if the comment is a triple - slash comment else false
2015-07-20 18:55:04 +02:00
* * /
2015-09-10 22:05:51 +02:00
function isTripleSlashComment ( comment : CommentRange ) {
2015-07-20 18:55:04 +02:00
// 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
2015-11-07 01:21:43 +01:00
if ( currentText . charCodeAt ( comment . pos + 1 ) === CharacterCodes . slash &&
2015-07-20 18:55:04 +02:00
comment . pos + 2 < comment . end &&
2015-11-07 01:21:43 +01:00
currentText . charCodeAt ( comment . pos + 2 ) === CharacterCodes . slash ) {
const textSubStr = currentText . substring ( comment . pos , comment . end ) ;
2015-07-20 18:55:04 +02:00
return textSubStr . match ( fullTripleSlashReferencePathRegEx ) ||
textSubStr . match ( fullTripleSlashAMDReferencePathRegEx ) ?
true : false ;
2015-03-18 03:25:40 +01:00
}
2015-08-25 22:47:02 +02:00
return false ;
2015-03-18 03:25:40 +01:00
}
2014-10-11 01:44:14 +02: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
2015-11-07 01:21:43 +01:00
return getLeadingCommentRangesOfNodeFromText ( node , currentText ) ;
2015-01-23 00:58:00 +01:00
}
2014-07-17 00:39:14 +02: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 ) {
2015-11-07 01:21:43 +01:00
return getTrailingCommentRanges ( currentText , node . end ) ;
2015-03-18 02:34:42 +01:00
}
}
}
2015-08-21 01:29:55 +02:00
/ * *
* Emit comments associated with node that will not be emitted into JS file
* /
2015-08-20 01:19:17 +02:00
function emitCommentsOnNotEmittedNode ( node : Node ) {
2015-11-14 02:43:53 +01:00
emitLeadingCommentsWorker ( node , /*isEmittedNode*/ false ) ;
2015-03-18 02:34:42 +01:00
}
function emitLeadingComments ( node : Node ) {
2015-11-14 02:43:53 +01:00
return emitLeadingCommentsWorker ( node , /*isEmittedNode*/ true ) ;
2015-03-18 02:34:42 +01:00
}
2015-08-20 01:19:17 +02:00
function emitLeadingCommentsWorker ( node : Node , isEmittedNode : boolean ) {
2015-09-01 05:33:02 +02:00
if ( compilerOptions . removeComments ) {
return ;
}
2015-10-01 00:45:23 +02:00
2015-08-25 22:47:02 +02:00
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 eventhough 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
}
2015-08-25 22:47:02 +02:00
}
2015-03-18 02:34:42 +01:00
2015-11-07 01:21:43 +01:00
emitNewLineBeforeLeadingComments ( currentLineMap , writer , node , leadingComments ) ;
2015-03-18 02:34:42 +01:00
2014-10-11 01:44:14 +02:00
// 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
2015-03-18 02:34:42 +01:00
function emitTrailingComments ( node : Node ) {
2015-09-01 05:33:02 +02:00
if ( compilerOptions . removeComments ) {
return ;
}
2014-10-11 01:44:14 +02:00
// 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 02:34:42 +01:00
2015-03-18 03:25:40 +01:00
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
2015-11-07 01:21:43 +01:00
emitComments ( currentText , currentLineMap , writer , trailingComments , /*trailingSeparator*/ false , newLine , writeComment ) ;
2014-07-13 01:04:16 +02:00
}
2015-08-07 02:20:35 +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
2015-08-07 02:20:35 +02:00
* ^ = > 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 ;
}
2015-11-07 01:21:43 +01:00
const trailingComments = getTrailingCommentRanges ( currentText , pos ) ;
2015-08-07 02:20:35 +02:00
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
2015-11-07 01:21:43 +01:00
emitComments ( currentText , currentLineMap , writer , trailingComments , /*trailingSeparator*/ true , newLine , writeComment ) ;
2015-08-07 02:20:35 +02:00
}
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 [ ] ;
2014-10-11 01:44:14 +02:00
if ( hasDetachedComments ( pos ) ) {
// get comments without detached comments
leadingComments = getLeadingCommentsWithoutDetachedComments ( ) ;
2014-12-03 00:52:29 +01:00
}
else {
2014-10-11 01:44:14 +02:00
// get the leading comments from the node
2015-11-07 01:21:43 +01:00
leadingComments = getLeadingCommentRanges ( currentText , pos ) ;
2014-07-17 00:39:14 +02:00
}
2015-03-18 02:34:42 +01:00
2015-11-07 01:21:43 +01:00
emitNewLineBeforeLeadingComments ( currentLineMap , writer , { pos : pos , end : pos } , leadingComments ) ;
2015-03-18 02:34:42 +01:00
2014-10-11 01:44:14 +02:00
// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
2015-11-07 01:21:43 +01:00
emitComments ( currentText , currentLineMap , writer , leadingComments , /*trailingSeparator*/ true , newLine , writeComment ) ;
2014-10-11 01:44:14 +02:00
}
2014-07-13 01:04:16 +02:00
2015-10-30 20:43:22 +01:00
function emitDetachedCommentsAndUpdateCommentsInfo ( node : TextRange ) {
2015-11-07 01:21:43 +01:00
const currentDetachedCommentInfo = emitDetachedComments ( currentText , currentLineMap , writer , writeComment , node , newLine , compilerOptions . removeComments ) ;
2014-08-07 02:06:59 +02:00
2015-10-30 20:43:22 +01:00
if ( currentDetachedCommentInfo ) {
if ( detachedCommentsInfo ) {
detachedCommentsInfo . push ( currentDetachedCommentInfo ) ;
}
else {
detachedCommentsInfo = [ currentDetachedCommentInfo ] ;
2014-08-07 02:06:59 +02:00
}
2014-07-17 00:39:14 +02:00
}
2014-07-13 01:04:16 +02:00
}
2015-10-01 00:45:23 +02:00
2015-11-24 07:38:05 +01: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() {
2015-11-07 01:21:43 +01:00
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
}
2015-10-30 20:54:30 +01:00
function emitFile ( { jsFilePath , sourceMapFilePath , declarationFilePath } : { jsFilePath : string , sourceMapFilePath : string , declarationFilePath : string } ,
sourceFiles : SourceFile [ ] , isBundledEmit : boolean ) {
2015-10-20 22:40:34 +02:00
// Make sure not to write js File and source map file if any of them cannot be written
2015-11-30 21:46:53 +01:00
if ( ! host . isEmitBlocked ( jsFilePath ) && ! compilerOptions . noEmit ) {
2015-10-30 20:54:30 +01:00
emitJavaScript ( jsFilePath , sourceMapFilePath , sourceFiles , isBundledEmit ) ;
}
else {
emitSkipped = true ;
2015-09-10 18:21:01 +02:00
}
2014-12-02 08:15:13 +01:00
2015-10-30 19:36:39 +01:00
if ( declarationFilePath ) {
2015-10-30 23:04:46 +01:00
emitSkipped = writeDeclarationFile ( declarationFilePath , sourceFiles , isBundledEmit , host , resolver , emitterDiagnostics ) || emitSkipped ;
2014-12-02 08:15:13 +01:00
}
2014-09-06 01:18:39 +02:00
}
2014-07-13 01:04:16 +02:00
}
}