1import { 2 addRange, append, Bundle, chainBundle, CompilerOptions, createEmitHelperFactory, CustomTransformer, 3 CustomTransformerFactory, CustomTransformers, Debug, DiagnosticWithLocation, disposeEmitNodes, EmitFlags, 4 EmitHelper, EmitHint, EmitHost, EmitResolver, EmitTransformers, emptyArray, factory, FunctionDeclaration, 5 getEmitFlags, getEmitModuleKind, getEmitScriptTarget, getJSXTransformEnabled, getParseTreeNode, getSourceFileOfNode, 6 Identifier, isBundle, isSourceFile, LexicalEnvironmentFlags, map, memoize, MemoryDotting, ModuleKind, Node, PerformanceDotting, 7 NodeFactory, NodeFlags, noop, notImplemented, returnUndefined, ScriptTarget, setEmitFlags, some, SourceFile, 8 Statement, SyntaxKind, tracing, TransformationContext, TransformationResult, transformClassFields, 9 transformDeclarations, transformECMAScriptModule, Transformer, TransformerFactory, transformES2015, transformES2016, 10 transformES2017, transformES2018, transformES2019, transformES2020, transformES2021, transformES5, transformESNext, 11 transformGenerators, transformJsx, transformLegacyDecorators, transformModule, transformNodeModule, 12 transformSystemModule, transformTypeScript, VariableDeclaration, 13} from "./_namespaces/ts"; 14import * as performance from "./_namespaces/ts.performance"; 15 16function getModuleTransformer(moduleKind: ModuleKind): TransformerFactory<SourceFile | Bundle> { 17 switch (moduleKind) { 18 case ModuleKind.ESNext: 19 case ModuleKind.ES2022: 20 case ModuleKind.ES2020: 21 case ModuleKind.ES2015: 22 return transformECMAScriptModule; 23 case ModuleKind.System: 24 return transformSystemModule; 25 case ModuleKind.Node16: 26 case ModuleKind.NodeNext: 27 return transformNodeModule; 28 default: 29 return transformModule; 30 } 31} 32 33const enum TransformationState { 34 Uninitialized, 35 Initialized, 36 Completed, 37 Disposed 38} 39 40const enum SyntaxKindFeatureFlags { 41 Substitution = 1 << 0, 42 EmitNotifications = 1 << 1, 43} 44 45/** @internal */ 46export const noTransformers: EmitTransformers = { scriptTransformers: emptyArray, declarationTransformers: emptyArray }; 47 48/** @internal */ 49export function getTransformers(compilerOptions: CompilerOptions, customTransformers?: CustomTransformers, emitOnlyDtsFiles?: boolean): EmitTransformers { 50 return { 51 scriptTransformers: getScriptTransformers(compilerOptions, customTransformers, emitOnlyDtsFiles), 52 declarationTransformers: getDeclarationTransformers(customTransformers), 53 }; 54} 55 56function getScriptTransformers(compilerOptions: CompilerOptions, customTransformers?: CustomTransformers, emitOnlyDtsFiles?: boolean) { 57 if (emitOnlyDtsFiles) return emptyArray; 58 59 const languageVersion = getEmitScriptTarget(compilerOptions); 60 const moduleKind = getEmitModuleKind(compilerOptions); 61 const transformers: TransformerFactory<SourceFile | Bundle>[] = []; 62 63 addRange(transformers, customTransformers && map(customTransformers.before, wrapScriptTransformerFactory)); 64 65 transformers.push(transformTypeScript); 66 transformers.push(transformLegacyDecorators); 67 transformers.push(transformClassFields); 68 69 if (getJSXTransformEnabled(compilerOptions)) { 70 transformers.push(transformJsx); 71 } 72 73 if (languageVersion < ScriptTarget.ESNext) { 74 transformers.push(transformESNext); 75 } 76 77 if (languageVersion < ScriptTarget.ES2021) { 78 transformers.push(transformES2021); 79 } 80 81 if (languageVersion < ScriptTarget.ES2020) { 82 transformers.push(transformES2020); 83 } 84 85 if (languageVersion < ScriptTarget.ES2019) { 86 transformers.push(transformES2019); 87 } 88 89 if (languageVersion < ScriptTarget.ES2018) { 90 transformers.push(transformES2018); 91 } 92 93 if (languageVersion < ScriptTarget.ES2017) { 94 transformers.push(transformES2017); 95 } 96 97 if (languageVersion < ScriptTarget.ES2016) { 98 transformers.push(transformES2016); 99 } 100 101 if (languageVersion < ScriptTarget.ES2015) { 102 transformers.push(transformES2015); 103 transformers.push(transformGenerators); 104 } 105 106 transformers.push(getModuleTransformer(moduleKind)); 107 108 // The ES5 transformer is last so that it can substitute expressions like `exports.default` 109 // for ES3. 110 if (languageVersion < ScriptTarget.ES5) { 111 transformers.push(transformES5); 112 } 113 114 addRange(transformers, customTransformers && map(customTransformers.after, wrapScriptTransformerFactory)); 115 return transformers; 116} 117 118function getDeclarationTransformers(customTransformers?: CustomTransformers) { 119 const transformers: TransformerFactory<SourceFile | Bundle>[] = []; 120 transformers.push(transformDeclarations); 121 addRange(transformers, customTransformers && map(customTransformers.afterDeclarations, wrapDeclarationTransformerFactory)); 122 return transformers; 123} 124 125/** 126 * Wrap a custom script or declaration transformer object in a `Transformer` callback with fallback support for transforming bundles. 127 */ 128function wrapCustomTransformer(transformer: CustomTransformer): Transformer<Bundle | SourceFile> { 129 return node => isBundle(node) ? transformer.transformBundle(node) : transformer.transformSourceFile(node); 130} 131 132/** 133 * Wrap a transformer factory that may return a custom script or declaration transformer object. 134 */ 135function wrapCustomTransformerFactory<T extends SourceFile | Bundle>(transformer: TransformerFactory<T> | CustomTransformerFactory, handleDefault: (context: TransformationContext, tx: Transformer<T>) => Transformer<Bundle | SourceFile>): TransformerFactory<Bundle | SourceFile> { 136 return context => { 137 const customTransformer = transformer(context); 138 return typeof customTransformer === "function" 139 ? handleDefault(context, customTransformer) 140 : wrapCustomTransformer(customTransformer); 141 }; 142} 143 144function wrapScriptTransformerFactory(transformer: TransformerFactory<SourceFile> | CustomTransformerFactory): TransformerFactory<Bundle | SourceFile> { 145 return wrapCustomTransformerFactory(transformer, chainBundle); 146} 147 148function wrapDeclarationTransformerFactory(transformer: TransformerFactory<Bundle | SourceFile> | CustomTransformerFactory): TransformerFactory<Bundle | SourceFile> { 149 return wrapCustomTransformerFactory(transformer, (_, node) => node); 150} 151 152/** @internal */ 153export function noEmitSubstitution(_hint: EmitHint, node: Node) { 154 return node; 155} 156 157/** @internal */ 158export function noEmitNotification(hint: EmitHint, node: Node, callback: (hint: EmitHint, node: Node) => void) { 159 callback(hint, node); 160} 161 162/** 163 * Transforms an array of SourceFiles by passing them through each transformer. 164 * 165 * @param resolver The emit resolver provided by the checker. 166 * @param host The emit host object used to interact with the file system. 167 * @param options Compiler options to surface in the `TransformationContext`. 168 * @param nodes An array of nodes to transform. 169 * @param transforms An array of `TransformerFactory` callbacks. 170 * @param allowDtsFiles A value indicating whether to allow the transformation of .d.ts files. 171 * 172 * @internal 173 */ 174export function transformNodes<T extends Node>(resolver: EmitResolver | undefined, host: EmitHost | undefined, factory: NodeFactory, options: CompilerOptions, nodes: readonly T[], transformers: readonly TransformerFactory<T>[], allowDtsFiles: boolean): TransformationResult<T> { 175 const enabledSyntaxKindFeatures = new Array<SyntaxKindFeatureFlags>(SyntaxKind.Count); 176 let lexicalEnvironmentVariableDeclarations: VariableDeclaration[]; 177 let lexicalEnvironmentFunctionDeclarations: FunctionDeclaration[]; 178 let lexicalEnvironmentStatements: Statement[]; 179 let lexicalEnvironmentFlags = LexicalEnvironmentFlags.None; 180 let lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; 181 let lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; 182 let lexicalEnvironmentStatementsStack: Statement[][] = []; 183 let lexicalEnvironmentFlagsStack: LexicalEnvironmentFlags[] = []; 184 let lexicalEnvironmentStackOffset = 0; 185 let lexicalEnvironmentSuspended = false; 186 let blockScopedVariableDeclarationsStack: Identifier[][] = []; 187 let blockScopeStackOffset = 0; 188 let blockScopedVariableDeclarations: Identifier[]; 189 let emitHelpers: EmitHelper[] | undefined; 190 let onSubstituteNode: TransformationContext["onSubstituteNode"] = noEmitSubstitution; 191 let onEmitNode: TransformationContext["onEmitNode"] = noEmitNotification; 192 let state = TransformationState.Uninitialized; 193 const diagnostics: DiagnosticWithLocation[] = []; 194 195 // The transformation context is provided to each transformer as part of transformer 196 // initialization. 197 const context: TransformationContext = { 198 factory, 199 getCompilerOptions: () => options, 200 getEmitResolver: () => resolver!, // TODO: GH#18217 201 getEmitHost: () => host!, // TODO: GH#18217 202 getEmitHelperFactory: memoize(() => createEmitHelperFactory(context)), 203 startLexicalEnvironment, 204 suspendLexicalEnvironment, 205 resumeLexicalEnvironment, 206 endLexicalEnvironment, 207 setLexicalEnvironmentFlags, 208 getLexicalEnvironmentFlags, 209 hoistVariableDeclaration, 210 hoistFunctionDeclaration, 211 addInitializationStatement, 212 startBlockScope, 213 endBlockScope, 214 addBlockScopedVariable, 215 requestEmitHelper, 216 readEmitHelpers, 217 enableSubstitution, 218 enableEmitNotification, 219 isSubstitutionEnabled, 220 isEmitNotificationEnabled, 221 isLexicalEnvironmentSuspended, 222 get onSubstituteNode() { return onSubstituteNode; }, 223 set onSubstituteNode(value) { 224 Debug.assert(state < TransformationState.Initialized, "Cannot modify transformation hooks after initialization has completed."); 225 Debug.assert(value !== undefined, "Value must not be 'undefined'"); 226 onSubstituteNode = value; 227 }, 228 get onEmitNode() { return onEmitNode; }, 229 set onEmitNode(value) { 230 Debug.assert(state < TransformationState.Initialized, "Cannot modify transformation hooks after initialization has completed."); 231 Debug.assert(value !== undefined, "Value must not be 'undefined'"); 232 onEmitNode = value; 233 }, 234 addDiagnostic(diag) { 235 diagnostics.push(diag); 236 } 237 }; 238 239 // Ensure the parse tree is clean before applying transformations 240 for (const node of nodes) { 241 disposeEmitNodes(getSourceFileOfNode(getParseTreeNode(node))); 242 } 243 244 const recordInfo = MemoryDotting.recordStage(MemoryDotting.TRANSFORM); 245 performance.mark("beforeTransform"); 246 247 // Chain together and initialize each transformer. 248 const transformersWithContext = transformers.map(t => t(context)); 249 const transformation = (node: T): T => { 250 for (const transform of transformersWithContext) { 251 node = transform(node); 252 } 253 return node; 254 }; 255 256 // prevent modification of transformation hooks. 257 state = TransformationState.Initialized; 258 259 // Transform each node. 260 const transformed: T[] = []; 261 for (const node of nodes) { 262 tracing?.push(tracing.Phase.Emit, "transformNodes", node.kind === SyntaxKind.SourceFile ? { path: (node as any as SourceFile).path } : { kind: node.kind, pos: node.pos, end: node.end }); 263 PerformanceDotting.start("transformRoot"); 264 transformed.push((allowDtsFiles ? transformation : transformRoot)(node)); 265 PerformanceDotting.stop("transformRoot"); 266 tracing?.pop(); 267 } 268 269 // prevent modification of the lexical environment. 270 state = TransformationState.Completed; 271 272 performance.mark("afterTransform"); 273 MemoryDotting.stopRecordStage(recordInfo); 274 performance.measure("transformTime", "beforeTransform", "afterTransform"); 275 276 return { 277 transformed, 278 substituteNode, 279 emitNodeWithNotification, 280 isEmitNotificationEnabled, 281 dispose, 282 diagnostics 283 }; 284 285 function transformRoot(node: T) { 286 return node && (!isSourceFile(node) || !node.isDeclarationFile) ? transformation(node) : node; 287 } 288 289 /** 290 * Enables expression substitutions in the pretty printer for the provided SyntaxKind. 291 */ 292 function enableSubstitution(kind: SyntaxKind) { 293 Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed."); 294 enabledSyntaxKindFeatures[kind] |= SyntaxKindFeatureFlags.Substitution; 295 } 296 297 /** 298 * Determines whether expression substitutions are enabled for the provided node. 299 */ 300 function isSubstitutionEnabled(node: Node) { 301 return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.Substitution) !== 0 302 && (getEmitFlags(node) & EmitFlags.NoSubstitution) === 0; 303 } 304 305 /** 306 * Emits a node with possible substitution. 307 * 308 * @param hint A hint as to the intended usage of the node. 309 * @param node The node to emit. 310 * @param emitCallback The callback used to emit the node or its substitute. 311 */ 312 function substituteNode(hint: EmitHint, node: Node) { 313 Debug.assert(state < TransformationState.Disposed, "Cannot substitute a node after the result is disposed."); 314 return node && isSubstitutionEnabled(node) && onSubstituteNode(hint, node) || node; 315 } 316 317 /** 318 * Enables before/after emit notifications in the pretty printer for the provided SyntaxKind. 319 */ 320 function enableEmitNotification(kind: SyntaxKind) { 321 Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed."); 322 enabledSyntaxKindFeatures[kind] |= SyntaxKindFeatureFlags.EmitNotifications; 323 } 324 325 /** 326 * Determines whether before/after emit notifications should be raised in the pretty 327 * printer when it emits a node. 328 */ 329 function isEmitNotificationEnabled(node: Node) { 330 return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.EmitNotifications) !== 0 331 || (getEmitFlags(node) & EmitFlags.AdviseOnEmitNode) !== 0; 332 } 333 334 /** 335 * Emits a node with possible emit notification. 336 * 337 * @param hint A hint as to the intended usage of the node. 338 * @param node The node to emit. 339 * @param emitCallback The callback used to emit the node. 340 */ 341 function emitNodeWithNotification(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { 342 Debug.assert(state < TransformationState.Disposed, "Cannot invoke TransformationResult callbacks after the result is disposed."); 343 if (node) { 344 // TODO: Remove check and unconditionally use onEmitNode when API is breakingly changed 345 // (see https://github.com/microsoft/TypeScript/pull/36248/files/5062623f39120171b98870c71344b3242eb03d23#r369766739) 346 if (isEmitNotificationEnabled(node)) { 347 onEmitNode(hint, node, emitCallback); 348 } 349 else { 350 emitCallback(hint, node); 351 } 352 } 353 } 354 355 /** 356 * Records a hoisted variable declaration for the provided name within a lexical environment. 357 */ 358 function hoistVariableDeclaration(name: Identifier): void { 359 Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization."); 360 Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed."); 361 const decl = setEmitFlags(factory.createVariableDeclaration(name), EmitFlags.NoNestedSourceMaps); 362 if (!lexicalEnvironmentVariableDeclarations) { 363 lexicalEnvironmentVariableDeclarations = [decl]; 364 } 365 else { 366 lexicalEnvironmentVariableDeclarations.push(decl); 367 } 368 if (lexicalEnvironmentFlags & LexicalEnvironmentFlags.InParameters) { 369 lexicalEnvironmentFlags |= LexicalEnvironmentFlags.VariablesHoistedInParameters; 370 } 371 } 372 373 /** 374 * Records a hoisted function declaration within a lexical environment. 375 */ 376 function hoistFunctionDeclaration(func: FunctionDeclaration): void { 377 Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization."); 378 Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed."); 379 setEmitFlags(func, EmitFlags.CustomPrologue); 380 if (!lexicalEnvironmentFunctionDeclarations) { 381 lexicalEnvironmentFunctionDeclarations = [func]; 382 } 383 else { 384 lexicalEnvironmentFunctionDeclarations.push(func); 385 } 386 } 387 388 /** 389 * Adds an initialization statement to the top of the lexical environment. 390 */ 391 function addInitializationStatement(node: Statement): void { 392 Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization."); 393 Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed."); 394 setEmitFlags(node, EmitFlags.CustomPrologue); 395 if (!lexicalEnvironmentStatements) { 396 lexicalEnvironmentStatements = [node]; 397 } 398 else { 399 lexicalEnvironmentStatements.push(node); 400 } 401 } 402 403 /** 404 * Starts a new lexical environment. Any existing hoisted variable or function declarations 405 * are pushed onto a stack, and the related storage variables are reset. 406 */ 407 function startLexicalEnvironment(): void { 408 Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization."); 409 Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed."); 410 Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended."); 411 412 // Save the current lexical environment. Rather than resizing the array we adjust the 413 // stack size variable. This allows us to reuse existing array slots we've 414 // already allocated between transformations to avoid allocation and GC overhead during 415 // transformation. 416 lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentVariableDeclarations; 417 lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentFunctionDeclarations; 418 lexicalEnvironmentStatementsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentStatements; 419 lexicalEnvironmentFlagsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentFlags; 420 lexicalEnvironmentStackOffset++; 421 lexicalEnvironmentVariableDeclarations = undefined!; 422 lexicalEnvironmentFunctionDeclarations = undefined!; 423 lexicalEnvironmentStatements = undefined!; 424 lexicalEnvironmentFlags = LexicalEnvironmentFlags.None; 425 } 426 427 /** Suspends the current lexical environment, usually after visiting a parameter list. */ 428 function suspendLexicalEnvironment(): void { 429 Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization."); 430 Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed."); 431 Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is already suspended."); 432 lexicalEnvironmentSuspended = true; 433 } 434 435 /** Resumes a suspended lexical environment, usually before visiting a function body. */ 436 function resumeLexicalEnvironment(): void { 437 Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization."); 438 Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed."); 439 Debug.assert(lexicalEnvironmentSuspended, "Lexical environment is not suspended."); 440 lexicalEnvironmentSuspended = false; 441 } 442 443 /** 444 * Ends a lexical environment. The previous set of hoisted declarations are restored and 445 * any hoisted declarations added in this environment are returned. 446 */ 447 function endLexicalEnvironment(): Statement[] | undefined { 448 Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization."); 449 Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed."); 450 Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended."); 451 452 let statements: Statement[] | undefined; 453 if (lexicalEnvironmentVariableDeclarations || 454 lexicalEnvironmentFunctionDeclarations || 455 lexicalEnvironmentStatements) { 456 if (lexicalEnvironmentFunctionDeclarations) { 457 statements = [...lexicalEnvironmentFunctionDeclarations]; 458 } 459 460 if (lexicalEnvironmentVariableDeclarations) { 461 const statement = factory.createVariableStatement( 462 /*modifiers*/ undefined, 463 factory.createVariableDeclarationList(lexicalEnvironmentVariableDeclarations) 464 ); 465 466 setEmitFlags(statement, EmitFlags.CustomPrologue); 467 468 if (!statements) { 469 statements = [statement]; 470 } 471 else { 472 statements.push(statement); 473 } 474 } 475 476 if (lexicalEnvironmentStatements) { 477 if (!statements) { 478 statements = [...lexicalEnvironmentStatements]; 479 } 480 else { 481 statements = [...statements, ...lexicalEnvironmentStatements]; 482 } 483 } 484 } 485 486 // Restore the previous lexical environment. 487 lexicalEnvironmentStackOffset--; 488 lexicalEnvironmentVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset]; 489 lexicalEnvironmentFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset]; 490 lexicalEnvironmentStatements = lexicalEnvironmentStatementsStack[lexicalEnvironmentStackOffset]; 491 lexicalEnvironmentFlags = lexicalEnvironmentFlagsStack[lexicalEnvironmentStackOffset]; 492 if (lexicalEnvironmentStackOffset === 0) { 493 lexicalEnvironmentVariableDeclarationsStack = []; 494 lexicalEnvironmentFunctionDeclarationsStack = []; 495 lexicalEnvironmentStatementsStack = []; 496 lexicalEnvironmentFlagsStack = []; 497 } 498 return statements; 499 } 500 501 function setLexicalEnvironmentFlags(flags: LexicalEnvironmentFlags, value: boolean): void { 502 lexicalEnvironmentFlags = value ? 503 lexicalEnvironmentFlags | flags : 504 lexicalEnvironmentFlags & ~flags; 505 } 506 507 function getLexicalEnvironmentFlags(): LexicalEnvironmentFlags { 508 return lexicalEnvironmentFlags; 509 } 510 511 /** 512 * Starts a block scope. Any existing block hoisted variables are pushed onto the stack and the related storage variables are reset. 513 */ 514 function startBlockScope() { 515 Debug.assert(state > TransformationState.Uninitialized, "Cannot start a block scope during initialization."); 516 Debug.assert(state < TransformationState.Completed, "Cannot start a block scope after transformation has completed."); 517 blockScopedVariableDeclarationsStack[blockScopeStackOffset] = blockScopedVariableDeclarations; 518 blockScopeStackOffset++; 519 blockScopedVariableDeclarations = undefined!; 520 } 521 522 /** 523 * Ends a block scope. The previous set of block hoisted variables are restored. Any hoisted declarations are returned. 524 */ 525 function endBlockScope() { 526 Debug.assert(state > TransformationState.Uninitialized, "Cannot end a block scope during initialization."); 527 Debug.assert(state < TransformationState.Completed, "Cannot end a block scope after transformation has completed."); 528 const statements: Statement[] | undefined = some(blockScopedVariableDeclarations) ? 529 [ 530 factory.createVariableStatement( 531 /*modifiers*/ undefined, 532 factory.createVariableDeclarationList( 533 blockScopedVariableDeclarations.map(identifier => factory.createVariableDeclaration(identifier)), 534 NodeFlags.Let 535 ) 536 ) 537 ] : undefined; 538 blockScopeStackOffset--; 539 blockScopedVariableDeclarations = blockScopedVariableDeclarationsStack[blockScopeStackOffset]; 540 if (blockScopeStackOffset === 0) { 541 blockScopedVariableDeclarationsStack = []; 542 } 543 return statements; 544 } 545 546 function addBlockScopedVariable(name: Identifier): void { 547 Debug.assert(blockScopeStackOffset > 0, "Cannot add a block scoped variable outside of an iteration body."); 548 (blockScopedVariableDeclarations || (blockScopedVariableDeclarations = [])).push(name); 549 } 550 551 function requestEmitHelper(helper: EmitHelper): void { 552 Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization."); 553 Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed."); 554 Debug.assert(!helper.scoped, "Cannot request a scoped emit helper."); 555 if (helper.dependencies) { 556 for (const h of helper.dependencies) { 557 requestEmitHelper(h); 558 } 559 } 560 emitHelpers = append(emitHelpers, helper); 561 } 562 563 function readEmitHelpers(): EmitHelper[] | undefined { 564 Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization."); 565 Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed."); 566 const helpers = emitHelpers; 567 emitHelpers = undefined; 568 return helpers; 569 } 570 571 function dispose() { 572 if (state < TransformationState.Disposed) { 573 // Clean up emit nodes on parse tree 574 for (const node of nodes) { 575 disposeEmitNodes(getSourceFileOfNode(getParseTreeNode(node))); 576 } 577 578 // Release references to external entries for GC purposes. 579 lexicalEnvironmentVariableDeclarations = undefined!; 580 lexicalEnvironmentVariableDeclarationsStack = undefined!; 581 lexicalEnvironmentFunctionDeclarations = undefined!; 582 lexicalEnvironmentFunctionDeclarationsStack = undefined!; 583 onSubstituteNode = undefined!; 584 onEmitNode = undefined!; 585 emitHelpers = undefined; 586 587 // Prevent further use of the transformation result. 588 state = TransformationState.Disposed; 589 } 590 } 591 592 /** Determines whether the lexical environment is suspended */ 593 function isLexicalEnvironmentSuspended(): boolean { 594 return lexicalEnvironmentSuspended; 595 } 596} 597 598export const nullTransformationContext: TransformationContext = { 599 factory: factory, // eslint-disable-line object-shorthand 600 getCompilerOptions: () => ({}), 601 getEmitResolver: notImplemented, 602 getEmitHost: notImplemented, 603 getEmitHelperFactory: notImplemented, 604 startLexicalEnvironment: noop, 605 resumeLexicalEnvironment: noop, 606 suspendLexicalEnvironment: noop, 607 endLexicalEnvironment: returnUndefined, 608 setLexicalEnvironmentFlags: noop, 609 getLexicalEnvironmentFlags: () => 0, 610 hoistVariableDeclaration: noop, 611 hoistFunctionDeclaration: noop, 612 addInitializationStatement: noop, 613 startBlockScope: noop, 614 endBlockScope: returnUndefined, 615 addBlockScopedVariable: noop, 616 requestEmitHelper: noop, 617 readEmitHelpers: notImplemented, 618 enableSubstitution: noop, 619 enableEmitNotification: noop, 620 isSubstitutionEnabled: notImplemented, 621 isEmitNotificationEnabled: notImplemented, 622 onSubstituteNode: noEmitSubstitution, 623 onEmitNode: noEmitNotification, 624 addDiagnostic: noop, 625}; 626