2014-12-16 22:14:14 +01:00
/// <reference path="checker.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 */
2014-07-13 01:04:16 +02:00
module ts {
2015-03-19 00:37:52 +01:00
export function isExternalModuleOrDeclarationFile ( sourceFile : SourceFile ) {
return isExternalModule ( sourceFile ) || isDeclarationFile ( sourceFile ) ;
2014-10-13 21:41:26 +02:00
}
2014-07-13 01:04:16 +02:00
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 ; }
__ . prototype = b . prototype ;
d . prototype = new __ ( ) ;
} ; ` ;
// 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-04-21 23:10:37 +02:00
if ( typeof Reflect === "object" && typeof Reflect . decorate === "function" ) return Reflect . decorate ( decorators , target , key , desc ) ;
2015-04-02 02:58:28 +02:00
switch ( arguments . length ) {
case 2 : return decorators . reduceRight ( function ( o , d ) { return ( d && d ( o ) ) || o ; } , target ) ;
case 3 : return decorators . reduceRight ( function ( o , d ) { return ( d && d ( target , key ) ) , void 0 ; } , void 0 ) ;
case 4 : return decorators . reduceRight ( function ( o , d ) { return ( d && d ( target , key , o ) ) || o ; } , desc ) ;
}
} ; ` ;
// 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-03-13 20:34:12 +01:00
let compilerOptions = host . getCompilerOptions ( ) ;
let languageVersion = compilerOptions . target || ScriptTarget . ES3 ;
2015-04-08 09:14:23 +02:00
let sourceMapDataList : SourceMapData [ ] = compilerOptions . sourceMap || compilerOptions . inlineSourceMap ? [ ] : undefined ;
2015-03-13 20:34:12 +01:00
let diagnostics : Diagnostic [ ] = [ ] ;
let newLine = host . getNewLine ( ) ;
2014-11-13 20:33:31 +01:00
2015-02-05 23:46:50 +01:00
if ( targetSourceFile === undefined ) {
forEach ( host . getSourceFiles ( ) , sourceFile = > {
if ( shouldEmitToOwnFile ( sourceFile , compilerOptions ) ) {
2015-03-13 20:34:12 +01:00
let jsFilePath = getOwnEmitOutputFilePath ( sourceFile , host , ".js" ) ;
2015-02-05 23:46:50 +01:00
emitFile ( jsFilePath , sourceFile ) ;
}
} ) ;
if ( compilerOptions . out ) {
emitFile ( compilerOptions . out ) ;
}
}
else {
// targetSourceFile is specified (e.g calling emitter from language service or calling getSemanticDiagnostic from language service)
if ( shouldEmitToOwnFile ( targetSourceFile , compilerOptions ) ) {
2015-03-13 20:34:12 +01:00
let jsFilePath = getOwnEmitOutputFilePath ( targetSourceFile , host , ".js" ) ;
2015-02-05 23:46:50 +01:00
emitFile ( jsFilePath , targetSourceFile ) ;
}
else if ( ! isDeclarationFile ( targetSourceFile ) && compilerOptions . out ) {
emitFile ( compilerOptions . out ) ;
}
}
// Sort and make the unique list of diagnostics
diagnostics = sortAndDeduplicateDiagnostics ( diagnostics ) ;
return {
2015-02-06 00:50:18 +01:00
emitSkipped : false ,
2015-02-05 23:46:50 +01:00
diagnostics ,
sourceMaps : sourceMapDataList
} ;
2015-03-24 00:16:29 +01:00
function isNodeDescendentOf ( node : Node , ancestor : Node ) : boolean {
while ( node ) {
if ( node === ancestor ) return true ;
node = node . parent ;
}
return false ;
}
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 ;
}
2014-10-11 01:44:14 +02:00
function emitJavaScript ( jsFilePath : string , root? : SourceFile ) {
2015-03-13 20:34:12 +01:00
let writer = createTextWriter ( newLine ) ;
let write = writer . write ;
let writeTextOfNode = writer . writeTextOfNode ;
let writeLine = writer . writeLine ;
let increaseIndent = writer . increaseIndent ;
let decreaseIndent = writer . decreaseIndent ;
2014-11-04 23:43:43 +01:00
2015-03-13 20:34:12 +01:00
let currentSourceFile : SourceFile ;
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-03-27 00:32:27 +01:00
let generatedNameSet : Map < string > = { } ;
let nodeToGeneratedName : string [ ] = [ ] ;
2015-03-24 00:16:29 +01:00
let blockScopedVariableToGeneratedName : string [ ] ;
2015-03-25 03:57:21 +01:00
let computedPropertyNamesToGeneratedNames : string [ ] ;
2015-02-14 09:48:46 +01:00
2015-03-13 20:34:12 +01:00
let extendsEmitted = false ;
2015-03-17 21:41:29 +01:00
let decorateEmitted = false ;
2015-04-02 02:58:28 +02:00
let paramEmitted = false ;
2015-03-26 18:51:07 +01:00
let 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-03-21 23:09:54 +01:00
let hasExportStars : boolean ;
2014-07-13 01:04:16 +02:00
2015-05-13 04:58:12 +02:00
/** Write emitted output to disk */
2015-03-13 20:34:12 +01:00
let writeEmittedFiles = writeJavaScriptFile ;
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
2015-03-13 20:34:12 +01:00
let writeComment = writeCommentRange ;
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
/** Emit a node */
2015-03-13 20:34:12 +01:00
let emit = emitNodeWithoutSourceMap ;
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
/** Called just before starting emit of a node */
2015-03-13 20:34:12 +01:00
let emitStart = function ( node : Node ) { } ;
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
/** Called once the emit of the node is done */
2015-03-13 20:34:12 +01:00
let emitEnd = function ( node : Node ) { } ;
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02: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
2015-02-05 22:57:27 +01:00
* This by default writes the text provided with the given tokenKind
2014-10-11 01:44:14 +02:00
* 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 * /
2015-03-13 20:34:12 +01:00
let emitToken = emitTokenText ;
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
/ * * C a l l e d t o b e f o r e s t a r t i n g t h e l e x i c a l s c o p e s a s i n f u n c t i o n / c l a s s i n t h e e m i t t e d c o d e b e c a u s e o f n o d e
* @param scopeDeclaration node that starts the lexical scope
* @param scopeName Optional name of this scope instead of deducing one from the declaration node * /
2015-03-13 20:34:12 +01:00
let scopeEmitStart = function ( scopeDeclaration : Node , scopeName? : string ) { }
2014-07-17 00:39:14 +02:00
2014-10-11 01:44:14 +02:00
/** Called after coming out of the scope */
2015-03-13 20:34:12 +01:00
let scopeEmitEnd = function ( ) { }
2014-07-17 00:39:14 +02: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-04-08 09:14:23 +02:00
if ( compilerOptions . sourceMap || compilerOptions . inlineSourceMap ) {
2015-02-05 23:41:04 +01:00
initializeEmitterWithSourceMaps ( ) ;
}
if ( root ) {
2015-03-09 21:32:02 +01:00
// Do not call emit directly. It does not set the currentSourceFile.
emitSourceFile ( root ) ;
2015-02-05 23:41:04 +01:00
}
else {
forEach ( host . getSourceFiles ( ) , sourceFile = > {
if ( ! isExternalModuleOrDeclarationFile ( sourceFile ) ) {
2015-03-09 21:32:02 +01:00
emitSourceFile ( sourceFile ) ;
2015-02-05 23:41:04 +01:00
}
} ) ;
}
writeLine ( ) ;
writeEmittedFiles ( writer . getText ( ) , /*writeByteOrderMark*/ compilerOptions . emitBOM ) ;
return ;
2015-03-09 21:32:02 +01:00
function emitSourceFile ( sourceFile : SourceFile ) : void {
currentSourceFile = sourceFile ;
2015-04-10 21:10:38 +02:00
exportFunctionForFile = undefined ;
2015-03-09 21:32:02 +01:00
emit ( sourceFile ) ;
}
2015-03-24 00:16:29 +01:00
function isUniqueName ( name : string ) : boolean {
return ! resolver . hasGlobalName ( name ) &&
! hasProperty ( currentSourceFile . identifiers , 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 ) ) {
var name = flags === TempFlags . _i ? "_i" : "_n"
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 ) {
let count = tempFlags & TempFlags . CountMask ;
tempFlags ++ ;
2015-03-27 00:32:27 +01:00
// Skip over 'i' and 'n'
if ( count !== 8 && count !== 13 ) {
let name = count < 26 ? "_" + String . fromCharCode ( CharacterCodes . a + count ) : "_" + ( count - 26 ) ;
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-03-26 18:51:07 +01:00
let 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-26 18:51:07 +01:00
function assignGeneratedName ( node : Node , name : string ) {
2015-03-27 00:32:27 +01:00
nodeToGeneratedName [ getNodeId ( node ) ] = unescapeIdentifier ( name ) ;
2015-02-14 09:48:46 +01:00
}
2015-03-24 00:16:29 +01:00
function generateNameForFunctionOrClassDeclaration ( node : Declaration ) {
if ( ! node . name ) {
2015-03-26 18:51:07 +01:00
assignGeneratedName ( node , makeUniqueName ( "default" ) ) ;
2015-03-04 06:34:20 +01:00
}
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 generateNameForModuleOrEnum ( node : ModuleDeclaration | EnumDeclaration ) {
if ( node . name . kind === SyntaxKind . Identifier ) {
let name = node . name . text ;
// Use module/enum name itself if it is unique, otherwise make a unique variation
2015-03-26 18:51:07 +01:00
assignGeneratedName ( node , isUniqueLocalName ( name , node ) ? name : makeUniqueName ( name ) ) ;
2015-03-04 06:34:20 +01:00
}
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 ) {
let expr = getExternalModuleName ( node ) ;
let baseName = expr . kind === SyntaxKind . StringLiteral ?
escapeIdentifier ( makeIdentifierFromModuleName ( ( < LiteralExpression > expr ) . text ) ) : "module" ;
2015-03-26 18:51:07 +01:00
assignGeneratedName ( node , makeUniqueName ( baseName ) ) ;
2015-03-24 00:16:29 +01:00
}
function generateNameForImportDeclaration ( node : ImportDeclaration ) {
2015-03-25 08:06:38 +01:00
if ( node . importClause ) {
2015-03-24 00:16:29 +01:00
generateNameForImportOrExportDeclaration ( node ) ;
}
}
function generateNameForExportDeclaration ( node : ExportDeclaration ) {
if ( node . module Specifier ) {
generateNameForImportOrExportDeclaration ( node ) ;
}
}
function generateNameForExportAssignment ( node : ExportAssignment ) {
if ( node . expression && node . expression . kind !== SyntaxKind . Identifier ) {
2015-03-26 18:51:07 +01:00
assignGeneratedName ( node , makeUniqueName ( "default" ) ) ;
}
}
function generateNameForNode ( node : Node ) {
switch ( node . kind ) {
case SyntaxKind . FunctionDeclaration :
case SyntaxKind . ClassDeclaration :
2015-04-02 02:28:59 +02:00
case SyntaxKind . ClassExpression :
2015-03-26 18:51:07 +01:00
generateNameForFunctionOrClassDeclaration ( < Declaration > node ) ;
break ;
case SyntaxKind . ModuleDeclaration :
generateNameForModuleOrEnum ( < ModuleDeclaration > node ) ;
generateNameForNode ( ( < ModuleDeclaration > node ) . body ) ;
break ;
case SyntaxKind . EnumDeclaration :
generateNameForModuleOrEnum ( < EnumDeclaration > node ) ;
break ;
case SyntaxKind . ImportDeclaration :
generateNameForImportDeclaration ( < ImportDeclaration > node ) ;
break ;
case SyntaxKind . ExportDeclaration :
generateNameForExportDeclaration ( < ExportDeclaration > node ) ;
break ;
case SyntaxKind . ExportAssignment :
generateNameForExportAssignment ( < ExportAssignment > node ) ;
break ;
2015-03-04 06:34:20 +01:00
}
2015-03-24 00:16:29 +01:00
}
function getGeneratedNameForNode ( node : Node ) {
let nodeId = getNodeId ( node ) ;
2015-03-27 00:32:27 +01:00
if ( ! nodeToGeneratedName [ nodeId ] ) {
2015-03-24 00:16:29 +01:00
generateNameForNode ( node ) ;
}
2015-03-27 00:32:27 +01:00
return nodeToGeneratedName [ nodeId ] ;
2015-02-14 09:48:46 +01:00
}
2014-10-11 01:44:14 +02:00
function initializeEmitterWithSourceMaps() {
2015-03-13 20:34:12 +01:00
let sourceMapDir : string ; // The directory in which sourcemap will be
2014-10-11 01:44:14 +02:00
// Current source map file and its index in the sources list
2015-03-13 20:34:12 +01:00
let sourceMapSourceIndex = - 1 ;
2014-10-11 01:44:14 +02:00
// Names and its index map
2015-03-13 20:34:12 +01:00
let sourceMapNameIndexMap : Map < number > = { } ;
let sourceMapNameIndices : number [ ] = [ ] ;
2014-10-11 01:44:14 +02:00
function getSourceMapNameIndex() {
2015-05-02 03:22:35 +02:00
return sourceMapNameIndices . length ? lastOrUndefined ( sourceMapNameIndices ) : - 1 ;
2014-07-17 00:39:14 +02:00
}
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
// Last recorded and encoded spans
2015-03-13 20:34:12 +01:00
let lastRecordedSourceMapSpan : SourceMapSpan ;
let lastEncodedSourceMapSpan : SourceMapSpan = {
2014-10-11 01:44:14 +02:00
emittedLine : 1 ,
emittedColumn : 1 ,
sourceLine : 1 ,
sourceColumn : 1 ,
sourceIndex : 0
} ;
2015-03-13 20:34:12 +01:00
let lastEncodedNameIndex = 0 ;
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
// Encoding for sourcemap span
function encodeLastRecordedSourceMapSpan() {
if ( ! lastRecordedSourceMapSpan || lastRecordedSourceMapSpan === lastEncodedSourceMapSpan ) {
2014-07-17 00:39:14 +02:00
return ;
2014-10-13 06:10:04 +02:00
}
2014-07-13 01:04:16 +02:00
2015-03-13 20:34:12 +01:00
let prevEncodedEmittedColumn = lastEncodedSourceMapSpan . emittedColumn ;
2014-10-11 01:44:14 +02:00
// Line/Comma delimiters
if ( lastEncodedSourceMapSpan . emittedLine == lastRecordedSourceMapSpan . emittedLine ) {
// Emit comma to separate the entry
if ( sourceMapData . sourceMapMappings ) {
sourceMapData . sourceMapMappings += "," ;
}
2014-10-13 06:10:04 +02:00
}
else {
2014-10-11 01:44:14 +02:00
// Emit line delimiters
2015-03-13 20:34:12 +01:00
for ( let encodedLine = lastEncodedSourceMapSpan . emittedLine ; encodedLine < lastRecordedSourceMapSpan . emittedLine ; encodedLine ++ ) {
2014-10-11 01:44:14 +02:00
sourceMapData . sourceMapMappings += ";" ;
}
prevEncodedEmittedColumn = 1 ;
2014-10-13 06:10:04 +02:00
}
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
// 1. Relative Column 0 based
sourceMapData . sourceMapMappings += base64VLQFormatEncode ( lastRecordedSourceMapSpan . emittedColumn - prevEncodedEmittedColumn ) ;
2014-07-13 01:04:16 +02:00
2015-02-05 22:57:27 +01:00
// 2. Relative sourceIndex
2014-10-11 01:44:14 +02:00
sourceMapData . sourceMapMappings += base64VLQFormatEncode ( lastRecordedSourceMapSpan . sourceIndex - lastEncodedSourceMapSpan . sourceIndex ) ;
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
// 3. Relative sourceLine 0 based
sourceMapData . sourceMapMappings += base64VLQFormatEncode ( lastRecordedSourceMapSpan . sourceLine - lastEncodedSourceMapSpan . sourceLine ) ;
2014-07-13 01:04:16 +02:00
2015-02-05 22:57:27 +01:00
// 4. Relative sourceColumn 0 based
2014-10-11 01:44:14 +02:00
sourceMapData . sourceMapMappings += base64VLQFormatEncode ( lastRecordedSourceMapSpan . sourceColumn - lastEncodedSourceMapSpan . sourceColumn ) ;
2014-11-04 02:15:14 +01:00
2014-10-11 01:44:14 +02:00
// 5. Relative namePosition 0 based
if ( lastRecordedSourceMapSpan . nameIndex >= 0 ) {
sourceMapData . sourceMapMappings += base64VLQFormatEncode ( lastRecordedSourceMapSpan . nameIndex - lastEncodedNameIndex ) ;
lastEncodedNameIndex = lastRecordedSourceMapSpan . nameIndex ;
}
2014-11-04 02:15:14 +01:00
2014-10-11 01:44:14 +02:00
lastEncodedSourceMapSpan = lastRecordedSourceMapSpan ;
sourceMapData . sourceMapDecodedMappings . push ( lastEncodedSourceMapSpan ) ;
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
function base64VLQFormatEncode ( inValue : number ) {
function base64FormatEncode ( inValue : number ) {
if ( inValue < 64 ) {
return 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' . charAt ( inValue ) ;
}
throw TypeError ( inValue + ": not a 64 based value" ) ;
}
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
// Add a new least significant bit that has the sign of the value.
// if negative number the least significant bit that gets added to the number has value 1
// else least significant bit value that gets added is 0
// eg. -1 changes to binary : 01 [1] => 3
// +1 changes to binary : 01 [0] => 2
if ( inValue < 0 ) {
inValue = ( ( - inValue ) << 1 ) + 1 ;
2014-11-19 11:11:17 +01:00
}
else {
2014-10-11 01:44:14 +02:00
inValue = inValue << 1 ;
2014-11-19 11:11:17 +01:00
}
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
// Encode 5 bits at a time starting from least significant bits
2015-03-13 20:34:12 +01:00
let encodedStr = "" ;
2014-10-11 01:44:14 +02:00
do {
2015-03-13 20:34:12 +01:00
let currentDigit = inValue & 31 ; // 11111
2014-10-11 01:44:14 +02:00
inValue = inValue >> 5 ;
if ( inValue > 0 ) {
// There are still more digits to decode, set the msb (6th bit)
currentDigit = currentDigit | 32 ;
}
encodedStr = encodedStr + base64FormatEncode ( currentDigit ) ;
} while ( inValue > 0 ) ;
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
return encodedStr ;
}
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 recordSourceMapSpan ( pos : number ) {
2015-03-13 20:34:12 +01:00
let sourceLinePos = getLineAndCharacterOfPosition ( currentSourceFile , pos ) ;
2015-02-17 02:47:32 +01:00
// Convert the location to be one-based.
sourceLinePos . line ++ ;
sourceLinePos . character ++ ;
2015-03-13 20:34:12 +01:00
let emittedLine = writer . getLine ( ) ;
let emittedColumn = writer . getColumn ( ) ;
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
// If this location wasn't recorded or the location in source is going backwards, record the span
if ( ! lastRecordedSourceMapSpan ||
lastRecordedSourceMapSpan . emittedLine != emittedLine ||
lastRecordedSourceMapSpan . emittedColumn != emittedColumn ||
( lastRecordedSourceMapSpan . sourceIndex === sourceMapSourceIndex &&
2015-02-07 05:16:35 +01:00
( lastRecordedSourceMapSpan . sourceLine > sourceLinePos . line ||
( lastRecordedSourceMapSpan . sourceLine === sourceLinePos . line && lastRecordedSourceMapSpan . sourceColumn > sourceLinePos . character ) ) ) ) {
2014-10-11 01:44:14 +02:00
// Encode the last recordedSpan before assigning new
encodeLastRecordedSourceMapSpan ( ) ;
2014-11-28 19:13:52 +01:00
2014-10-11 01:44:14 +02:00
// New span
lastRecordedSourceMapSpan = {
emittedLine : emittedLine ,
emittedColumn : emittedColumn ,
sourceLine : sourceLinePos.line ,
sourceColumn : sourceLinePos.character ,
nameIndex : getSourceMapNameIndex ( ) ,
sourceIndex : sourceMapSourceIndex
} ;
2014-12-03 00:52:29 +01:00
}
else {
2014-10-11 01:44:14 +02:00
// Take the new pos instead since there is no change in emittedLine and column since last location
lastRecordedSourceMapSpan . sourceLine = sourceLinePos . line ;
lastRecordedSourceMapSpan . sourceColumn = sourceLinePos . character ;
lastRecordedSourceMapSpan . sourceIndex = sourceMapSourceIndex ;
2014-12-03 00:52:29 +01:00
}
2014-11-10 22:05:47 +01:00
}
2014-11-28 19:13:52 +01:00
2014-10-11 01:44:14 +02:00
function recordEmitNodeStartSpan ( node : Node ) {
// Get the token pos after skipping to the token (ignoring the leading trivia)
recordSourceMapSpan ( skipTrivia ( currentSourceFile . text , node . pos ) ) ;
2014-11-28 19:13:52 +01:00
}
2014-10-11 01:44:14 +02:00
function recordEmitNodeEndSpan ( node : Node ) {
recordSourceMapSpan ( node . end ) ;
2014-11-28 19:13:52 +01:00
}
2014-10-11 01:44:14 +02:00
function writeTextWithSpanRecord ( tokenKind : SyntaxKind , startPos : number , emitFn ? : ( ) = > void ) {
2015-03-13 20:34:12 +01:00
let tokenStartPos = ts . skipTrivia ( currentSourceFile . text , startPos ) ;
2014-10-11 01:44:14 +02:00
recordSourceMapSpan ( tokenStartPos ) ;
2015-03-13 20:34:12 +01:00
let tokenEndPos = emitTokenText ( tokenKind , tokenStartPos , emitFn ) ;
2014-10-11 01:44:14 +02:00
recordSourceMapSpan ( tokenEndPos ) ;
return tokenEndPos ;
2014-11-28 19:13:52 +01:00
}
2014-10-11 01:44:14 +02:00
function recordNewSourceFileStart ( node : SourceFile ) {
// Add the file to tsFilePaths
2015-02-05 22:57:27 +01:00
// If sourceroot option: Use the relative path corresponding to the common directory path
2014-10-11 01:44:14 +02:00
// otherwise source locations relative to map file location
2015-03-13 20:34:12 +01:00
let sourcesDirectoryPath = compilerOptions . sourceRoot ? host . getCommonSourceDirectory ( ) : sourceMapDir ;
2014-11-28 19:13:52 +01:00
2014-10-11 01:44:14 +02:00
sourceMapData . sourceMapSources . push ( getRelativePathToDirectoryOrUrl ( sourcesDirectoryPath ,
2015-02-04 01:08:46 +01:00
node . fileName ,
2014-12-16 23:42:58 +01:00
host . getCurrentDirectory ( ) ,
host . getCanonicalFileName ,
2014-10-11 01:44:14 +02:00
/*isAbsolutePathAnUrl*/ true ) ) ;
sourceMapSourceIndex = sourceMapData . sourceMapSources . length - 1 ;
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
// The one that can be used from program to get the actual source file
2015-02-04 01:08:46 +01:00
sourceMapData . inputSourceFileNames . push ( node . fileName ) ;
2015-04-21 05:33:31 +02:00
if ( compilerOptions . inlineSources ) {
if ( ! sourceMapData . sourceMapSourcesContent ) {
sourceMapData . sourceMapSourcesContent = [ ] ;
}
sourceMapData . sourceMapSourcesContent . push ( node . text ) ;
}
2014-11-28 19:13:52 +01:00
}
2014-10-11 01:44:14 +02:00
function recordScopeNameOfNode ( node : Node , scopeName? : string ) {
function recordScopeNameIndex ( scopeNameIndex : number ) {
sourceMapNameIndices . push ( scopeNameIndex ) ;
2014-11-28 19:13:52 +01:00
}
2014-10-11 01:44:14 +02:00
function recordScopeNameStart ( scopeName : string ) {
2015-03-13 20:34:12 +01:00
let scopeNameIndex = - 1 ;
2014-10-11 01:44:14 +02:00
if ( scopeName ) {
2015-03-13 20:34:12 +01:00
let parentIndex = getSourceMapNameIndex ( ) ;
2014-10-11 01:44:14 +02:00
if ( parentIndex !== - 1 ) {
2015-01-21 20:01:05 +01:00
// Child scopes are always shown with a dot (even if they have no name),
// unless it is a computed property. Then it is shown with brackets,
// but the brackets are included in the name.
2015-03-13 20:34:12 +01:00
let name = ( < Declaration > node ) . name ;
2015-01-21 20:01:05 +01:00
if ( ! name || name . kind !== SyntaxKind . ComputedPropertyName ) {
scopeName = "." + scopeName ;
}
scopeName = sourceMapData . sourceMapNames [ parentIndex ] + scopeName ;
2014-10-11 01:44:14 +02:00
}
2014-11-28 19:13:52 +01:00
2014-10-11 01:44:14 +02:00
scopeNameIndex = getProperty ( sourceMapNameIndexMap , scopeName ) ;
if ( scopeNameIndex === undefined ) {
scopeNameIndex = sourceMapData . sourceMapNames . length ;
sourceMapData . sourceMapNames . push ( scopeName ) ;
sourceMapNameIndexMap [ scopeName ] = scopeNameIndex ;
}
2014-12-02 20:39:04 +01:00
}
2014-10-11 01:44:14 +02:00
recordScopeNameIndex ( scopeNameIndex ) ;
2014-12-02 20:39:04 +01:00
}
2014-10-11 01:44:14 +02:00
if ( scopeName ) {
// The scope was already given a name use it
recordScopeNameStart ( scopeName ) ;
2014-12-02 20:39:04 +01:00
}
2014-10-11 01:44:14 +02:00
else if ( node . kind === SyntaxKind . FunctionDeclaration ||
node . kind === SyntaxKind . FunctionExpression ||
2014-12-09 01:16:11 +01:00
node . kind === SyntaxKind . MethodDeclaration ||
node . kind === SyntaxKind . MethodSignature ||
2014-10-11 01:44:14 +02:00
node . kind === SyntaxKind . GetAccessor ||
node . kind === SyntaxKind . SetAccessor ||
node . kind === SyntaxKind . ModuleDeclaration ||
node . kind === SyntaxKind . ClassDeclaration ||
node . kind === SyntaxKind . EnumDeclaration ) {
// Declaration and has associated name use it
if ( ( < Declaration > node ) . name ) {
2015-03-13 20:34:12 +01:00
let name = ( < Declaration > node ) . name ;
2015-01-21 20:01:05 +01:00
// For computed property names, the text will include the brackets
scopeName = name . kind === SyntaxKind . ComputedPropertyName
? getTextOfNode ( name )
: ( < Identifier > ( < Declaration > node ) . name ) . text ;
2014-12-02 20:39:04 +01:00
}
2014-10-11 01:44:14 +02:00
recordScopeNameStart ( scopeName ) ;
2014-12-02 20:39:04 +01:00
}
else {
2014-10-11 01:44:14 +02:00
// Block just use the name from upper level scope
recordScopeNameIndex ( getSourceMapNameIndex ( ) ) ;
2014-12-02 20:39:04 +01:00
}
}
2014-10-11 01:44:14 +02:00
function recordScopeNameEnd() {
sourceMapNameIndices . pop ( ) ;
} ;
2014-07-13 01:04:16 +02:00
2014-10-13 19:53:57 +02:00
function writeCommentRangeWithMap ( curentSourceFile : SourceFile , writer : EmitTextWriter , comment : CommentRange , newLine : string ) {
2014-10-11 01:44:14 +02:00
recordSourceMapSpan ( comment . pos ) ;
2014-10-13 19:53:57 +02:00
writeCommentRange ( currentSourceFile , writer , comment , newLine ) ;
2014-10-11 01:44:14 +02:00
recordSourceMapSpan ( comment . end ) ;
2014-07-13 01:04:16 +02:00
}
2014-07-17 00:39:14 +02:00
2015-04-08 09:14:23 +02:00
function serializeSourceMapContents ( version : number , file : string , sourceRoot : string , sources : string [ ] , names : string [ ] , mappings : string , sourcesContent? : string [ ] ) {
2014-10-11 01:44:14 +02:00
if ( typeof JSON !== "undefined" ) {
2015-04-08 09:14:23 +02:00
let map : any = {
version ,
file ,
sourceRoot ,
sources ,
names ,
mappings
} ;
if ( sourcesContent !== undefined ) {
map . sourcesContent = sourcesContent ;
}
return JSON . stringify ( map ) ;
2014-12-02 20:39:04 +01:00
}
2014-07-13 01:04:16 +02:00
2015-04-08 09:14:23 +02:00
return "{\"version\":" + version + ",\"file\":\"" + escapeString ( file ) + "\",\"sourceRoot\":\"" + escapeString ( sourceRoot ) + "\",\"sources\":[" + serializeStringArray ( sources ) + "],\"names\":[" + serializeStringArray ( names ) + "],\"mappings\":\"" + escapeString ( mappings ) + "\" " + ( sourcesContent !== undefined ? ",\"sourcesContent\":[" + serializeStringArray ( sourcesContent ) + "]" : "" ) + "}" ;
2014-08-22 23:40:47 +02:00
2014-10-11 01:44:14 +02:00
function serializeStringArray ( list : string [ ] ) : string {
2015-03-13 20:34:12 +01:00
let output = "" ;
for ( let i = 0 , n = list . length ; i < n ; i ++ ) {
2014-10-11 01:44:14 +02:00
if ( i ) {
output += "," ;
}
output += "\"" + escapeString ( list [ i ] ) + "\"" ;
2014-12-02 20:39:04 +01:00
}
2014-10-11 01:44:14 +02:00
return output ;
2014-12-02 20:39:04 +01:00
}
}
2014-10-11 01:44:14 +02:00
function writeJavaScriptAndSourceMapFile ( emitOutput : string , writeByteOrderMark : boolean ) {
encodeLastRecordedSourceMapSpan ( ) ;
2015-04-08 09:14:23 +02:00
let sourceMapText = serializeSourceMapContents (
2014-10-11 01:44:14 +02:00
3 ,
sourceMapData . sourceMapFile ,
sourceMapData . sourceMapSourceRoot ,
sourceMapData . sourceMapSources ,
sourceMapData . sourceMapNames ,
2015-04-08 09:14:23 +02:00
sourceMapData . sourceMapMappings ,
2015-04-21 05:33:31 +02:00
sourceMapData . sourceMapSourcesContent ) ;
2015-04-08 09:14:23 +02:00
2014-10-11 01:44:14 +02:00
sourceMapDataList . push ( sourceMapData ) ;
2014-11-28 19:13:52 +01:00
2015-04-08 09:14:23 +02:00
let sourceMapUrl : string ;
if ( compilerOptions . inlineSourceMap ) {
// Encode the sourceMap into the sourceMap url
let base64SourceMapText = convertToBase64 ( sourceMapText ) ;
sourceMapUrl = ` //# sourceMappingURL=data:application/json;base64, ${ base64SourceMapText } ` ;
}
else {
// Write source map file
writeFile ( host , diagnostics , sourceMapData . sourceMapFilePath , sourceMapText , /*writeByteOrderMark*/ false ) ;
sourceMapUrl = ` //# sourceMappingURL= ${ sourceMapData . jsSourceMappingURL } ` ;
}
2014-10-11 01:44:14 +02:00
// Write sourcemap url to the js file and write the js file
2015-04-08 09:14:23 +02:00
writeJavaScriptFile ( emitOutput + sourceMapUrl , writeByteOrderMark ) ;
2014-11-10 22:05:47 +01:00
}
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
// Initialize source map data
2015-03-13 20:34:12 +01:00
let sourceMapJsFile = getBaseFileName ( normalizeSlashes ( jsFilePath ) ) ;
2014-10-11 01:44:14 +02:00
sourceMapData = {
sourceMapFilePath : jsFilePath + ".map" ,
jsSourceMappingURL : sourceMapJsFile + ".map" ,
sourceMapFile : sourceMapJsFile ,
sourceMapSourceRoot : compilerOptions.sourceRoot || "" ,
sourceMapSources : [ ] ,
inputSourceFileNames : [ ] ,
sourceMapNames : [ ] ,
sourceMapMappings : "" ,
2015-04-21 05:33:31 +02:00
sourceMapSourcesContent : undefined ,
2014-10-11 01:44:14 +02:00
sourceMapDecodedMappings : [ ]
} ;
2014-08-19 21:06:52 +02:00
2015-02-05 22:57:27 +01:00
// Normalize source root and make sure it has trailing "/" so that it can be used to combine paths with the
2014-10-11 01:44:14 +02:00
// relative paths of the sources list in the sourcemap
sourceMapData . sourceMapSourceRoot = ts . normalizeSlashes ( sourceMapData . sourceMapSourceRoot ) ;
if ( sourceMapData . sourceMapSourceRoot . length && sourceMapData . sourceMapSourceRoot . charCodeAt ( sourceMapData . sourceMapSourceRoot . length - 1 ) !== CharacterCodes . slash ) {
sourceMapData . sourceMapSourceRoot += directorySeparator ;
2014-11-10 22:05:47 +01:00
}
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
if ( compilerOptions . mapRoot ) {
sourceMapDir = normalizeSlashes ( compilerOptions . mapRoot ) ;
if ( root ) { // emitting single module file
// For modules or multiple emit files the mapRoot will have directory structure like the sources
// So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map
2014-12-16 22:52:47 +01:00
sourceMapDir = getDirectoryPath ( getSourceFilePathInNewDir ( root , host , sourceMapDir ) ) ;
2014-10-13 06:10:04 +02:00
}
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
if ( ! isRootedDiskPath ( sourceMapDir ) && ! isUrl ( sourceMapDir ) ) {
// The relative paths are relative to the common directory
2014-12-16 22:52:47 +01:00
sourceMapDir = combinePaths ( host . getCommonSourceDirectory ( ) , sourceMapDir ) ;
2014-10-11 01:44:14 +02:00
sourceMapData . jsSourceMappingURL = getRelativePathToDirectoryOrUrl (
getDirectoryPath ( normalizePath ( jsFilePath ) ) , // get the relative sourceMapDir path based on jsFilePath
combinePaths ( sourceMapDir , sourceMapData . jsSourceMappingURL ) , // this is where user expects to see sourceMap
2014-12-16 23:42:58 +01:00
host . getCurrentDirectory ( ) ,
host . getCanonicalFileName ,
2014-10-11 01:44:14 +02:00
/*isAbsolutePathAnUrl*/ true ) ;
2014-10-13 06:10:04 +02:00
}
else {
2014-10-11 01:44:14 +02:00
sourceMapData . jsSourceMappingURL = combinePaths ( sourceMapDir , sourceMapData . jsSourceMappingURL ) ;
2014-10-13 06:10:04 +02:00
}
}
2014-10-11 01:44:14 +02:00
else {
sourceMapDir = getDirectoryPath ( normalizePath ( jsFilePath ) ) ;
2014-07-13 01:04:16 +02:00
}
2015-03-16 20:37:21 +01:00
function emitNodeWithSourceMap ( node : Node , allowGeneratedIdentifiers? : boolean ) {
2014-10-11 01:44:14 +02:00
if ( node ) {
2015-03-05 20:47:40 +01:00
if ( nodeIsSynthesized ( node ) ) {
2015-03-16 20:37:21 +01:00
return emitNodeWithoutSourceMap ( node , /*allowGeneratedIdentifiers*/ false ) ;
2015-03-05 20:47:40 +01:00
}
2014-10-11 01:44:14 +02:00
if ( node . kind != SyntaxKind . SourceFile ) {
recordEmitNodeStartSpan ( node ) ;
2015-03-16 20:37:21 +01:00
emitNodeWithoutSourceMap ( node , allowGeneratedIdentifiers ) ;
2014-10-11 01:44:14 +02:00
recordEmitNodeEndSpan ( node ) ;
}
else {
recordNewSourceFileStart ( < SourceFile > node ) ;
2015-03-16 20:37:21 +01:00
emitNodeWithoutSourceMap ( node , /*allowGeneratedIdentifiers*/ false ) ;
2014-07-17 00:39:14 +02:00
}
2014-11-23 00:38:20 +01:00
}
}
2014-10-11 01:44:14 +02:00
writeEmittedFiles = writeJavaScriptAndSourceMapFile ;
2015-03-09 23:47:23 +01:00
emit = emitNodeWithSourceMap ;
2014-10-11 01:44:14 +02:00
emitStart = recordEmitNodeStartSpan ;
emitEnd = recordEmitNodeEndSpan ;
emitToken = writeTextWithSpanRecord ;
scopeEmitStart = recordScopeNameOfNode ;
scopeEmitEnd = recordScopeNameEnd ;
writeComment = writeCommentRangeWithMap ;
2014-07-13 01:04:16 +02:00
}
2014-10-11 01:44:14 +02:00
function writeJavaScriptFile ( emitOutput : string , writeByteOrderMark : boolean ) {
2014-12-16 23:42:58 +01:00
writeFile ( host , diagnostics , 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-03-13 20:34:12 +01:00
let 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 {
let 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
2014-10-11 01:44:14 +02:00
function emitTokenText ( tokenKind : SyntaxKind , startPos : number , emitFn ? : ( ) = > void ) {
2015-03-13 20:34:12 +01:00
let 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
}
2014-10-11 01:44:14 +02:00
return startPos + tokenString . length ;
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-04-03 00:22:53 +02:00
emitNode ( nodes [ start + i ] ) ;
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 ) {
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-03-03 00:51:23 +01:00
function isBinaryOrOctalIntegerLiteral ( node : LiteralExpression , text : string ) : boolean {
if ( node . kind === SyntaxKind . NumericLiteral && text . length > 1 ) {
switch ( text . charCodeAt ( 1 ) ) {
case CharacterCodes . b :
case CharacterCodes . B :
case CharacterCodes . o :
case CharacterCodes . O :
return true ;
}
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
}
2014-10-11 01:44:14 +02:00
function emitLiteral ( node : LiteralExpression ) {
2015-03-13 20:34:12 +01:00
let 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-03-03 01:40:00 +01:00
function getLiteralText ( node : LiteralExpression ) {
// Any template literal or string literal with an extended escape
// (e.g. "\u{0067}") will need to be downleveled as a escaped string literal.
if ( languageVersion < ScriptTarget . ES6 && ( isTemplateLiteralKind ( node . kind ) || node . hasExtendedUnicodeEscape ) ) {
return getQuotedEscapedLiteralText ( '"' , node . text , '"' ) ;
}
2015-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 ) {
return getSourceTextOfNodeFromSourceFile ( currentSourceFile , node ) ;
}
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 :
return getQuotedEscapedLiteralText ( '"' , node . text , '"' ) ;
case SyntaxKind . NoSubstitutionTemplateLiteral :
return getQuotedEscapedLiteralText ( '`' , node . text , '`' ) ;
case SyntaxKind . TemplateHead :
return getQuotedEscapedLiteralText ( '`' , node . text , '${' ) ;
case SyntaxKind . TemplateMiddle :
return getQuotedEscapedLiteralText ( '}' , node . text , '${' ) ;
case SyntaxKind . TemplateTail :
return getQuotedEscapedLiteralText ( '}' , node . text , '`' ) ;
case SyntaxKind . NumericLiteral :
return node . text ;
}
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-03-13 20:34:12 +01:00
let text = getSourceTextOfNodeFromSourceFile ( currentSourceFile , node ) ;
2015-01-09 21:22:42 +01: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-03-13 20:34:12 +01:00
let 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-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-01-23 15:44:21 +01:00
write ( '"' + text + '"' ) ;
2015-01-19 11:45:48 +01:00
}
2015-03-23 19:06:44 +01:00
2015-02-24 06:29:21 +01:00
function emitDownlevelTaggedTemplateArray ( node : TaggedTemplateExpression , literalEmitter : ( literal : LiteralExpression ) = > 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-03-26 18:51:07 +01:00
let 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-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-03-13 20:34:12 +01:00
let 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-03-13 20:34:12 +01:00
let 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-03-13 20:34:12 +01:00
let 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-03-13 20:34:12 +01:00
let 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 ) {
write ( " + " )
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
}
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
// of the property expression so that we can apply decorators later. This is to ensure
// we don't introduce unintended side effects:
//
// class C {
// [_a = x]() { }
// }
2015-03-24 22:16:52 +01:00
//
// The emit for the decorated computed property decorator is:
//
// Object.defineProperty(C.prototype, _a, __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 {
2014-11-21 05:24:08 +01:00
writeTextOfNode ( currentSourceFile , 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
}
2014-11-21 05:24:08 +01:00
function isNotExpressionIdentifier ( node : Identifier ) {
2015-03-13 20:34:12 +01:00
let parent = node . parent ;
2014-10-11 01:44:14 +02:00
switch ( parent . kind ) {
case SyntaxKind . Parameter :
case SyntaxKind . VariableDeclaration :
2014-12-06 22:53:06 +01:00
case SyntaxKind . BindingElement :
2014-12-09 01:37:35 +01:00
case SyntaxKind . PropertyDeclaration :
case SyntaxKind . PropertySignature :
2014-10-11 01:44:14 +02:00
case SyntaxKind . PropertyAssignment :
2014-11-21 05:24:08 +01:00
case SyntaxKind . ShorthandPropertyAssignment :
2014-10-11 01:44:14 +02:00
case SyntaxKind . EnumMember :
2014-12-09 01:16:11 +01:00
case SyntaxKind . MethodDeclaration :
case SyntaxKind . MethodSignature :
2014-10-11 01:44:14 +02:00
case SyntaxKind . FunctionDeclaration :
case SyntaxKind . GetAccessor :
case SyntaxKind . SetAccessor :
case SyntaxKind . FunctionExpression :
case SyntaxKind . ClassDeclaration :
case SyntaxKind . InterfaceDeclaration :
case SyntaxKind . EnumDeclaration :
case SyntaxKind . ModuleDeclaration :
2015-01-27 23:42:20 +01:00
case SyntaxKind . ImportEqualsDeclaration :
2015-03-22 17:10:10 +01:00
case SyntaxKind . ImportClause :
case SyntaxKind . NamespaceImport :
2014-10-11 01:44:14 +02:00
return ( < Declaration > parent ) . name === node ;
2015-03-22 17:10:10 +01:00
case SyntaxKind . ImportSpecifier :
case SyntaxKind . ExportSpecifier :
return ( < ImportOrExportSpecifier > parent ) . name === node || ( < ImportOrExportSpecifier > parent ) . propertyName === node ;
2014-10-11 01:44:14 +02:00
case SyntaxKind . BreakStatement :
case SyntaxKind . ContinueStatement :
case SyntaxKind . ExportAssignment :
return false ;
case SyntaxKind . LabeledStatement :
return ( < LabeledStatement > node . parent ) . label === node ;
2014-07-17 00:39:14 +02:00
}
2014-07-13 01:04:16 +02:00
}
2014-11-21 05:24:08 +01:00
function emitExpressionIdentifier ( node : Identifier ) {
2015-03-24 00:16:29 +01:00
let substitution = resolver . getExpressionNameSubstitution ( node , getGeneratedNameForNode ) ;
2015-02-10 23:59:20 +01:00
if ( substitution ) {
write ( substitution ) ;
}
else {
writeTextOfNode ( currentSourceFile , node ) ;
2014-08-22 23:50:56 +02:00
}
2014-11-21 05:24:08 +01:00
}
2015-03-16 20:37:21 +01:00
function getGeneratedNameForIdentifier ( node : Identifier ) : string {
2015-03-24 00:16:29 +01:00
if ( nodeIsSynthesized ( node ) || ! blockScopedVariableToGeneratedName ) {
2015-03-16 20:37:21 +01:00
return undefined ;
}
var variableId = resolver . getBlockScopedVariableId ( node )
if ( variableId === undefined ) {
return undefined ;
}
2015-03-24 00:16:29 +01:00
return blockScopedVariableToGeneratedName [ variableId ] ;
2015-02-14 09:48:46 +01:00
}
2015-03-16 20:37:21 +01:00
function emitIdentifier ( node : Identifier , allowGeneratedIdentifiers : boolean ) {
if ( allowGeneratedIdentifiers ) {
let generatedName = getGeneratedNameForIdentifier ( node ) ;
if ( generatedName ) {
write ( generatedName ) ;
2015-02-14 09:48:46 +01:00
return ;
}
}
2014-12-04 01:43:01 +01:00
if ( ! node . parent ) {
write ( node . text ) ;
2014-07-13 01:04:16 +02:00
}
2014-12-04 01:43:01 +01:00
else if ( ! isNotExpressionIdentifier ( node ) ) {
2014-11-21 05:24:08 +01:00
emitExpressionIdentifier ( node ) ;
2014-07-17 00:39:14 +02:00
}
2014-11-21 05:24:08 +01:00
else {
writeTextOfNode ( currentSourceFile , 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
}
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-03-13 00:34:06 +01:00
var flags = resolver . getNodeCheckFlags ( node ) ;
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-03-13 20:34:12 +01:00
let 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-03-13 20:34:12 +01:00
let 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-03-16 20:37:21 +01:00
emit ( node . propertyName , /*allowGeneratedIdentifiers*/ false ) ;
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-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 ;
let 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>, ...)
2014-12-12 01:23:57 +01:00
if ( group === 1 ) {
2015-05-16 10:42:49 +02:00
if ( useConcat ) {
write ( ".concat(" ) ;
}
else {
write ( ", " ) ;
}
2014-12-12 01:23:57 +01:00
}
else if ( group > 1 ) {
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-05-16 10:42:49 +02:00
if ( useConcat ) {
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-03-13 20:34:12 +01:00
let 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-02-22 00:00:23 +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 ) {
var 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 ) {
emitLinePreservingList ( node , properties , /* allowTrailingComma */ languageVersion >= ScriptTarget . ES5 , /* spacesBetweenBraces */ true ) ;
}
else {
2015-04-06 23:19:28 +02:00
let 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-04-06 23:19:28 +02:00
let multiLine = ( node . flags & NodeFlags . MultiLine ) !== 0 ;
let 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-04-06 23:19:28 +02:00
let 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.
emit ( tempVar )
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-04-06 23:19:28 +02:00
let property = properties [ i ] ;
2015-02-20 23:16:09 +01:00
2015-02-24 02:44:48 +01:00
emitStart ( property )
if ( property . kind === SyntaxKind . GetAccessor || property . kind === SyntaxKind . SetAccessor ) {
// TODO (drosen): Reconcile with 'emitMemberFunctions'.
2015-04-06 23:19:28 +02:00
let 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 ) {
writeLine ( )
emitLeadingComments ( accessors . getAccessor ) ;
write ( "get: " ) ;
emitStart ( accessors . getAccessor ) ;
write ( "function " ) ;
emitSignatureAndBody ( accessors . getAccessor ) ;
emitEnd ( accessors . getAccessor ) ;
emitTrailingComments ( accessors . getAccessor ) ;
write ( "," ) ;
}
if ( accessors . setAccessor ) {
writeLine ( ) ;
emitLeadingComments ( accessors . setAccessor ) ;
write ( "set: " ) ;
emitStart ( accessors . setAccessor ) ;
write ( "function " ) ;
emitSignatureAndBody ( accessors . setAccessor ) ;
emitEnd ( accessors . setAccessor ) ;
emitTrailingComments ( accessors . setAccessor ) ;
write ( "," ) ;
}
writeLine ( ) ;
write ( "enumerable: true," ) ;
writeLine ( ) ;
write ( "configurable: true" ) ;
decreaseIndent ( ) ;
writeLine ( ) ;
write ( "})" ) ;
emitEnd ( property ) ;
}
else {
emitLeadingComments ( property ) ;
emitStart ( property . name ) ;
emit ( tempVar ) ;
emitMemberAccessForPropertyName ( property . name ) ;
emitEnd ( property . name ) ;
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-03-13 20:34:12 +01:00
let properties = node . properties ;
2015-02-05 03:35:04 +01:00
2015-02-18 21:01:24 +01:00
if ( languageVersion < ScriptTarget . ES6 ) {
2015-03-13 20:34:12 +01:00
let 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-03-13 20:34:12 +01:00
let 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 {
let result = < BinaryExpression > createSynthesizedNode ( SyntaxKind . BinaryExpression , startsOnNewLine ) ;
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-04-06 23:19:28 +02:00
let result = < PropertyAccessExpression > createSynthesizedNode ( SyntaxKind . PropertyAccessExpression ) ;
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-04-11 03:56:11 +02:00
function createElementAccessExpression ( expression : Expression , argumentExpression : Expression ) : ElementAccessExpression {
2015-04-06 23:19:28 +02:00
let result = < ElementAccessExpression > createSynthesizedNode ( SyntaxKind . ElementAccessExpression ) ;
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-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
//
if ( isLeftHandSideExpression ( expr ) && expr . kind !== SyntaxKind . NewExpression && expr . kind !== SyntaxKind . NumericLiteral ) {
2015-04-11 03:56:11 +02:00
return < LeftHandSideExpression > expr ;
}
let node = < ParenthesizedExpression > createSynthesizedNode ( SyntaxKind . ParenthesizedExpression ) ;
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-03-16 20:37:21 +01:00
emit ( node . name , /*allowGeneratedIdentifiers*/ false ) ;
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-03-16 20:37:21 +01:00
emit ( node . name , /*allowGeneratedIdentifiers*/ false ) ;
2014-11-25 21:12:55 +01:00
write ( ": " ) ;
2014-10-11 01:44:14 +02:00
emit ( node . initializer ) ;
2014-11-25 21:12:55 +01:00
}
2014-12-08 23:34:00 +01:00
function emitShorthandPropertyAssignment ( node : ShorthandPropertyAssignment ) {
2015-03-16 20:37:21 +01:00
emit ( node . name , /*allowGeneratedIdentifiers*/ false ) ;
2014-12-02 22:29:49 +01:00
// If short-hand property has a prefix, then regardless of the target version, we will emit it as normal property assignment. For example:
// module m {
2015-03-13 20:34:12 +01:00
// export let y;
2014-12-02 22:29:49 +01:00
// }
// module m {
2015-03-13 20:34:12 +01:00
// export let obj = { y };
2014-12-02 22:29:49 +01:00
// }
// The short-hand property in obj need to emit as such ... = { y : m.y } regardless of the TargetScript version
2015-03-16 20:37:21 +01:00
if ( languageVersion < ScriptTarget . ES6 ) {
// Emit identifier as an identifier
write ( ": " ) ;
var generatedName = getGeneratedNameForIdentifier ( node . name ) ;
if ( generatedName ) {
write ( generatedName ) ;
}
else {
// Even though this is stored as identifier treat it as an expression
// Short-hand, { x }, is equivalent of normal form { x: x }
emitExpressionIdentifier ( node . name ) ;
}
}
2015-03-24 00:16:29 +01:00
else if ( resolver . getExpressionNameSubstitution ( node . name , getGeneratedNameForNode ) ) {
2014-12-08 23:34:00 +01:00
// Emit identifier as an identifier
write ( ": " ) ;
// Even though this is stored as identifier treat it as an expression
// Short-hand, { x }, is equivalent of normal form { x: x }
emitExpressionIdentifier ( node . name ) ;
2014-11-21 05:24:08 +01:00
}
}
2014-11-30 00:58:55 +01:00
function tryEmitConstantValue ( node : PropertyAccessExpression | ElementAccessExpression ) : boolean {
2015-03-31 04:33:15 +02:00
if ( compilerOptions . separateCompilation ) {
// do not inline enum values in separate compilation mode
return false ;
}
2015-03-13 20:34:12 +01:00
let constantValue = resolver . getConstantValue ( 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-03-13 20:34:12 +01:00
let 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-03-09 23:01:02 +01:00
// Returns 'true' if the code was actually indented, false otherwise.
// If the code is not indented, an optional valueToWriteWhenNotIndenting will be
// emitted instead.
function indentIfOnDifferentLines ( parent : Node , node1 : Node , node2 : Node , valueToWriteWhenNotIndenting? : string ) : boolean {
2015-03-26 21:46:35 +01:00
let 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-03-13 20:34:12 +01:00
let 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-03-13 20:34:12 +01:00
let indentedBeforeDot = indentIfOnDifferentLines ( node , node . expression , node . dotToken ) ;
2014-11-21 05:24:08 +01:00
write ( "." ) ;
2015-03-13 20:34:12 +01:00
let indentedAfterDot = indentIfOnDifferentLines ( node , node . dotToken , node . name ) ;
2015-03-16 20:37:21 +01:00
emit ( node . name , /*allowGeneratedIdentifiers*/ false ) ;
2015-03-09 09:07:02 +01:00
decreaseIndentIf ( indentedBeforeDot , indentedAfterDot ) ;
2014-07-13 01:04:16 +02:00
}
2014-08-22 23:35:07 +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
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 {
while ( node . kind === SyntaxKind . ParenthesizedExpression || node . kind === SyntaxKind . TypeAssertionExpression ) {
2015-02-06 16:39:11 +01:00
node = ( < ParenthesizedExpression | TypeAssertion > 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-03-26 18:51:07 +01:00
let 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 ;
let 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 {
2015-05-15 02:16:01 +02: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 {
2015-05-15 02:16:01 +02:00
write ( "(" ) ;
emitCommaList ( node . arguments ) ;
write ( ")" ) ;
}
}
function emitNewExpression ( node : NewExpression ) {
2014-10-11 01:44:14 +02:00
write ( "new " ) ;
2015-05-13 03:36:40 +02:00
// Spread operator logic can be supported in new expressions in ES5 using a combination
// of Function.prototype.bind() and Function.prototype.apply().
//
// Example:
//
// var arguments = [1, 2, 3, 4, 5];
// new Array(...arguments);
//
// Could be transpiled into ES5:
//
// var arguments = [1, 2, 3, 4, 5];
2015-05-14 06:33:15 +02:00
// new (Array.bind.apply(Array, [void 0].concat(arguments)));
2015-05-13 03:36:40 +02:00
//
// `[void 0]` is the first argument which represents `thisArg` to the bind method above.
// And `thisArg` will be set to the return value of the constructor when instantiated
// with the new operator — regardless of any value we set `thisArg` to. Thus, we set it
// to an undefined, `void 0`.
if ( languageVersion === ScriptTarget . ES5 &&
node . arguments &&
hasSpreadElement ( node . arguments ) ) {
2014-10-11 01:44:14 +02:00
write ( "(" ) ;
2015-05-14 06:33:15 +02:00
let target = emitCallTarget ( node . expression ) ;
write ( ".bind.apply(" ) ;
emit ( target ) ;
2015-05-13 03:36:40 +02:00
write ( ", [void 0].concat(" ) ;
2015-05-16 10:42:49 +02: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-03-07 11:08:36 +01:00
if ( ! node . parent || node . parent . kind !== SyntaxKind . ArrowFunction ) {
if ( node . expression . kind === SyntaxKind . TypeAssertionExpression ) {
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;
while ( operand . kind == SyntaxKind . TypeAssertionExpression ) {
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 ) &&
! ( operand . kind === SyntaxKind . FunctionExpression && node . parent . kind === SyntaxKind . CallExpression ) ) {
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-03-13 20:34:12 +01:00
let 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-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 :
* - check if node is a exported source file level value to determine
* if we should also export the value after its it changed
* - check if node is a source level declaration to emit it differently ,
* i . e non - exported variable statement 'var x = 1' is hoisted so
* 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-04-13 10:32:16 +02:00
if ( ! node || languageVersion >= ScriptTarget . ES6 || ! 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 ) {
return ! isExported || ( ( getCombinedNodeFlags ( node ) & NodeFlags . Export ) !== 0 )
}
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
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 ( ` ", ` ) ;
}
2014-12-04 01:43:01 +01:00
emit ( node . left ) ;
2015-03-13 20:34:12 +01:00
let indentedBeforeOperator = indentIfOnDifferentLines ( node , node . left , node . operatorToken , node . operatorToken . kind !== SyntaxKind . CommaToken ? " " : undefined ) ;
2015-02-21 03:56:42 +01:00
write ( tokenToString ( node . operatorToken . kind ) ) ;
2015-03-13 20:34:12 +01:00
let indentedAfterOperator = indentIfOnDifferentLines ( node , node . operatorToken , node . right , " " ) ;
2015-03-07 22:33:02 +01:00
emit ( node . right ) ;
2015-03-09 09:07:02 +01:00
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-03-13 20:34:12 +01:00
let indentedBeforeQuestion = indentIfOnDifferentLines ( node , node . condition , node . questionToken , " " ) ;
2015-03-07 22:50:26 +01:00
write ( "?" ) ;
2015-03-13 20:34:12 +01:00
let 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-03-13 20:34:12 +01:00
let indentedBeforeColon = indentIfOnDifferentLines ( node , node . whenTrue , node . colonToken , " " ) ;
2015-03-07 22:50:26 +01:00
write ( ":" ) ;
2015-03-13 20:34:12 +01:00
let 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-03-09 23:01:02 +01:00
// Helper function to decrease the indent if we previously indented. Allows multiple
// previous indent values to be considered at a time. This also allows caller to just
// call this once, passing in all their appropriate indent values, instead of needing
// to call this helper function multiple times.
2015-03-09 09:07:02 +01:00
function decreaseIndentIf ( value1 : boolean , value2? : boolean ) {
if ( value1 ) {
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-03-13 20:34:12 +01:00
let 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
scopeEmitStart ( node . parent ) ;
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 ) ;
scopeEmitEnd ( ) ;
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 ) {
write ( "do" ) ;
emitEmbeddedStatement ( node . statement ) ;
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 ) {
write ( "while (" ) ;
emit ( node . expression ) ;
write ( ")" ) ;
emitEmbeddedStatement ( node . statement ) ;
}
2015-04-22 07:27:33 +02:00
/ * R e t u r n s t r u e i f s t a r t o f v a r i a b l e d e c l a r a t i o n l i s t w a s e m i t t e d .
* Return false if nothing was written - this can happen for source file level variable declarations
* in system module s - such variable declarations are hoisted .
* /
function tryEmitStartOfVariableDeclarationList ( decl : VariableDeclarationList , startPos? : number ) : 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-03-13 20:34:12 +01:00
let tokenKind = SyntaxKind . VarKeyword ;
2015-02-14 09:48:46 +01:00
if ( decl && languageVersion >= ScriptTarget . ES6 ) {
if ( isLet ( decl ) ) {
tokenKind = SyntaxKind . LetKeyword ;
}
else if ( isConst ( decl ) ) {
tokenKind = SyntaxKind . ConstKeyword ;
}
}
if ( startPos !== undefined ) {
emitToken ( tokenKind , startPos ) ;
2015-04-10 21:10:38 +02:00
write ( " " )
2015-02-14 09:48:46 +01:00
}
else {
switch ( tokenKind ) {
case SyntaxKind . VarKeyword :
2015-04-11 17:23:25 +02:00
write ( "var " ) ;
break ;
2015-02-14 09:48:46 +01:00
case SyntaxKind . LetKeyword :
2015-04-11 17:23:25 +02:00
write ( "let " ) ;
break ;
2015-02-14 09:48:46 +01:00
case SyntaxKind . ConstKeyword :
2015-04-11 17:23:25 +02:00
write ( "const " ) ;
break ;
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 ;
for ( let decl of list . declarations ) {
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
}
2014-10-11 01:44:14 +02:00
function emitForStatement ( node : ForStatement ) {
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-03-13 20:34:12 +01:00
let variableDeclarationList = < VariableDeclarationList > node . initializer ;
2015-04-22 07:27:33 +02:00
let startIsEmitted = tryEmitStartOfVariableDeclarationList ( variableDeclarationList , endPos ) ;
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 ( ")" ) ;
emitEmbeddedStatement ( node . statement ) ;
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 ) {
return emitDownLevelForOfStatement ( node ) ;
}
2015-03-23 19:06:44 +01:00
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-03-13 20:34:12 +01:00
let variableDeclarationList = < VariableDeclarationList > node . initializer ;
2014-12-16 10:09:42 +01:00
if ( variableDeclarationList . declarations . length >= 1 ) {
2015-04-22 07:27:33 +02:00
tryEmitStartOfVariableDeclarationList ( variableDeclarationList , endPos ) ;
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 ) ;
emitEmbeddedStatement ( node . statement ) ;
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 ) {
// 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-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-03-04 21:10:20 +01:00
2015-03-13 20:34:12 +01:00
// Do not emit the LHS let declaration yet, because it might contain destructuring.
2015-03-03 20:43:32 +01: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
//
// we don't want to emit a temporary variable for the RHS, just use it directly.
2015-03-13 20:34:12 +01:00
let rhsIsIdentifier = node . expression . kind === SyntaxKind . Identifier ;
2015-03-26 18:51:07 +01:00
let counter = createTempVariable ( TempFlags . _i ) ;
let rhsReference = rhsIsIdentifier ? < Identifier > node.expression : createTempVariable ( 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-03-03 20:43:32 +01: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-03-05 01:06:37 +01:00
if ( ! rhsIsIdentifier ) {
// , _a = expr
write ( ", " ) ;
2015-03-05 20:47:40 +01:00
emitStart ( node . expression ) ;
2015-03-10 18:41:26 +01:00
emitNodeWithoutSourceMap ( rhsReference ) ;
2015-03-05 01:06:37 +01:00
write ( " = " ) ;
2015-03-10 18:41:26 +01:00
emitNodeWithoutSourceMap ( node . expression ) ;
2015-03-05 20:47:40 +01:00
emitEnd ( node . expression ) ;
2015-03-05 01:06:37 +01:00
}
2015-03-14 00:15:25 +01:00
2015-03-03 20:43:32 +01:00
write ( "; " ) ;
// _i < _a.length;
2015-03-05 20:47:40 +01:00
emitStart ( node . initializer ) ;
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-03-29 08:50:52 +02:00
emitNodeWithoutSourceMap ( rhsReference ) ;
write ( ".length" ) ;
2015-03-14 00:15:25 +01:00
2015-03-05 20:47:40 +01:00
emitEnd ( node . initializer ) ;
write ( "; " ) ;
2015-03-03 20:43:32 +01:00
// _i++)
2015-03-05 20:47:40 +01:00
emitStart ( node . initializer ) ;
2015-03-10 18:41:26 +01:00
emitNodeWithoutSourceMap ( counter ) ;
2015-03-03 20:43:32 +01:00
write ( "++" ) ;
2015-03-05 20:47:40 +01:00
emitEnd ( node . initializer ) ;
2015-03-03 20:43:32 +01:00
emitToken ( SyntaxKind . CloseParenToken , node . expression . end ) ;
// Body
write ( " {" ) ;
writeLine ( ) ;
increaseIndent ( ) ;
// Initialize LHS
2015-03-13 20:34:12 +01:00
// let v = _a[_i];
let rhsIterationValue = createElementAccessExpression ( rhsReference , counter ) ;
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-03-13 20:34:12 +01:00
let variableDeclarationList = < VariableDeclarationList > node . initializer ;
2015-03-09 21:32:02 +01:00
if ( variableDeclarationList . declarations . length > 0 ) {
2015-03-13 20:34:12 +01:00
let 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-03-10 18:41:26 +01:00
emitNodeWithoutSourceMap ( 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-03-13 20:34:12 +01:00
let 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-03-10 18:41:26 +01:00
emitNodeWithoutSourceMap ( 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-03-03 20:43:32 +01:00
if ( node . statement . kind === SyntaxKind . Block ) {
emitLines ( ( < Block > node . statement ) . statements ) ;
}
else {
2015-03-04 21:10:20 +01:00
writeLine ( ) ;
2015-03-03 20:43:32 +01:00
emit ( node . statement ) ;
}
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 ) {
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 ) {
emitToken ( SyntaxKind . ReturnKeyword , node . pos ) ;
emitOptional ( " " , node . expression ) ;
write ( ";" ) ;
}
2014-08-19 19:43:13 +02:00
2014-10-11 01:44:14 +02:00
function emitWithStatement ( node : WhileStatement ) {
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-03-11 02:17:52 +01:00
emitCaseBlock ( node . caseBlock , endPos )
}
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 ) {
2014-11-21 05:24:08 +01:00
return getLineOfLocalPosition ( currentSourceFile , skipTrivia ( currentSourceFile . text , node1 . pos ) ) ===
2015-02-07 05:16:35 +01:00
getLineOfLocalPosition ( currentSourceFile , skipTrivia ( currentSourceFile . text , node2 . pos ) ) ;
}
function nodeEndPositionsAreOnSameLine ( node1 : Node , node2 : Node ) {
return getLineOfLocalPosition ( currentSourceFile , node1 . end ) ===
getLineOfLocalPosition ( currentSourceFile , node2 . end ) ;
2014-08-19 19:43:13 +02:00
}
2015-02-07 03:45:09 +01:00
function nodeEndIsOnSameLineAsNodeStart ( node1 : Node , node2 : Node ) {
return getLineOfLocalPosition ( currentSourceFile , node1 . end ) ===
getLineOfLocalPosition ( currentSourceFile , skipTrivia ( currentSourceFile . text , node2 . pos ) ) ;
}
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-03-13 20:34:12 +01:00
let 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
2014-10-11 01:44:14 +02:00
function emitLabelledStatement ( node : LabeledStatement ) {
emit ( node . label ) ;
write ( ": " ) ;
emit ( node . statement ) ;
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-03-13 20:34:12 +01:00
let 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-02-17 22:47:15 +01:00
var container = getContainingModule ( node ) ;
if ( container ) {
2015-03-24 00:16:29 +01:00
write ( getGeneratedNameForNode ( container ) ) ;
2015-02-17 22:47:15 +01:00
write ( "." ) ;
}
2015-04-10 21:10:38 +02:00
else if ( languageVersion < ScriptTarget . ES6 && compilerOptions . module !== ModuleKind . System ) {
2015-02-17 22:47:15 +01:00
write ( "exports." ) ;
}
2014-07-17 00:39:14 +02:00
}
2015-03-09 23:01:02 +01:00
emitNodeWithoutSourceMap ( 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-03-13 20:34:12 +01:00
let zero = < LiteralExpression > createSynthesizedNode ( SyntaxKind . NumericLiteral ) ;
2015-02-16 22:39:32 +01:00
zero . text = "0" ;
2015-03-13 20:34:12 +01:00
let 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-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-05-11 09:07:49 +02:00
if ( compilerOptions . module === 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 {
emitNodeWithoutSourceMap ( node . name ) ;
}
write ( ` ", ` ) ;
emitDeclarationName ( node ) ;
write ( ")" )
2015-03-21 21:12:39 +01:00
}
else {
2015-04-10 21:10:38 +02:00
if ( node . flags & NodeFlags . Default ) {
2015-04-20 22:40:13 +02:00
if ( languageVersion === ScriptTarget . ES3 ) {
write ( "exports[\"default\"]" ) ;
} else {
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-03-21 21:12:39 +01:00
if ( ! exportEquals && exportSpecifiers && hasProperty ( exportSpecifiers , name . text ) ) {
for ( let specifier of exportSpecifiers [ name . text ] ) {
2015-02-13 19:07:10 +01:00
writeLine ( ) ;
2015-04-10 21:10:38 +02:00
if ( compilerOptions . module === ModuleKind . System ) {
2015-04-29 01:18:33 +02:00
emitStart ( specifier . name ) ;
2015-04-10 21:10:38 +02:00
write ( ` ${ exportFunctionForFile } (" ` ) ;
emitNodeWithoutSourceMap ( specifier . name ) ;
write ( ` ", ` ) ;
emitExpressionIdentifier ( name ) ;
write ( ")" )
2015-04-29 01:18:33 +02:00
emitEnd ( specifier . name ) ;
2015-04-10 21:10:38 +02:00
}
else {
2015-04-29 01:18:33 +02:00
emitStart ( specifier . name ) ;
2015-04-10 21:10:38 +02:00
emitContainingModuleName ( specifier ) ;
write ( "." ) ;
emitNodeWithoutSourceMap ( specifier . name ) ;
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-02-16 22:39:32 +01: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 ) {
let isExported = getCombinedNodeFlags ( root ) & NodeFlags . Export ;
2015-04-24 06:14:03 +02:00
let 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 ) ;
2014-12-04 01:43:01 +01:00
emitBindingElement ( < BindingElement > root , value ) ;
}
function emitAssignment ( name : Identifier , value : Expression ) {
if ( emitCount ++ ) {
write ( ", " ) ;
}
2015-02-14 09:48:46 +01:00
renameNonTopLevelLetAndConst ( name ) ;
2015-04-11 13:58:39 +02:00
const isVariableDeclarationOrBindingElement =
name . parent && ( name . parent . kind === SyntaxKind . VariableDeclaration || name . parent . kind === SyntaxKind . BindingElement ) ;
2015-04-13 10:32:16 +02:00
let exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule ( name ) ;
2015-04-11 13:58:39 +02:00
2015-04-13 10:32:16 +02:00
if ( exportChanged ) {
2015-04-11 13:58:39 +02:00
write ( ` ${ exportFunctionForFile } (" ` ) ;
emitNodeWithoutSourceMap ( name ) ;
write ( ` ", ` ) ;
}
if ( isVariableDeclarationOrBindingElement ) {
2014-12-06 22:53:06 +01:00
emitModuleMemberName ( < Declaration > name . parent ) ;
2014-12-04 01:43:01 +01:00
}
else {
emit ( name ) ;
}
2015-04-11 13:58:39 +02:00
2014-12-04 01:43:01 +01:00
write ( " = " ) ;
emit ( value ) ;
2015-04-11 13:58:39 +02:00
2015-04-13 10:32:16 +02:00
if ( exportChanged ) {
2015-04-11 13:58:39 +02:00
write ( ")" ) ;
}
2014-12-04 01:43:01 +01:00
}
function ensureIdentifier ( expr : Expression ) : Expression {
if ( expr . kind !== SyntaxKind . Identifier ) {
2015-03-26 18:51:07 +01:00
let identifier = createTempVariable ( TempFlags . Auto ) ;
2015-04-22 07:27:33 +02:00
if ( ! canDefineTempVariablesInPlace ) {
2014-12-04 01:43:01 +01:00
recordTempDeclaration ( identifier ) ;
}
emitAssignment ( identifier , expr ) ;
expr = identifier ;
}
return expr ;
}
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
value = ensureIdentifier ( value ) ;
// Return the expression 'value === void 0 ? defaultValue : value'
2015-03-13 20:34:12 +01:00
let 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-03-13 20:34:12 +01:00
let 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-03-13 20:34:12 +01:00
let node = < LiteralExpression > createSynthesizedNode ( SyntaxKind . NumericLiteral ) ;
2014-12-04 01:43:01 +01:00
node . text = "" + value ;
return node ;
}
2015-04-13 23:10:03 +02:00
function createPropertyAccessForDestructuringProperty ( object : Expression , propName : Identifier | LiteralExpression ) : Expression {
2014-12-04 01:43:01 +01:00
if ( propName . kind !== SyntaxKind . Identifier ) {
2015-04-11 03:56:11 +02:00
return createElementAccessExpression ( object , propName ) ;
2014-12-04 01:43:01 +01:00
}
2015-04-11 03:56:11 +02:00
return createPropertyAccessExpression ( object , propName ) ;
2014-12-04 01:43:01 +01:00
}
2015-04-13 23:10:03 +02:00
function createSliceCall ( value : Expression , sliceIndex : number ) : CallExpression {
let call = < CallExpression > createSynthesizedNode ( SyntaxKind . CallExpression ) ;
let sliceIdentifier = < Identifier > createSynthesizedNode ( SyntaxKind . Identifier ) ;
sliceIdentifier . text = "slice" ;
call . expression = createPropertyAccessExpression ( value , sliceIdentifier ) ;
call . arguments = < NodeArray < LiteralExpression > > createSynthesizedNodeArray ( ) ;
call . arguments [ 0 ] = createNumericLiteral ( sliceIndex ) ;
return call ;
2014-12-04 01:43:01 +01:00
}
function emitObjectLiteralAssignment ( target : ObjectLiteralExpression , value : Expression ) {
2015-03-13 20:34:12 +01:00
let 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.
value = ensureIdentifier ( value ) ;
}
2015-03-13 17:41:54 +01:00
for ( let p of properties ) {
2014-12-04 01:43:01 +01:00
if ( p . kind === SyntaxKind . PropertyAssignment || p . kind === SyntaxKind . ShorthandPropertyAssignment ) {
// TODO(andersh): Computed property support
2015-04-13 23:10:03 +02:00
let propName = < Identifier | LiteralExpression > ( ( < PropertyAssignment > p ) . name ) ;
2015-04-11 03:56:11 +02:00
emitDestructuringAssignment ( ( < PropertyAssignment > p ) . initializer || propName , createPropertyAccessForDestructuringProperty ( value , propName ) ) ;
2014-12-04 01:43:01 +01:00
}
}
}
function emitArrayLiteralAssignment ( target : ArrayLiteralExpression , value : Expression ) {
2015-03-13 20:34:12 +01:00
let 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.
value = ensureIdentifier ( value ) ;
}
2015-03-13 20:34:12 +01:00
for ( let i = 0 ; i < elements . length ; i ++ ) {
let 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-04-11 03:56:11 +02:00
emitDestructuringAssignment ( e , createElementAccessExpression ( value , createNumericLiteral ( i ) ) ) ;
2014-12-12 02:41:24 +01:00
}
2015-04-11 03:56:11 +02:00
else if ( i === elements . length - 1 ) {
2015-04-13 23:10:03 +02:00
emitDestructuringAssignment ( ( < SpreadElementExpression > e ) . expression , createSliceCall ( value , i ) ) ;
2014-12-12 02:41:24 +01:00
}
2014-12-04 01:43:01 +01:00
}
}
}
function emitDestructuringAssignment ( target : Expression , value : Expression ) {
2015-02-21 03:56:42 +01:00
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 ) {
emitObjectLiteralAssignment ( < ObjectLiteralExpression > target , value ) ;
}
else if ( target . kind === SyntaxKind . ArrayLiteralExpression ) {
emitArrayLiteralAssignment ( < ArrayLiteralExpression > target , value ) ;
}
else {
emitAssignment ( < Identifier > target , value ) ;
}
}
2015-03-09 21:32:02 +01:00
function emitAssignmentExpression ( root : BinaryExpression ) {
2015-03-13 20:34:12 +01:00
let target = root . left ;
let value = root . right ;
2015-03-09 21:32:02 +01:00
if ( isAssignmentExpressionStatement ) {
emitDestructuringAssignment ( target , value ) ;
}
else {
2014-12-04 01:43:01 +01:00
if ( root . parent . kind !== SyntaxKind . ParenthesizedExpression ) {
write ( "(" ) ;
}
value = ensureIdentifier ( value ) ;
emitDestructuringAssignment ( target , value ) ;
write ( ", " ) ;
emit ( value ) ;
if ( root . parent . kind !== SyntaxKind . ParenthesizedExpression ) {
write ( ")" ) ;
}
}
}
function emitBindingElement ( target : BindingElement , value : Expression ) {
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-03-13 20:34:12 +01:00
let pattern = < BindingPattern > target . name ;
let elements = pattern . 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.
value = ensureIdentifier ( value ) ;
}
2015-03-13 20:34:12 +01:00
for ( let i = 0 ; i < elements . length ; i ++ ) {
let 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-03-13 20:34:12 +01:00
let propName = element . propertyName || < Identifier > element . name ;
2015-04-11 03:56:11 +02:00
emitBindingElement ( element , createPropertyAccessForDestructuringProperty ( value , propName ) ) ;
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-04-11 03:56:11 +02:00
emitBindingElement ( element , createElementAccessExpression ( value , createNumericLiteral ( i ) ) ) ;
2014-12-12 03:27:08 +01:00
}
2015-04-11 03:56:11 +02:00
else if ( i === elements . length - 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 {
emitAssignment ( < Identifier > target . name , value ) ;
}
}
}
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-04 01:14:03 +01:00
renameNonTopLevelLetAndConst ( < Identifier > node . name ) ;
2015-02-16 22:39:32 +01:00
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-03-13 20:34:12 +01:00
let isUninitializedLet =
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
if ( isUninitializedLet &&
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-04-13 10:32:16 +02:00
let 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 } (" ` ) ;
emitNodeWithoutSourceMap ( node . name ) ;
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-04-10 21:31:10 +02:00
write ( ")" )
}
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-03-14 01:23:42 +01:00
let 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 ) ;
}
function renameNonTopLevelLetAndConst ( node : Node ) : void {
2015-02-14 09:48:46 +01:00
// do not rename if
// - language version is ES6+
2015-02-26 09:04:44 +01:00
// - node is synthesized
2015-02-18 01:26:32 +01:00
// - node is not identifier (can happen when tree is malformed)
2015-02-14 09:48:46 +01:00
// - node is definitely not name of variable declaration.
// it still can be part of parameter declaration, this check will be done next
if ( languageVersion >= ScriptTarget . ES6 ||
2015-02-26 09:04:44 +01:00
nodeIsSynthesized ( node ) ||
2015-02-18 01:26:32 +01:00
node . kind !== SyntaxKind . Identifier ||
2015-02-14 09:48:46 +01:00
( node . parent . kind !== SyntaxKind . VariableDeclaration && node . parent . kind !== SyntaxKind . BindingElement ) ) {
2015-02-18 01:26:32 +01:00
return ;
2015-02-14 09:48:46 +01:00
}
2015-03-13 20:34:12 +01:00
let combinedFlags = getCombinedFlagsForIdentifier ( < Identifier > node ) ;
2015-02-14 09:48:46 +01:00
if ( ( ( combinedFlags & NodeFlags . BlockScoped ) === 0 ) || combinedFlags & NodeFlags . Export ) {
// do not rename exported or non-block scoped variables
2015-02-18 01:26:32 +01:00
return ;
2015-02-14 09:48:46 +01:00
}
// here it is known that node is a block scoped variable
2015-03-13 20:34:12 +01:00
let list = getAncestor ( node , SyntaxKind . VariableDeclarationList ) ;
2015-03-24 00:16:29 +01:00
if ( list . parent . kind === SyntaxKind . VariableStatement ) {
let isSourceFileLevelBinding = list . parent . parent . kind === SyntaxKind . SourceFile ;
2015-03-24 18:09:33 +01:00
let isModuleLevelBinding = list . parent . parent . kind === SyntaxKind . ModuleBlock ;
let isFunctionLevelBinding =
list . parent . parent . kind === SyntaxKind . Block && isFunctionLike ( list . parent . parent . parent ) ;
2015-03-24 00:16:29 +01:00
if ( isSourceFileLevelBinding || isModuleLevelBinding || isFunctionLevelBinding ) {
return ;
}
2015-02-14 09:48:46 +01:00
}
2015-03-13 20:34:12 +01:00
let blockScopeContainer = getEnclosingBlockScopeContainer ( node ) ;
let parent = blockScopeContainer . kind === SyntaxKind . SourceFile
2015-02-26 02:44:09 +01:00
? blockScopeContainer
: blockScopeContainer . parent ;
2015-03-26 18:51:07 +01:00
if ( resolver . resolvesToSomeValue ( parent , ( < Identifier > node ) . text ) ) {
2015-03-24 00:16:29 +01:00
let variableId = resolver . getBlockScopedVariableId ( < Identifier > node ) ;
if ( ! blockScopedVariableToGeneratedName ) {
blockScopedVariableToGeneratedName = [ ] ;
}
let generatedName = makeUniqueName ( ( < Identifier > node ) . text ) ;
blockScopedVariableToGeneratedName [ variableId ] = generatedName ;
2015-02-14 09:48:46 +01:00
}
}
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 ) &&
languageVersion >= ScriptTarget . ES6 &&
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 ;
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
}
if ( startIsEmitted ) {
emitCommaList ( node . declarationList . declarations ) ;
write ( ";" ) ;
}
else {
let atLeastOneItem = emitVariableDeclarationListSkippingUninitializedEntries ( node . declarationList ) ;
if ( atLeastOneItem ) {
write ( ";" ) ;
}
2015-02-17 23:12:28 +01:00
}
2015-02-13 03:05:02 +01:00
if ( languageVersion < ScriptTarget . ES6 && node . parent === currentSourceFile ) {
forEach ( node . declarationList . declarations , emitExportVariableAssignments ) ;
}
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 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-03-26 18:51:07 +01:00
let 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 ;
2014-12-14 18:43:14 +01:00
forEach ( node . parameters , p = > {
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.
if ( p . dotDotDotToken ) {
return ;
}
2014-12-14 18:43:14 +01:00
if ( isBindingPattern ( p . name ) ) {
writeLine ( ) ;
write ( "var " ) ;
2015-03-09 21:32:02 +01:00
emitDestructuring ( p , /*isAssignmentExpressionStatement*/ false , tempParameters [ tempIndex ] ) ;
2014-12-14 18:43:14 +01:00
write ( ";" ) ;
tempIndex ++ ;
}
else if ( p . initializer ) {
writeLine ( ) ;
emitStart ( p ) ;
write ( "if (" ) ;
2015-03-09 23:01:02 +01:00
emitNodeWithoutSourceMap ( p . name ) ;
2014-12-14 18:43:14 +01:00
write ( " === void 0)" ) ;
emitEnd ( p ) ;
write ( " { " ) ;
emitStart ( p ) ;
2015-03-09 23:01:02 +01:00
emitNodeWithoutSourceMap ( p . name ) ;
2014-12-14 18:43:14 +01:00
write ( " = " ) ;
2015-03-09 23:01:02 +01:00
emitNodeWithoutSourceMap ( p . initializer ) ;
2014-12-14 18:43:14 +01:00
emitEnd ( p ) ;
write ( "; }" ) ;
}
} ) ;
}
2014-11-11 00:36:10 +01:00
}
2014-11-21 05:24:08 +01:00
function emitRestParameter ( node : FunctionLikeDeclaration ) {
2015-01-16 16:15:31 +01:00
if ( languageVersion < ScriptTarget . ES6 && hasRestParameters ( node ) ) {
2015-03-13 20:34:12 +01:00
let restIndex = node . parameters . length - 1 ;
let 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-03-26 18:51:07 +01:00
let 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-03-09 23:01:02 +01:00
emitNodeWithoutSourceMap ( 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-03-09 23:01:02 +01:00
emitNodeWithoutSourceMap ( 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-03-16 20:37:21 +01:00
emit ( node . name , /*allowGeneratedIdentifiers*/ false ) ;
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-03-09 23:01:02 +01:00
emitNodeWithoutSourceMap ( 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)
return ! ! node . name || languageVersion < ScriptTarget . 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-03-18 02:34:42 +01:00
return emitOnlyPinnedOrTripleSlashComments ( node ) ;
2014-07-13 01:04:16 +02:00
}
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
// Methods will emit the comments as part of emitting method declaration
emitLeadingComments ( node ) ;
2014-08-22 02:17:02 +02:00
}
2015-01-07 03:18:37 +01:00
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-03-04 00:56:40 +01:00
if ( languageVersion < ScriptTarget . 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
}
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-03-13 20:34:12 +01:00
let parameters = node . parameters ;
let omitCount = languageVersion < ScriptTarget . ES6 && hasRestParameters ( 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-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
2014-11-21 05:24:08 +01:00
function emitSignatureAndBody ( node : FunctionLikeDeclaration ) {
2015-03-26 18:51:07 +01:00
let saveTempFlags = tempFlags ;
2015-03-13 20:34:12 +01:00
let saveTempVariables = tempVariables ;
let saveTempParameters = tempParameters ;
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-02-23 02:59:24 +01:00
if ( ! node . body ) {
// There can be no body when there are parse errors. Just emit an empty block
// in that case.
2015-02-07 03:45:09 +01:00
write ( " { }" ) ;
2014-07-17 00:39:14 +02:00
}
2015-02-07 04:53:52 +01:00
else if ( node . body . kind === SyntaxKind . Block ) {
emitBlockFunctionBody ( node , < Block > node . body ) ;
}
2014-10-11 01:44:14 +02:00
else {
2015-02-07 04:53:52 +01:00
emitExpressionFunctionBody ( node , < Expression > node . body ) ;
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-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-02-22 02:16:04 +01:00
if ( languageVersion < ScriptTarget . ES6 ) {
emitDownLevelExpressionFunctionBody ( node , body ) ;
return ;
}
2015-03-07 10:30:45 +01: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 ( " {" ) ;
scopeEmitStart ( node ) ;
increaseIndent ( ) ;
2015-03-13 20:34:12 +01:00
let outPos = writer . getTextPos ( ) ;
2015-02-07 04:53:52 +01:00
emitDetachedComments ( node . body ) ;
emitFunctionBodyPreamble ( node ) ;
2015-03-13 20:34:12 +01:00
let 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 ) ;
write ( "return " ) ;
2015-03-18 02:34:42 +01:00
emit ( 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 ) ;
scopeEmitEnd ( ) ;
}
function emitBlockFunctionBody ( node : FunctionLikeDeclaration , body : Block ) {
write ( " {" ) ;
scopeEmitStart ( node ) ;
2015-03-13 20:34:12 +01:00
let initialTextPos = writer . getTextPos ( ) ;
2015-02-23 02:59:24 +01:00
2015-02-07 04:53:52 +01:00
increaseIndent ( ) ;
emitDetachedComments ( 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-03-13 20:34:12 +01:00
let 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-03-13 20:34:12 +01:00
let 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-03-13 17:08:27 +01:00
for ( let 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
scopeEmitEnd ( ) ;
}
2014-10-11 01:44:14 +02:00
function findInitialSuperCall ( ctor : ConstructorDeclaration ) : ExpressionStatement {
if ( ctor . body ) {
2015-03-13 20:34:12 +01:00
let statement = ( < Block > ctor . body ) . statements [ 0 ] ;
2014-10-11 01:44:14 +02:00
if ( statement && statement . kind === SyntaxKind . ExpressionStatement ) {
2015-03-13 20:34:12 +01:00
let expr = ( < ExpressionStatement > statement ) . expression ;
2014-10-11 01:44:14 +02:00
if ( expr && expr . kind === SyntaxKind . CallExpression ) {
2015-03-13 20:34:12 +01:00
let 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-03-10 18:41:26 +01:00
// TODO: (jfreeman,drosen): comment on why this is emitNodeWithoutSourceMap instead of emit here.
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-03-09 23:01:02 +01:00
emitNodeWithoutSourceMap ( 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-03-09 23:01:02 +01:00
emitNodeWithoutSourceMap ( memberName ) ;
2014-07-17 00:39:14 +02:00
}
}
2015-05-03 20:14:09 +02:00
function getInitializedProperties ( node : ClassLikeDeclaration , isStatic : boolean ) {
2015-04-03 02:02:26 +02:00
let properties : PropertyDeclaration [ ] = [ ] ;
for ( let 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 [ ] ) {
for ( let property of properties ) {
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-03-18 02:34:42 +01:00
return emitOnlyPinnedOrTripleSlashComments ( 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 ( " = " ) ;
emitStart ( member ) ;
emitFunctionDeclaration ( < MethodDeclaration > member ) ;
emitEnd ( 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-03-13 20:34:12 +01:00
let 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-03-17 00:24:40 +01:00
for ( let member of node . members ) {
if ( ( member . kind === SyntaxKind . MethodDeclaration || node . kind === SyntaxKind . MethodSignature ) && ! ( < MethodDeclaration > member ) . body ) {
2015-03-18 02:34:42 +01:00
emitOnlyPinnedOrTripleSlashComments ( member ) ;
2015-03-16 23:41:51 +01:00
}
2015-03-31 23:12:35 +02:00
else if ( member . kind === SyntaxKind . MethodDeclaration ||
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-03-26 18:51:07 +01:00
let saveTempFlags = tempFlags ;
2015-03-16 23:20:40 +01:00
let saveTempVariables = tempVariables ;
let saveTempParameters = tempParameters ;
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 ) ;
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-03-18 02:34:42 +01:00
emitOnlyPinnedOrTripleSlashComments ( 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-03-16 23:20:40 +01:00
let 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-03-10 20:12:41 +01:00
write ( " {" ) ;
scopeEmitStart ( node , "constructor" ) ;
increaseIndent ( ) ;
if ( ctor ) {
2015-03-16 23:20:40 +01:00
emitDetachedComments ( ctor . body . statements ) ;
2015-03-10 20:12:41 +01:00
}
emitCaptureThisForNodeIfNecessary ( node ) ;
if ( ctor ) {
emitDefaultValueAssignments ( ctor ) ;
emitRestParameter ( ctor ) ;
2015-03-31 21:29:02 +02:00
if ( baseTypeElement ) {
2015-03-10 20:12:41 +01:00
var superCall = findInitialSuperCall ( ctor ) ;
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-04-03 02:02:26 +02:00
emitPropertyDeclarations ( node , getInitializedProperties ( node , /*static:*/ false ) ) ;
2015-03-10 20:12:41 +01:00
if ( ctor ) {
var statements : Node [ ] = ( < Block > ctor . body ) . statements ;
2015-03-16 23:20:40 +01:00
if ( superCall ) {
statements = statements . slice ( 1 ) ;
}
2015-03-10 20:12:41 +01:00
emitLines ( statements ) ;
}
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 ) ;
scopeEmitEnd ( ) ;
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-03-31 22:23:46 +02:00
function emitClassLikeDeclarationForES6AndHigher ( node : ClassLikeDeclaration ) {
2015-03-23 22:09:38 +01:00
let 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 {
// };
// Object.defineProperty(C, "name", { value: "C", configurable: true });
// C = __decorate([dec], C);
//
// * For an exported class declaration:
//
// @dec export class C {
// }
//
// The emit should be:
//
// export let C = class {
// };
// Object.defineProperty(C, "name", { value: "C", configurable: true });
// 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 {
// }
// Object.defineProperty(C, "name", { value: "C", configurable: true });
// 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
// to specialize the emit a bit. for a class expression of the form:
//
// class C { static a = 1; static b = 2; ... }
//
// 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.
let staticProperties = getInitializedProperties ( node , /*static:*/ true ) ;
let isClassExpressionWithStaticProperties = staticProperties . length > 0 && node . kind === SyntaxKind . ClassExpression ;
let tempVariable : Identifier ;
if ( isClassExpressionWithStaticProperties ) {
tempVariable = createAndRecordTempVariable ( TempFlags . Auto ) ;
write ( "(" ) ;
increaseIndent ( ) ;
emit ( tempVariable ) ;
write ( " = " )
}
2015-03-23 22:09:38 +01:00
write ( "class" ) ;
2015-03-25 08:06:38 +01:00
2015-03-23 22:09:38 +01:00
// check if this is an "export default class" as it may not have a name. Do not emit the name if the class is decorated.
if ( ( node . name || ! ( node . flags & NodeFlags . Default ) ) && ! thisNodeIsDecorated ) {
write ( " " ) ;
2015-03-17 04:54:28 +01:00
emitDeclarationName ( node ) ;
}
2015-03-23 22:09:38 +01:00
2015-04-01 02:23:52 +02:00
var 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 ( ) ;
scopeEmitStart ( node ) ;
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-03-10 20:12:41 +01:00
scopeEmitEnd ( ) ;
2015-03-12 00:13:08 +01:00
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-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 ( ) ;
emitPropertyDeclaration ( node , property , /*receiver:*/ tempVariable , /*isExpression:*/ true ) ;
}
write ( "," ) ;
writeLine ( ) ;
emit ( tempVariable ) ;
decreaseIndent ( ) ;
write ( ")" ) ;
}
else {
writeLine ( ) ;
emitPropertyDeclarations ( node , staticProperties ) ;
emitDecoratorsOfClass ( node ) ;
}
2015-03-17 05:18:31 +01:00
2015-03-17 05:58:57 +01:00
// If this is an exported class, but not on the top level (i.e. on an internal
2015-03-17 05:18:31 +01:00
// module), export it
2015-03-25 04:36:02 +01:00
if ( ! isES6ExportedDeclaration ( node ) && ( node . flags & NodeFlags . Export ) ) {
2015-03-17 05:18:31 +01:00
writeLine ( ) ;
emitStart ( node ) ;
emitModuleMemberName ( node ) ;
write ( " = " ) ;
emitDeclarationName ( node ) ;
emitEnd ( node ) ;
write ( ";" ) ;
}
2015-03-25 08:06:38 +01:00
else if ( isES6ExportedDeclaration ( node ) && ( node . flags & NodeFlags . Default ) && thisNodeIsDecorated ) {
2015-03-25 01:00:29 +01:00
// if this is a top level default export of decorated class, write the export after the declaration.
writeLine ( ) ;
write ( "export default " ) ;
emitDeclarationName ( node ) ;
write ( ";" ) ;
}
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-04-01 02:23:52 +02:00
let 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-03-26 18:51:07 +01:00
let saveTempFlags = tempFlags ;
2015-03-17 21:41:29 +01:00
let saveTempVariables = tempVariables ;
let saveTempParameters = tempParameters ;
2015-03-25 03:57:21 +01:00
let saveComputedPropertyNamesToGeneratedNames = computedPropertyNamesToGeneratedNames ;
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 ( ) ;
scopeEmitStart ( node ) ;
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-04-03 02:02:26 +02:00
emitPropertyDeclarations ( node , getInitializedProperties ( node , /*static:*/ 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-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 ) ;
scopeEmitEnd ( ) ;
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 ) ;
}
2015-03-12 18:27:46 +01:00
2015-03-04 00:09:40 +01:00
if ( languageVersion < ScriptTarget . ES6 && node . parent === currentSourceFile && node . name ) {
2015-02-13 19:07:10 +01:00
emitExportMemberAssignments ( node . name ) ;
2015-02-13 03:05:02 +01:00
}
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-03-24 22:16:52 +01: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-04-02 02:58:28 +02:00
let decorators = node . decorators ;
2015-03-24 22:16:52 +01:00
let constructor = getFirstConstructorWithBody ( node ) ;
2015-04-02 02:58:28 +02:00
let hasDecoratedParameters = constructor && forEach ( constructor . parameters , nodeIsDecorated ) ;
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
if ( ! decorators && ! hasDecoratedParameters ) {
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-03-17 21:41:29 +01:00
emitStart ( node ) ;
2015-03-24 22:16:52 +01:00
emitDeclarationName ( node ) ;
2015-04-02 02:58:28 +02:00
write ( " = __decorate([" ) ;
increaseIndent ( ) ;
writeLine ( ) ;
2015-04-03 00:22:53 +02:00
let decoratorCount = decorators ? decorators.length : 0 ;
let argumentsWritten = emitList ( decorators , 0 , decoratorCount , /*multiLine*/ true , /*trailingComma*/ false , /*leadingComma*/ false , /*noTrailingNewLine*/ true , decorator = > {
emitStart ( decorator ) ;
emit ( decorator . expression ) ;
emitEnd ( decorator ) ;
} ) ;
argumentsWritten += emitDecoratorsOfParameters ( constructor , /*leadingComma*/ argumentsWritten > 0 ) ;
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-03-17 21:41:29 +01:00
write ( ");" ) ;
emitEnd ( node ) ;
writeLine ( ) ;
}
2015-03-31 22:23:46 +02:00
function emitDecoratorsOfMembers ( node : ClassLikeDeclaration , staticFlag : NodeFlags ) {
2015-04-02 02:58:28 +02:00
for ( let member of node . members ) {
// only emit members in the correct group
2015-03-24 22:16:52 +01:00
if ( ( member . flags & NodeFlags . Static ) !== staticFlag ) {
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 a member if it or any of its parameters are not decorated
if ( ! nodeOrChildIsDecorated ( 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 ) ) {
let accessors = getAllAccessorDeclarations ( node . members , < AccessorDeclaration > member ) ;
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-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-04-02 02:58:28 +02:00
// Object.defineProperty(C.prototype, "method",
// __decorate([
// dec,
// __param(0, dec2),
// __metadata("design:type", Function),
// __metadata("design:paramtypes", [Object]),
// __metadata("design:returntype", void 0)
// ], C.prototype, "method", Object.getOwnPropertyDescriptor(C.prototype, "method")));
2015-03-24 22:16:52 +01:00
//
// The emit for an accessor is:
//
2015-04-02 02:58:28 +02:00
// Object.defineProperty(C.prototype, "accessor",
// __decorate([
// dec
// ], C.prototype, "accessor", Object.getOwnPropertyDescriptor(C.prototype, "accessor")));
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-03-24 22:16:52 +01:00
emitStart ( member ) ;
if ( member . kind !== SyntaxKind . PropertyDeclaration ) {
write ( "Object.defineProperty(" ) ;
emitStart ( member . name ) ;
emitClassMemberPrefix ( node , member ) ;
write ( ", " ) ;
emitExpressionForPropertyName ( member . name ) ;
emitEnd ( member . name ) ;
2015-04-02 02:58:28 +02:00
write ( "," ) ;
increaseIndent ( ) ;
writeLine ( ) ;
2015-03-24 22:16:52 +01:00
}
2015-04-02 02:58:28 +02:00
write ( "__decorate([" ) ;
increaseIndent ( ) ;
writeLine ( ) ;
2015-04-03 00:22:53 +02:00
let decoratorCount = decorators ? decorators.length : 0 ;
let argumentsWritten = emitList ( decorators , 0 , decoratorCount , /*multiLine*/ true , /*trailingComma*/ false , /*leadingComma*/ false , /*noTrailingNewLine*/ true , decorator = > {
emitStart ( decorator ) ;
emit ( decorator . expression ) ;
emitEnd ( decorator ) ;
} ) ;
argumentsWritten += emitDecoratorsOfParameters ( functionLikeMember , argumentsWritten > 0 ) ;
emitSerializedTypeMetadata ( member , argumentsWritten > 0 ) ;
2015-04-02 02:58:28 +02:00
decreaseIndent ( ) ;
writeLine ( ) ;
write ( "], " ) ;
2015-03-24 22:16:52 +01:00
emitStart ( member . name ) ;
emitClassMemberPrefix ( node , member ) ;
write ( ", " ) ;
emitExpressionForPropertyName ( member . name ) ;
emitEnd ( member . name ) ;
if ( member . kind !== SyntaxKind . PropertyDeclaration ) {
write ( ", Object.getOwnPropertyDescriptor(" ) ;
emitStart ( member . name ) ;
emitClassMemberPrefix ( node , member ) ;
write ( ", " ) ;
emitExpressionForPropertyName ( member . name ) ;
emitEnd ( member . name ) ;
write ( "))" ) ;
2015-04-02 02:58:28 +02:00
decreaseIndent ( ) ;
2015-03-24 22:16:52 +01:00
}
write ( ");" ) ;
emitEnd ( member ) ;
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 ;
for ( let parameter of node . parameters ) {
if ( nodeIsDecorated ( parameter ) ) {
2015-04-03 00:22:53 +02:00
let decorators = parameter . decorators ;
argumentsWritten += emitList ( decorators , 0 , decorators . length , /*multiLine*/ true , /*trailingComma*/ false , /*leadingComma*/ leadingComma , /*noTrailingNewLine*/ true , decorator = > {
emitStart ( decorator ) ;
write ( ` __param( ${ parameterIndex } , ` ) ;
emit ( decorator . expression ) ;
write ( ")" ) ;
emitEnd ( decorator ) ;
} ) ;
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.
// The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata
// 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.
// The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata
// 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.
// The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata
// 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-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 ) ) {
var serializedType = resolver . serializeTypeOfNode ( node , getGeneratedNameForNode ) ;
if ( serializedType ) {
if ( writeComma ) {
write ( ", " ) ;
}
writeLine ( ) ;
write ( "__metadata('design:type', " ) ;
emitSerializedType ( node , serializedType ) ;
write ( ")" ) ;
argumentsWritten ++ ;
2015-04-02 02:58:28 +02:00
}
2015-03-18 01:09:39 +01:00
}
2015-04-03 00:22:53 +02:00
if ( shouldEmitParamTypesMetadata ( node ) ) {
var serializedTypes = resolver . serializeParameterTypesOfNode ( node , getGeneratedNameForNode ) ;
if ( serializedTypes ) {
if ( writeComma || argumentsWritten ) {
2015-03-18 01:09:39 +01:00
write ( ", " ) ;
}
2015-04-03 00:22:53 +02:00
writeLine ( ) ;
write ( "__metadata('design:paramtypes', [" ) ;
for ( var i = 0 ; i < serializedTypes . length ; ++ i ) {
if ( i > 0 ) {
write ( ", " ) ;
}
emitSerializedType ( node , serializedTypes [ i ] ) ;
}
write ( "])" ) ;
argumentsWritten ++ ;
2015-03-18 01:09:39 +01:00
}
}
2015-04-03 00:22:53 +02:00
if ( shouldEmitReturnTypeMetadata ( node ) ) {
var serializedType = resolver . serializeReturnTypeOfNode ( node , getGeneratedNameForNode ) ;
if ( serializedType ) {
if ( writeComma || argumentsWritten ) {
write ( ", " ) ;
}
writeLine ( ) ;
write ( "__metadata('design:returntype', " ) ;
emitSerializedType ( node , serializedType ) ;
write ( ")" ) ;
argumentsWritten ++ ;
2015-04-02 02:58:28 +02:00
}
2015-03-18 01:09:39 +01:00
}
}
2015-04-03 00:22:53 +02:00
return argumentsWritten ;
2015-03-18 01:09:39 +01:00
}
2015-04-02 02:58:28 +02:00
function serializeTypeNameSegment ( location : Node , path : string [ ] , index : number ) : string {
switch ( index ) {
case 0 :
return ` typeof ${ path [ index ] } !== 'undefined' && ${ path [ index ] } ` ;
case 1 :
return ` ${ serializeTypeNameSegment ( location , path , index - 1 ) } . ${ path [ index ] } ` ;
default :
let temp = createAndRecordTempVariable ( TempFlags . Auto ) . text ;
return ` ( ${ temp } = ${ serializeTypeNameSegment ( location , path , index - 1 ) } ) && ${ temp } . ${ path [ index ] } ` ;
}
}
2015-03-18 01:09:39 +01:00
function emitSerializedType ( location : Node , name : string | string [ ] ) : void {
if ( typeof name === "string" ) {
write ( name ) ;
return ;
}
else {
2015-04-02 02:58:28 +02:00
Debug . assert ( name . length > 0 , "Invalid serialized type name" ) ;
2015-03-26 02:12:57 +01:00
write ( ` ( ${ serializeTypeNameSegment ( location , name , name . length - 1 ) } ) || Object ` ) ;
2015-03-18 01:09:39 +01:00
}
}
2015-04-02 02:58:28 +02:00
2014-10-11 01:44:14 +02:00
function emitInterfaceDeclaration ( node : InterfaceDeclaration ) {
2015-03-18 02:34:42 +01:00
emitOnlyPinnedOrTripleSlashComments ( 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-03-13 20:34:12 +01:00
let isConstEnum = isConst ( node ) ;
2015-03-31 04:33:15 +02:00
return ! isConstEnum || compilerOptions . preserveConstEnums || compilerOptions . separateCompilation ;
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
if ( ! ( node . flags & NodeFlags . Export ) || isES6ExportedDeclaration ( node ) ) {
emitStart ( node ) ;
if ( isES6ExportedDeclaration ( node ) ) {
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 ( ) ;
scopeEmitStart ( node ) ;
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 ) ;
scopeEmitEnd ( ) ;
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-02-13 03:05:02 +01:00
if ( languageVersion < ScriptTarget . ES6 && node . parent === currentSourceFile ) {
2015-05-11 09:07:49 +02:00
if ( compilerOptions . module === 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 ) ;
write ( ")" ) ;
}
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-03-13 20:34:12 +01:00
let 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-03-18 02:23:43 +01:00
let 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-03-13 20:34:12 +01:00
let 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-03-31 04:33:15 +02:00
return isInstantiatedModule ( node , compilerOptions . preserveConstEnums || compilerOptions . separateCompilation ) ;
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 ) ;
}
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-03-13 20:34:12 +01:00
let shouldEmit = shouldEmitModuleDeclaration ( node ) ;
2014-07-13 01:04:16 +02:00
2014-11-26 10:06:10 +01:00
if ( ! shouldEmit ) {
2015-03-18 02:34:42 +01:00
return emitOnlyPinnedOrTripleSlashComments ( node ) ;
2014-08-14 18:01:38 +02:00
}
2015-04-24 06:14:03 +02:00
let hoistedInDeclarationScope = shouldHoistDeclarationInSystemJsModule ( node ) ;
2015-04-20 22:40:13 +02:00
let emitVarForModule = ! hoistedInDeclarationScope && ! isModuleMergedWithES6Class ( node ) ;
2015-01-23 00:58:00 +01:00
2015-04-20 22:40:13 +02:00
if ( emitVarForModule ) {
2015-04-13 22:57:26 +02:00
emitStart ( node ) ;
if ( isES6ExportedDeclaration ( node ) ) {
write ( "export " ) ;
}
write ( "var " ) ;
emit ( node . name ) ;
write ( ";" ) ;
emitEnd ( node ) ;
writeLine ( ) ;
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-03-26 18:51:07 +01:00
let saveTempFlags = tempFlags ;
2015-03-13 20:34:12 +01:00
let saveTempVariables = tempVariables ;
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-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
scopeEmitStart ( node ) ;
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-03-13 20:34:12 +01:00
let module Block = < ModuleBlock > getInnerMostModuleDeclarationFromDottedModule ( node ) . body ;
2014-10-11 01:44:14 +02:00
emitToken ( SyntaxKind . CloseBraceToken , module Block.statements.end ) ;
scopeEmitEnd ( ) ;
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-04-13 10:32:16 +02:00
if ( compilerOptions . module === ModuleKind . System && ( node . flags & NodeFlags . Export ) ) {
2015-04-11 15:33:09 +02:00
writeLine ( ) ;
write ( ` ${ exportFunctionForFile } (" ` ) ;
emitDeclarationName ( node ) ;
write ( ` ", ` ) ;
emitDeclarationName ( node ) ;
write ( ")" ) ;
}
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-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(" ) ;
emitStart ( module Name ) ;
emitLiteral ( < LiteralExpression > module Name ) ;
emitEnd ( module Name ) ;
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 ;
}
let importClause = ( < ImportDeclaration > node ) . importClause ;
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-02-13 02:54:30 +01:00
if ( languageVersion < ScriptTarget . ES6 ) {
2015-03-12 08:17:40 +01:00
return emitExternalImportDeclaration ( node ) ;
}
// ES6 import
if ( node . importClause ) {
2015-03-25 05:43:03 +01:00
let shouldEmitDefaultBindings = resolver . isReferencedAliasDeclaration ( node . importClause ) ;
let 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-03-22 20:18:38 +01:00
let isExportedImport = node . kind === SyntaxKind . ImportEqualsDeclaration && ( node . flags & NodeFlags . Export ) !== 0 ;
2015-03-21 23:09:54 +01:00
let namespaceDeclaration = getNamespaceDeclarationNode ( node ) ;
2015-03-23 19:06:44 +01:00
2015-02-08 21:13:56 +01:00
if ( compilerOptions . module !== ModuleKind . AMD ) {
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"
let isNakedImport = SyntaxKind . ImportDeclaration && ! ( < ImportDeclaration > node ) . importClause ;
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-02-08 21:13:56 +01:00
( ! isExternalModule ( currentSourceFile ) && resolver . isTopLevelValueImportEqualsWithEntityName ( node ) ) ) {
emitLeadingComments ( node ) ;
emitStart ( node ) ;
2015-03-25 04:36:02 +01:00
if ( isES6ExportedDeclaration ( node ) ) {
2015-03-12 18:34:39 +01:00
write ( "export " ) ;
write ( "var " ) ;
}
else if ( ! ( node . flags & NodeFlags . Export ) ) {
write ( "var " ) ;
}
2015-02-08 21:13:56 +01:00
emitModuleMemberName ( node ) ;
write ( " = " ) ;
emit ( node . module Reference ) ;
write ( ";" ) ;
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-04-10 21:10:38 +02:00
Debug . assert ( compilerOptions . module !== ModuleKind . System ) ;
2015-03-25 05:17:11 +01:00
if ( languageVersion < ScriptTarget . ES6 ) {
2015-03-23 19:06:44 +01:00
if ( node . module Specifier && ( ! node . exportClause || resolver . isValueAliasDeclaration ( node ) ) ) {
emitStart ( node ) ;
2015-03-24 00:16:29 +01:00
let 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"
if ( compilerOptions . module !== ModuleKind . AMD ) {
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
}
for ( let specifier of node . exportClause . elements ) {
if ( resolver . isValueAliasDeclaration ( specifier ) ) {
writeLine ( ) ;
emitStart ( specifier ) ;
emitContainingModuleName ( specifier ) ;
write ( "." ) ;
emitNodeWithoutSourceMap ( specifier . name ) ;
write ( " = " ) ;
write ( generatedName ) ;
write ( "." ) ;
emitNodeWithoutSourceMap ( specifier . propertyName || specifier . name ) ;
write ( ";" ) ;
emitEnd ( specifier ) ;
}
}
2015-03-21 23:46:16 +01:00
}
else {
2015-03-23 19:06:44 +01:00
// export * from "foo"
writeLine ( ) ;
write ( "__export(" ) ;
if ( compilerOptions . module !== ModuleKind . AMD ) {
emitRequire ( getExternalModuleName ( node ) ) ;
}
else {
write ( generatedName ) ;
}
write ( ");" ) ;
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 ) ) {
emitStart ( 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 " ) ;
emitNodeWithoutSourceMap ( node . module Specifier ) ;
}
write ( ";" ) ;
emitEnd ( node ) ;
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-03-23 20:37:22 +01:00
Debug . assert ( languageVersion >= ScriptTarget . ES6 ) ;
let needsComma = false ;
for ( let 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 ( ", " ) ;
}
emitStart ( specifier ) ;
if ( specifier . propertyName ) {
emitNodeWithoutSourceMap ( specifier . propertyName ) ;
write ( " as " ) ;
}
emitNodeWithoutSourceMap ( specifier . name ) ;
emitEnd ( specifier ) ;
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 ) ) {
if ( languageVersion >= ScriptTarget . ES6 ) {
writeLine ( ) ;
emitStart ( node ) ;
write ( "export default " ) ;
var expression = node . expression ;
emit ( expression ) ;
if ( expression . kind !== SyntaxKind . FunctionDeclaration &&
expression . kind !== SyntaxKind . ClassDeclaration ) {
write ( ";" ) ;
}
emitEnd ( node ) ;
}
else {
writeLine ( ) ;
emitStart ( node ) ;
2015-04-10 21:10:38 +02:00
if ( compilerOptions . module === ModuleKind . System ) {
write ( ` ${ exportFunctionForFile } ("default", ` ) ;
emit ( node . expression ) ;
write ( ")" ) ;
}
else {
emitContainingModuleName ( node ) ;
2015-04-20 22:40:13 +02:00
if ( languageVersion === ScriptTarget . ES3 ) {
write ( "[\"default\"] = " ) ;
} else {
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-03-21 23:09:54 +01:00
hasExportStars = false ;
for ( let node of sourceFile . statements ) {
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"
externalImports . push ( < ExportDeclaration > node ) ;
hasExportStars = true ;
}
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 }
for ( let specifier of ( < ExportDeclaration > node ) . exportClause . elements ) {
let name = ( specifier . propertyName || specifier . name ) . text ;
( 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() {
if ( hasExportStars ) {
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-04-20 22:40:13 +02:00
function getLocalNameForExternalImport ( importNode : ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration ) : string {
2015-04-10 21:10:38 +02:00
let namespaceDeclaration = getNamespaceDeclarationNode ( importNode ) ;
if ( namespaceDeclaration && ! isDefaultImport ( importNode ) ) {
2015-04-20 22:40:13 +02:00
return getSourceTextOfNodeFromSourceFile ( currentSourceFile , namespaceDeclaration . name ) ;
2015-03-21 23:09:54 +01:00
}
2015-04-10 21:10:38 +02:00
else {
2015-04-20 22:40:13 +02:00
return getGeneratedNameForNode ( < ImportDeclaration | ExportDeclaration > importNode ) ;
2015-04-10 21:10:38 +02:00
}
}
2015-04-20 22:40:13 +02:00
function getExternalModuleNameText ( importNode : ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration ) : string {
let module Name = getExternalModuleName ( importNode ) ;
if ( module Name.kind === SyntaxKind . StringLiteral ) {
return 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 ;
for ( let 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-04-20 23:17:38 +02:00
let skipNode =
importNode . kind === SyntaxKind . ExportDeclaration ||
( importNode . kind === SyntaxKind . ImportDeclaration && ! ( < ImportDeclaration > importNode ) . importClause )
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.
if ( ! hasExportStars ) {
// 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 ;
for ( let externalImport of externalImports ) {
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 ) {
for ( let n in exportSpecifiers ) {
for ( let specifier of exportSpecifiers [ n ] ) {
// write name of export specified, i.e. 'export {x}'
writeExportedName ( specifier . name ) ;
}
}
}
2015-04-29 01:37:08 +02:00
for ( let externalImport of externalImports ) {
if ( externalImport . kind !== SyntaxKind . ExportDeclaration ) {
continue ;
}
2015-04-29 01:18:33 +02:00
2015-04-29 01:37:08 +02:00
let exportDecl = < ExportDeclaration > externalImport ;
if ( ! exportDecl . exportClause ) {
// export * from ...
continue ;
}
2015-04-29 01:18:33 +02:00
2015-04-29 01:37:08 +02:00
for ( let element of exportDecl . exportClause . elements ) {
// 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-04-29 03:51:29 +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-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-04-29 20:43:23 +02:00
write ( ` ) ${ exportFunctionForFile } (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 ( "}" ) ;
decreaseIndent ( ) ;
2015-04-29 05:17:55 +02:00
writeLine ( ) ;
write ( "}" )
return exportStarFunction ;
}
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
// 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 ) {
emitNodeWithoutSourceMap ( node ) ;
}
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-04-21 01:56:36 +02:00
// per ES6 spec:
// 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-05-11 09:07:49 +02:00
let seen : Map < string > = { } ;
2015-04-11 15:14:19 +02:00
for ( let i = 0 ; i < hoistedVars . length ; ++ i ) {
let local = hoistedVars [ i ] ;
2015-05-11 09:07:49 +02:00
let name = local . kind === SyntaxKind . Identifier
? < Identifier > local
: < Identifier > ( < ClassDeclaration | ModuleDeclaration | EnumDeclaration > local ) . name ;
if ( name ) {
// do not emit duplicate entries (in case of declaration merging) in the list of hoisted variables
let text = unescapeIdentifier ( name . text ) ;
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
let flags = getCombinedNodeFlags ( local . kind === SyntaxKind . Identifier ? local.parent : local ) ;
if ( flags & NodeFlags . Export ) {
if ( ! exportedDeclarations ) {
exportedDeclarations = [ ] ;
}
exportedDeclarations . push ( local ) ;
}
2015-04-10 21:10:38 +02:00
}
write ( ";" )
}
if ( hoistedFunctionDeclarations ) {
for ( let f of hoistedFunctionDeclarations ) {
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 ) ) {
let name = ( < VariableDeclaration | BindingElement > node ) . name ;
if ( name . kind === SyntaxKind . Identifier ) {
2015-04-22 07:27:33 +02:00
if ( ! hoistedVars ) {
hoistedVars = [ ] ;
}
hoistedVars . push ( < Identifier > name ) ;
2015-04-21 17:31:14 +02:00
}
else {
forEachChild ( name , visit ) ;
}
2015-04-10 21:10:38 +02:00
}
return ;
}
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
// if block scoped variables are nested in some another block then
// no other functions can use them except ones that are defined at least in the same block
return ( getCombinedNodeFlags ( node ) & NodeFlags . BlockScoped ) === 0 ||
getEnclosingBlockScopeContainer ( node ) . kind === SyntaxKind . SourceFile ;
}
2015-04-13 10:32:16 +02:00
function isCurrentFileSystemExternalModule() {
return compilerOptions . module === ModuleKind . System && isExternalModule ( currentSourceFile ) ;
2015-04-10 21:10:38 +02:00
}
function emitSystemModuleBody ( node : SourceFile , 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-04-29 01:18:33 +02:00
var exportedDeclarations = processTopLevelVariableAndFunctionDeclarations ( node ) ;
2015-04-29 03:51:29 +02:00
let exportStarFunction = emitLocalStorageForExportedNamesIfNecessary ( exportedDeclarations )
2015-04-10 21:10:38 +02:00
writeLine ( ) ;
write ( "return {" ) ;
increaseIndent ( ) ;
writeLine ( ) ;
2015-04-29 03:51:29 +02:00
emitSetters ( exportStarFunction ) ;
2015-04-10 21:10:38 +02:00
writeLine ( ) ;
emitExecute ( node , startIndex ) ;
2015-04-21 01:56:36 +02:00
emitTempDeclarations ( /*newLine*/ true )
2015-04-10 21:10:38 +02:00
decreaseIndent ( ) ;
writeLine ( ) ;
write ( "}" ) ; // return
}
2015-04-29 03:51:29 +02:00
function emitSetters ( exportStarFunction : string ) {
2015-04-22 07:27:33 +02:00
write ( "setters:[" ) ;
2015-04-10 21:10:38 +02:00
for ( let i = 0 ; i < externalImports . length ; ++ i ) {
if ( i !== 0 ) {
write ( "," ) ;
}
writeLine ( ) ;
increaseIndent ( ) ;
let importNode = externalImports [ i ] ;
2015-04-21 01:56:36 +02:00
let importVariableName = getLocalNameForExternalImport ( importNode ) || "" ;
let parameterName = "_" + importVariableName ;
write ( ` function ( ${ parameterName } ) { ` ) ;
2015-04-10 21:10:38 +02:00
switch ( importNode . kind ) {
case SyntaxKind . ImportDeclaration :
if ( ! ( < ImportDeclaration > importNode ) . importClause ) {
2015-04-21 01:56:36 +02:00
// 'import "..."' case
// module is imported only for side-effects, setter body will be empty
2015-04-10 21:10:38 +02:00
break ;
}
2015-04-20 22:40:13 +02:00
// fall-through
2015-04-10 21:10:38 +02:00
case SyntaxKind . ImportEqualsDeclaration :
2015-04-21 01:56:36 +02:00
Debug . assert ( importVariableName !== "" ) ;
2015-04-10 21:10:38 +02:00
increaseIndent ( ) ;
writeLine ( ) ;
2015-04-21 01:56:36 +02:00
// save import into the local
2015-04-29 01:18:33 +02:00
write ( ` ${ importVariableName } = ${ parameterName } ; ` ) ;
2015-04-10 21:10:38 +02:00
writeLine ( ) ;
2015-04-13 22:00:00 +02:00
let defaultName =
importNode . kind === SyntaxKind . ImportDeclaration
? ( < ImportDeclaration > importNode ) . importClause . name
: ( < ImportEqualsDeclaration > importNode ) . name ;
2015-04-21 01:56:36 +02:00
2015-04-13 22:00:00 +02:00
if ( defaultName ) {
2015-04-21 01:56:36 +02:00
// emit re-export for imported default name
// import n1 from 'foo1'
// import n2 = require('foo2')
// export {n1}
// export {n2}
2015-04-13 22:00:00 +02:00
emitExportMemberAssignments ( defaultName ) ;
writeLine ( ) ;
}
2015-04-21 01:56:36 +02:00
if ( importNode . kind === SyntaxKind . ImportDeclaration &&
( < ImportDeclaration > importNode ) . importClause . namedBindings ) {
let namedBindings = ( < ImportDeclaration > importNode ) . importClause . namedBindings ;
if ( namedBindings . kind === SyntaxKind . NamespaceImport ) {
// emit re-export for namespace
// import * as n from 'foo'
// export {n}
emitExportMemberAssignments ( ( < NamespaceImport > namedBindings ) . name ) ;
2015-04-13 22:00:00 +02:00
writeLine ( ) ;
}
else {
2015-04-21 01:56:36 +02:00
// emit re-exports for named imports
// import {a, b} from 'foo'
// export {a, b as c}
for ( let element of ( < NamedImports > namedBindings ) . elements ) {
2015-04-13 22:00:00 +02:00
emitExportMemberAssignments ( element . name || element . propertyName ) ;
writeLine ( )
}
}
}
2015-04-10 21:10:38 +02:00
decreaseIndent ( ) ;
break ;
case SyntaxKind . ExportDeclaration :
2015-04-21 01:56:36 +02:00
Debug . assert ( importVariableName !== "" ) ;
2015-04-10 21:10:38 +02:00
increaseIndent ( ) ;
if ( ( < ExportDeclaration > importNode ) . exportClause ) {
2015-04-21 01:56:36 +02:00
// export {a, b as c} from 'foo'
// emit as:
// exports('a', _foo["a"])
// exports('c', _foo["b"])
2015-04-10 21:10:38 +02:00
for ( let e of ( < ExportDeclaration > importNode ) . exportClause . elements ) {
writeLine ( ) ;
write ( ` ${ exportFunctionForFile } (" ` ) ;
emitNodeWithoutSourceMap ( e . name ) ;
2015-04-21 01:56:36 +02:00
write ( ` ", ${ parameterName } [" ` ) ;
2015-04-10 21:10:38 +02:00
emitNodeWithoutSourceMap ( e . propertyName || e . name ) ;
2015-04-13 21:14:32 +02:00
write ( ` "]); ` ) ;
2015-04-10 21:10:38 +02:00
}
}
else {
writeLine ( ) ;
2015-04-21 01:56:36 +02:00
// export * from 'foo'
// emit as:
2015-04-29 05:17:55 +02:00
// exportStar(_foo);
write ( ` ${ exportStarFunction } ( ${ parameterName } ); ` ) ;
2015-04-10 21:10:38 +02:00
}
writeLine ( ) ;
decreaseIndent ( ) ;
break ;
2015-02-10 23:59:20 +01:00
}
2015-04-10 21:10:38 +02:00
write ( "}" )
decreaseIndent ( ) ;
}
write ( "]," ) ;
}
function emitExecute ( node : SourceFile , startIndex : number ) {
write ( "execute: function() {" ) ;
increaseIndent ( ) ;
writeLine ( ) ;
for ( let i = startIndex ; i < node . statements . length ; ++ i ) {
let statement = node . statements [ i ] ;
2015-04-21 01:56:36 +02:00
// - imports/exports are not emitted for system modules
// - function declarations are not emitted because they were already hoisted
2015-04-10 21:10:38 +02:00
switch ( statement . kind ) {
case SyntaxKind . ExportDeclaration :
case SyntaxKind . ImportDeclaration :
case SyntaxKind . ImportEqualsDeclaration :
case SyntaxKind . FunctionDeclaration :
continue ;
}
writeLine ( ) ;
emit ( statement ) ;
}
decreaseIndent ( ) ;
writeLine ( ) ;
write ( "}" ) // execute
}
function emitSystemModule ( node : SourceFile , startIndex : number ) : 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.
// 'exports' returns its 'value' argument so in most cases expressions
// that mutate exported values can be rewritten as:
// expr -> exports('name', expr).
// The only exception in this rule is postfix unary operators,
// 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" ) ;
write ( "System.register([" ) ;
2015-04-20 22:40:13 +02:00
for ( let i = 0 ; i < externalImports . length ; ++ i ) {
let text = getExternalModuleNameText ( externalImports [ i ] ) ;
if ( i !== 0 ) {
write ( ", " ) ;
}
write ( text ) ;
}
2015-04-10 21:10:38 +02:00
write ( ` ], function( ${ exportFunctionForFile } ) { ` ) ;
writeLine ( ) ;
increaseIndent ( ) ;
emitCaptureThisForNodeIfNecessary ( node ) ;
emitSystemModuleBody ( node , startIndex ) ;
decreaseIndent ( ) ;
writeLine ( ) ;
write ( "});" ) ;
}
2015-04-03 08:05:58 +02:00
function emitAMDDependencies ( node : SourceFile , includeNonAmdDependencies : boolean ) {
2015-04-17 19:16:53 +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) {
2015-04-03 08:05:58 +02:00
// The location of the alias in the parameter list in the factory function needs to
2015-04-17 19:16:53 +02:00
// match the position of the module name in the dependency list.
//
2015-04-03 08:05:58 +02:00
// To ensure this is true in cases of modules with no aliases, e.g.:
// `import "module"` or `<amd-dependency path= "a.css" />`
2015-04-17 19:16:53 +02:00
// we need to add modules without alias names to the end of the dependencies list
2015-04-03 08:05:58 +02:00
let aliasedModuleNames : string [ ] = [ ] ; // names of modules with corresponding parameter in the
2015-04-17 02:37:01 +02:00
// factory function.
let unaliasedModuleNames : string [ ] = [ ] ; // names of modules with no corresponding parameters in
// factory function.
2015-04-17 19:16:53 +02:00
let importAliasNames : string [ ] = [ ] ; // names of the parameters in the factory function; these
2015-04-21 02:01:47 +02:00
// parameters need to match the indexes of the corresponding
2015-04-17 19:16:53 +02:00
// module names in aliasedModuleNames.
2015-04-17 02:32:40 +02:00
// Fill in amd-dependency tags
for ( let amdDependency of node . amdDependencies ) {
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-03-21 23:09:54 +01:00
for ( let importNode of externalImports ) {
2015-04-16 23:56:22 +02:00
// Find the name of the external module
2015-04-20 22:40:13 +02:00
let externalModuleName = getExternalModuleNameText ( importNode ) ;
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
let 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-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-04-16 23:56:22 +02:00
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-04-03 08:05:58 +02:00
}
function emitAMDModule ( node : SourceFile , startIndex : number ) {
collectExternalModuleInfo ( node ) ;
writeLine ( ) ;
write ( "define(" ) ;
if ( node . amdModuleName ) {
write ( "\"" + node . amdModuleName + "\", " ) ;
}
emitAMDDependencies ( node , /*includeNonAmdDependencies*/ true ) ;
2014-10-11 01:44:14 +02:00
write ( ") {" ) ;
increaseIndent ( ) ;
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
}
2014-10-11 01:44:14 +02:00
function emitCommonJSModule ( node : SourceFile , startIndex : number ) {
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-04-03 08:05:58 +02:00
function emitUMDModule ( node : SourceFile , startIndex : number ) {
collectExternalModuleInfo ( node ) ;
// Module is detected first to support Browserify users that load into a browser with an AMD loader
writeLines ( ` (function (deps, factory) {
if ( typeof module === 'object' && typeof module .exports === 'object' ) {
var v = factory ( require , exports ) ; if ( v !== undefined ) module .exports = v ;
}
else if ( typeof define === 'function' && define . amd ) {
define ( deps , factory ) ;
}
} ) ( ` );
emitAMDDependencies ( node , false ) ;
write ( ") {" ) ;
increaseIndent ( ) ;
emitExportStarHelper ( ) ;
emitCaptureThisForNodeIfNecessary ( node ) ;
emitLinesStartingAt ( node . statements , startIndex ) ;
emitTempDeclarations ( /*newLine*/ true ) ;
emitExportEquals ( /*emitAsReturn*/ true ) ;
decreaseIndent ( ) ;
writeLine ( ) ;
write ( "});" ) ;
}
2015-03-12 07:29:30 +01:00
function emitES6Module ( node : SourceFile , startIndex : number ) {
externalImports = undefined ;
exportSpecifiers = undefined ;
2015-03-23 19:06:44 +01:00
exportEquals = undefined ;
hasExportStars = false ;
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-17 03:25:02 +01: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
2014-11-30 23:06:15 +01:00
function emitDirectivePrologues ( statements : Node [ ] , startWithNewLine : boolean ) : number {
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 ] ) ) {
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 {
// 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
}
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-03-17 21:41:29 +01:00
let lines = text . split ( /\r\n|\r|\n/g ) ;
for ( let i = 0 ; i < lines . length ; ++ i ) {
let line = lines [ i ] ;
if ( line . length ) {
writeLine ( ) ;
write ( line ) ;
}
}
}
2015-03-09 21:32:02 +01:00
function emitSourceFileNode ( node : SourceFile ) {
2014-10-11 01:44:14 +02:00
// Start new file on new line
writeLine ( ) ;
emitDetachedComments ( node ) ;
2014-07-13 01:04:16 +02:00
2014-10-11 01:44:14 +02:00
// emit prologue directives prior to __extends
var startIndex = emitDirectivePrologues ( node . statements , /*startWithNewLine*/ false ) ;
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
// 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-04-28 00:47:49 +02:00
if ( isExternalModule ( node ) || compilerOptions . separateCompilation ) {
2015-02-13 02:54:30 +01:00
if ( languageVersion >= ScriptTarget . ES6 ) {
2015-03-12 08:18:20 +01:00
emitES6Module ( node , startIndex ) ;
2014-07-17 00:39:14 +02:00
}
2015-03-12 08:18:20 +01:00
else if ( compilerOptions . module === ModuleKind . AMD ) {
2014-10-11 01:44:14 +02:00
emitAMDModule ( node , startIndex ) ;
2014-07-17 00:39:14 +02:00
}
2015-04-10 21:10:38 +02:00
else if ( compilerOptions . module === ModuleKind . System ) {
emitSystemModule ( node , startIndex ) ;
2015-04-24 05:50:35 +02:00
}
2015-04-03 08:05:58 +02:00
else if ( compilerOptions . module === ModuleKind . UMD ) {
emitUMDModule ( node , startIndex ) ;
}
2014-10-11 01:44:14 +02:00
else {
emitCommonJSModule ( node , startIndex ) ;
2014-07-17 00:39:14 +02:00
}
2014-07-12 00:13:01 +02:00
}
2014-10-11 01:44:14 +02:00
else {
2015-02-13 03:05:02 +01:00
externalImports = undefined ;
exportSpecifiers = undefined ;
2015-03-21 21:12:39 +01:00
exportEquals = undefined ;
2015-03-21 23:09:54 +01:00
hasExportStars = false ;
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-03-18 02:34:42 +01:00
function emitNodeWithoutSourceMap ( node : Node , allowGeneratedIdentifiers? : boolean ) : void {
2014-10-11 01:44:14 +02:00
if ( ! node ) {
return ;
2014-07-13 01:04:16 +02:00
}
2014-12-16 10:09:42 +01:00
2014-10-11 01:44:14 +02:00
if ( node . flags & NodeFlags . Ambient ) {
2015-03-18 02:34:42 +01:00
return emitOnlyPinnedOrTripleSlashComments ( node ) ;
2014-07-12 00:13:01 +02:00
}
2015-01-23 00:58:00 +01:00
2015-03-13 20:34:12 +01:00
let emitComments = shouldEmitLeadingAndTrailingComments ( node ) ;
2015-01-23 00:58:00 +01:00
if ( emitComments ) {
emitLeadingComments ( node ) ;
}
2015-03-16 21:36:34 +01:00
emitJavaScriptWorker ( node , allowGeneratedIdentifiers ) ;
2015-01-23 00:58:00 +01:00
if ( emitComments ) {
emitTrailingComments ( node ) ;
}
}
function shouldEmitLeadingAndTrailingComments ( node : Node ) {
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 :
return false ;
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-03-18 03:25:40 +01: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-03-18 03:13:00 +01:00
( < ArrowFunction > node . parent ) . body === node &&
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-03-16 21:36:34 +01:00
function emitJavaScriptWorker ( node : Node , allowGeneratedIdentifiers : boolean = true ) {
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-03-16 20:37:21 +01:00
return emitIdentifier ( < Identifier > node , allowGeneratedIdentifiers ) ;
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 ) ;
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 ) ;
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 ) ;
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 :
return emitLabelledStatement ( < LabeledStatement > node ) ;
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-03-18 02:34:42 +01:00
let leadingComments = getLeadingCommentRanges ( currentSourceFile . text ,
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-03-18 03:25:40 +01:00
function filterComments ( ranges : CommentRange [ ] , onlyPinnedOrTripleSlashComments : boolean ) : CommentRange [ ] {
// If we're removing comments, then we want to strip out all but the pinned or
// triple slash comments.
if ( ranges && onlyPinnedOrTripleSlashComments ) {
ranges = filter ( ranges , isPinnedOrTripleSlashComment ) ;
if ( ranges . length === 0 ) {
return undefined ;
}
}
return ranges ;
}
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-03-18 03:25:40 +01:00
return getLeadingCommentRangesOfNode ( node , currentSourceFile ) ;
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 ) {
return getTrailingCommentRanges ( currentSourceFile . text , node . end ) ;
2015-03-18 02:34:42 +01:00
}
}
}
function emitOnlyPinnedOrTripleSlashComments ( node : Node ) {
emitLeadingCommentsWorker ( node , /*onlyPinnedOrTripleSlashComments:*/ true ) ;
}
function emitLeadingComments ( node : Node ) {
2015-03-18 03:13:00 +01:00
return emitLeadingCommentsWorker ( node , /*onlyPinnedOrTripleSlashComments:*/ compilerOptions . removeComments ) ;
2015-03-18 02:34:42 +01:00
}
function emitLeadingCommentsWorker ( node : Node , onlyPinnedOrTripleSlashComments : boolean ) {
2015-03-18 02:43:39 +01:00
// If the caller only wants pinned or triple slash comments, then always filter
// down to that set. Otherwise, filter based on the current compiler options.
2015-03-18 03:13:00 +01:00
let leadingComments = filterComments ( getLeadingCommentsToEmit ( node ) , onlyPinnedOrTripleSlashComments ) ;
2015-03-18 02:34:42 +01:00
2014-10-13 19:53:57 +02:00
emitNewLineBeforeLeadingComments ( currentSourceFile , 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
2014-10-13 19:53:57 +02:00
emitComments ( currentSourceFile , 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 ) {
2014-10-11 01:44:14 +02:00
// Emit the trailing comments only if the parent's end doesn't match
2015-03-18 03:25:40 +01:00
var trailingComments = filterComments ( getTrailingCommentsToEmit ( node ) , /*onlyPinnedOrTripleSlashComments:*/ compilerOptions . removeComments ) ;
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*/
emitComments ( currentSourceFile , writer , trailingComments , /*trailingSeparator*/ false , newLine , writeComment ) ;
2014-07-13 01:04:16 +02:00
}
2015-03-18 02:34:42 +01:00
function emitLeadingCommentsOfPosition ( pos : number ) {
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
leadingComments = getLeadingCommentRanges ( currentSourceFile . text , pos ) ;
2014-07-17 00:39:14 +02:00
}
2015-03-18 02:34:42 +01:00
2015-03-18 03:13:00 +01:00
leadingComments = filterComments ( leadingComments , compilerOptions . removeComments ) ;
2014-10-13 19:53:57 +02:00
emitNewLineBeforeLeadingComments ( currentSourceFile , 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-02-05 22:57:27 +01:00
emitComments ( currentSourceFile , writer , leadingComments , /*trailingSeparator*/ true , newLine , writeComment ) ;
2014-10-11 01:44:14 +02:00
}
2014-07-13 01:04:16 +02:00
2015-03-18 02:34:42 +01:00
function emitDetachedComments ( node : TextRange ) {
2015-03-13 20:34:12 +01:00
let leadingComments = getLeadingCommentRanges ( currentSourceFile . text , node . pos ) ;
2014-10-11 01:44:14 +02:00
if ( leadingComments ) {
2015-03-13 20:34:12 +01:00
let detachedComments : CommentRange [ ] = [ ] ;
let lastComment : CommentRange ;
2014-08-07 02:06:59 +02:00
2014-10-11 01:44:14 +02:00
forEach ( leadingComments , comment = > {
if ( lastComment ) {
2015-03-13 20:34:12 +01:00
let lastCommentLine = getLineOfLocalPosition ( currentSourceFile , lastComment . end ) ;
let commentLine = getLineOfLocalPosition ( currentSourceFile , comment . pos ) ;
2014-08-07 02:06:59 +02:00
2014-10-11 01:44:14 +02:00
if ( commentLine >= lastCommentLine + 2 ) {
// There was a blank line between the last comment and this comment. This
2015-02-05 22:57:27 +01:00
// comment is not part of the copyright comments. Return what we have so
2014-10-11 01:44:14 +02:00
// far.
return detachedComments ;
}
}
2014-08-07 02:06:59 +02:00
2014-10-11 01:44:14 +02:00
detachedComments . push ( comment ) ;
lastComment = comment ;
} ) ;
2014-08-07 02:06:59 +02:00
2014-10-11 01:44:14 +02:00
if ( detachedComments . length ) {
// All comments look like they could have been part of the copyright header. Make
// sure there is at least one blank line between it and the node. If not, it's not
// a copyright header.
2015-05-02 03:22:35 +02:00
let lastCommentLine = getLineOfLocalPosition ( currentSourceFile , lastOrUndefined ( detachedComments ) . end ) ;
2015-03-13 20:34:12 +01:00
let nodeLine = getLineOfLocalPosition ( currentSourceFile , skipTrivia ( currentSourceFile . text , node . pos ) ) ;
2015-02-17 01:28:26 +01:00
if ( nodeLine >= lastCommentLine + 2 ) {
2014-10-11 01:44:14 +02:00
// Valid detachedComments
2014-10-13 19:53:57 +02:00
emitNewLineBeforeLeadingComments ( currentSourceFile , writer , node , leadingComments ) ;
emitComments ( currentSourceFile , writer , detachedComments , /*trailingSeparator*/ true , newLine , writeComment ) ;
2015-05-02 03:22:35 +02:00
let currentDetachedCommentInfo = { nodePos : node.pos , detachedCommentEndPos : lastOrUndefined ( detachedComments ) . end } ;
2014-10-11 01:44:14 +02:00
if ( detachedCommentsInfo ) {
detachedCommentsInfo . push ( currentDetachedCommentInfo ) ;
2014-08-07 02:06:59 +02:00
}
else {
2014-10-11 01:44:14 +02:00
detachedCommentsInfo = [ currentDetachedCommentInfo ] ;
2014-08-07 02:06:59 +02:00
}
2014-10-11 01:44:14 +02:00
}
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-03-18 02:34:42 +01:00
function isPinnedOrTripleSlashComment ( comment : CommentRange ) {
if ( currentSourceFile . text . charCodeAt ( comment . pos + 1 ) === CharacterCodes . asterisk ) {
return currentSourceFile . text . charCodeAt ( comment . pos + 2 ) === CharacterCodes . exclamation ;
}
// 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
else if ( currentSourceFile . text . charCodeAt ( comment . pos + 1 ) === CharacterCodes . slash &&
comment . pos + 2 < comment . end &&
currentSourceFile . text . charCodeAt ( comment . pos + 2 ) === CharacterCodes . slash &&
currentSourceFile . text . substring ( comment . pos , comment . end ) . match ( fullTripleSlashReferencePathRegEx ) ) {
return true ;
2014-07-17 00:39:14 +02:00
}
2014-07-19 03:06:37 +02:00
}
2014-07-13 01:04:16 +02:00
}
2014-12-02 08:15:13 +01:00
function emitFile ( jsFilePath : string , sourceFile? : SourceFile ) {
2015-02-05 23:41:04 +01:00
emitJavaScript ( jsFilePath , sourceFile ) ;
2014-12-02 08:15:13 +01:00
2015-02-05 23:41:04 +01:00
if ( compilerOptions . declaration ) {
2015-03-19 00:37:52 +01:00
writeDeclarationFile ( jsFilePath , sourceFile , host , resolver , diagnostics ) ;
2014-12-02 08:15:13 +01:00
}
2014-09-06 01:18:39 +02:00
}
2014-07-13 01:04:16 +02:00
}
}