From 930944a692ac6fb602b707a96e9c2d8d7b6d8152 Mon Sep 17 00:00:00 2001 From: Daniel Fischer Date: Thu, 12 Nov 2020 17:22:14 +0100 Subject: [PATCH 1/6] ESM: Import helpers only if they're directly requested --- src/compiler/emitter.ts | 2 +- src/compiler/factory/emitHelpers.ts | 2 +- src/compiler/factory/emitNode.ts | 77 +++++++++++++------ src/compiler/factory/nodeFactory.ts | 17 +++- src/compiler/factory/utilities.ts | 2 +- src/compiler/transformer.ts | 32 ++++++-- src/compiler/transformers/classFields.ts | 2 +- src/compiler/transformers/es2015.ts | 2 +- src/compiler/transformers/es2017.ts | 2 +- src/compiler/transformers/es2018.ts | 2 +- src/compiler/transformers/generators.ts | 2 +- src/compiler/transformers/jsx.ts | 2 +- src/compiler/transformers/module/module.ts | 6 +- src/compiler/transformers/ts.ts | 2 +- src/compiler/types.ts | 11 ++- .../reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- .../reference/importHelpersOnlyDirectESM.js | 16 ++++ .../importHelpersOnlyDirectESM.symbols | 22 ++++++ .../importHelpersOnlyDirectESM.types | 31 ++++++++ .../compiler/importHelpersOnlyDirectESM.ts | 12 +++ 21 files changed, 198 insertions(+), 50 deletions(-) create mode 100644 tests/baselines/reference/importHelpersOnlyDirectESM.js create mode 100644 tests/baselines/reference/importHelpersOnlyDirectESM.symbols create mode 100644 tests/baselines/reference/importHelpersOnlyDirectESM.types create mode 100644 tests/cases/compiler/importHelpersOnlyDirectESM.ts diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 24174483fdc6d..ab2c9cbac6fd8 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1100,7 +1100,7 @@ namespace ts { bundleFileInfo.sources.prologues = prologues; } - // Store helpes + // Store helpers const helpers = getHelpersFromBundledSourceFiles(bundle); if (helpers) { if (!bundleFileInfo.sources) bundleFileInfo.sources = {}; diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index 4333272616da6..eff76118bc05b 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -897,4 +897,4 @@ namespace ts { && (getEmitFlags(firstSegment.expression) & EmitFlags.HelperName) && firstSegment.expression.escapedText === helperName; } -} \ No newline at end of file +} diff --git a/src/compiler/factory/emitNode.ts b/src/compiler/factory/emitNode.ts index 1da968d027dec..488c2c4f85e27 100644 --- a/src/compiler/factory/emitNode.ts +++ b/src/compiler/factory/emitNode.ts @@ -193,7 +193,27 @@ namespace ts { */ export function addEmitHelper(node: T, helper: EmitHelper): T { const emitNode = getOrCreateEmitNode(node); - emitNode.helpers = append(emitNode.helpers, helper); + emitNode.helperRequests = append(emitNode.helperRequests, { helper, directlyUsed: true }); + return node; + } + + /** @internal */ + export function addEmitHelperRequests(node: T, requests: EmitHelperRequest[] | undefined): T { + if (some(requests)) { + const emitNode = getOrCreateEmitNode(node); + emitNode.helperRequests = emitNode.helperRequests ?? []; + for (const request of requests) { + const foundRequestIndex: number = findIndex(emitNode.helperRequests, existingRequest => existingRequest.helper === request.helper); + if (foundRequestIndex === -1) { + emitNode.helperRequests = append(emitNode.helperRequests, request); + } else { + emitNode.helperRequests = replaceElement(emitNode.helperRequests, foundRequestIndex, { + ...emitNode.helperRequests[foundRequestIndex], + directlyUsed: emitNode.helperRequests[foundRequestIndex].directlyUsed || request.directlyUsed + }); + } + } + } return node; } @@ -202,10 +222,7 @@ namespace ts { */ export function addEmitHelpers(node: T, helpers: EmitHelper[] | undefined): T { if (some(helpers)) { - const emitNode = getOrCreateEmitNode(node); - for (const helper of helpers) { - emitNode.helpers = appendIfUnique(emitNode.helpers, helper); - } + return addEmitHelperRequests(node, helpers.map(helper => ({ helper, directlyUsed: true }))); } return node; } @@ -214,9 +231,14 @@ namespace ts { * Removes an EmitHelper from a node. */ export function removeEmitHelper(node: Node, helper: EmitHelper): boolean { - const helpers = node.emitNode?.helpers; - if (helpers) { - return orderedRemoveItem(helpers, helper); + const helperRequests = node.emitNode?.helperRequests; + if (helperRequests) { + const foundRequestIndex = findIndex(helperRequests, request => request.helper === helper); + if (foundRequestIndex === -1) { + return false; + } + orderedRemoveItemAt(helperRequests, foundRequestIndex); + return true; } return false; } @@ -224,8 +246,8 @@ namespace ts { /** * Gets the EmitHelpers of a node. */ - export function getEmitHelpers(node: Node): EmitHelper[] | undefined { - return node.emitNode?.helpers; + export function getEmitHelpers(node: Node, directlyUsedOnly = false): EmitHelper[] | undefined { + return node.emitNode?.helperRequests?.filter(request => !directlyUsedOnly || request.directlyUsed).map(request => request.helper); } /** @@ -233,24 +255,33 @@ namespace ts { */ export function moveEmitHelpers(source: Node, target: Node, predicate: (helper: EmitHelper) => boolean) { const sourceEmitNode = source.emitNode; - const sourceEmitHelpers = sourceEmitNode && sourceEmitNode.helpers; - if (!some(sourceEmitHelpers)) return; + const sourceEmitHelperRequests = sourceEmitNode && sourceEmitNode.helperRequests; + if (!some(sourceEmitHelperRequests)) return; const targetEmitNode = getOrCreateEmitNode(target); - let helpersRemoved = 0; - for (let i = 0; i < sourceEmitHelpers.length; i++) { - const helper = sourceEmitHelpers[i]; - if (predicate(helper)) { - helpersRemoved++; - targetEmitNode.helpers = appendIfUnique(targetEmitNode.helpers, helper); + targetEmitNode.helperRequests = targetEmitNode.helperRequests ?? []; + let requestsRemoved = 0; + for (let i = 0; i < sourceEmitHelperRequests.length; i++) { + const request = sourceEmitHelperRequests[i]; + if (predicate(request.helper)) { + requestsRemoved++; + const foundRequestIndex: number = findIndex(targetEmitNode.helperRequests, targetRequest => request.helper === targetRequest.helper); + if (foundRequestIndex === -1) { + targetEmitNode.helperRequests = append(targetEmitNode.helperRequests, request); + } else { + targetEmitNode.helperRequests = replaceElement(targetEmitNode.helperRequests, foundRequestIndex, { + ...targetEmitNode.helperRequests[foundRequestIndex], + directlyUsed: request.directlyUsed || targetEmitNode.helperRequests[foundRequestIndex].directlyUsed + }); + } } - else if (helpersRemoved > 0) { - sourceEmitHelpers[i - helpersRemoved] = helper; + else if (requestsRemoved > 0) { + sourceEmitHelperRequests[i - requestsRemoved] = request; } } - if (helpersRemoved > 0) { - sourceEmitHelpers.length -= helpersRemoved; + if (requestsRemoved > 0) { + sourceEmitHelperRequests.length -= requestsRemoved; } } @@ -259,4 +290,4 @@ namespace ts { getOrCreateEmitNode(node).flags |= EmitFlags.IgnoreSourceNewlines; return node; } -} \ No newline at end of file +} diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 5f3a19a8949ac..026177436f015 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -6377,7 +6377,7 @@ namespace ts { sourceMapRange, tokenSourceMapRanges, constantValue, - helpers, + helperRequests, startsOnNewLine, } = sourceEmitNode; if (!destEmitNode) destEmitNode = {} as EmitNode; @@ -6389,9 +6389,18 @@ namespace ts { if (sourceMapRange) destEmitNode.sourceMapRange = sourceMapRange; if (tokenSourceMapRanges) destEmitNode.tokenSourceMapRanges = mergeTokenSourceMapRanges(tokenSourceMapRanges, destEmitNode.tokenSourceMapRanges!); if (constantValue !== undefined) destEmitNode.constantValue = constantValue; - if (helpers) { - for (const helper of helpers) { - destEmitNode.helpers = appendIfUnique(destEmitNode.helpers, helper); + if (some(helperRequests)) { + destEmitNode.helperRequests = destEmitNode.helperRequests ?? []; + for (const request of helperRequests) { + const foundRequestIndex: number = findIndex(destEmitNode.helperRequests, destRequest => request.helper === destRequest.helper); + if (foundRequestIndex === -1) { + destEmitNode.helperRequests = append(destEmitNode.helperRequests, request); + } else { + destEmitNode.helperRequests = replaceElement(destEmitNode.helperRequests, foundRequestIndex, { + ...destEmitNode.helperRequests[foundRequestIndex], + directlyUsed: request.directlyUsed || destEmitNode.helperRequests[foundRequestIndex].directlyUsed + }); + } } } if (startsOnNewLine !== undefined) destEmitNode.startsOnNewLine = startsOnNewLine; diff --git a/src/compiler/factory/utilities.ts b/src/compiler/factory/utilities.ts index 8b18c3152e3ff..4bbfefb4e52cd 100644 --- a/src/compiler/factory/utilities.ts +++ b/src/compiler/factory/utilities.ts @@ -407,7 +407,7 @@ namespace ts { const moduleKind = getEmitModuleKind(compilerOptions); if (moduleKind >= ModuleKind.ES2015 && moduleKind <= ModuleKind.ESNext) { // use named imports - const helpers = getEmitHelpers(sourceFile); + const helpers = getEmitHelpers(sourceFile, true); if (helpers) { const helperNames: string[] = []; for (const helper of helpers) { diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index efca6f7f187d3..cb97158447524 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -155,7 +155,7 @@ namespace ts { let lexicalEnvironmentFlagsStack: LexicalEnvironmentFlags[] = []; let lexicalEnvironmentStackOffset = 0; let lexicalEnvironmentSuspended = false; - let emitHelpers: EmitHelper[] | undefined; + let emitHelperRequests: EmitHelperRequest[] | undefined; let onSubstituteNode: TransformationContext["onSubstituteNode"] = noEmitSubstitution; let onEmitNode: TransformationContext["onEmitNode"] = noEmitNotification; let state = TransformationState.Uninitialized; @@ -178,8 +178,10 @@ namespace ts { hoistVariableDeclaration, hoistFunctionDeclaration, addInitializationStatement, + forwardEmitHelperRequest, requestEmitHelper, readEmitHelpers, + readEmitHelperRequests, enableSubstitution, enableEmitNotification, isSubstitutionEnabled, @@ -469,23 +471,37 @@ namespace ts { return lexicalEnvironmentFlags; } - function requestEmitHelper(helper: EmitHelper): void { + function forwardEmitHelperRequest(request: EmitHelperRequest) { + Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization."); + Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed."); + emitHelperRequests = append(emitHelperRequests, request); + } + + function requestEmitHelper(helper: EmitHelper, directlyUsed = true): void { Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization."); Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed."); Debug.assert(!helper.scoped, "Cannot request a scoped emit helper."); if (helper.dependencies) { for (const h of helper.dependencies) { - requestEmitHelper(h); + requestEmitHelper(h, false); } } - emitHelpers = append(emitHelpers, helper); + emitHelperRequests = append(emitHelperRequests, { helper, directlyUsed }); + } + + function readEmitHelperRequests(): EmitHelperRequest[] | undefined { + Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization."); + Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed."); + const helperRequests = emitHelperRequests; + emitHelperRequests = undefined; + return helperRequests; } function readEmitHelpers(): EmitHelper[] | undefined { Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization."); Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed."); - const helpers = emitHelpers; - emitHelpers = undefined; + const helpers = emitHelperRequests?.map(({helper}) => helper); + emitHelperRequests = undefined; return helpers; } @@ -503,7 +519,7 @@ namespace ts { lexicalEnvironmentFunctionDeclarationsStack = undefined!; onSubstituteNode = undefined!; onEmitNode = undefined!; - emitHelpers = undefined; + emitHelperRequests = undefined; // Prevent further use of the transformation result. state = TransformationState.Disposed; @@ -530,6 +546,8 @@ namespace ts { onEmitNode: noop, onSubstituteNode: notImplemented, readEmitHelpers: notImplemented, + readEmitHelperRequests: notImplemented, + forwardEmitHelperRequest: noop, requestEmitHelper: noop, resumeLexicalEnvironment: noop, startLexicalEnvironment: noop, diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index f9e539770dcdc..a015e5eaab233 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -75,7 +75,7 @@ namespace ts { return node; } const visited = visitEachChild(node, visitor, context); - addEmitHelpers(visited, context.readEmitHelpers()); + addEmitHelperRequests(visited, context.readEmitHelperRequests()); return visited; } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 6f28a651190e7..f14ed4c3edb4c 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -293,7 +293,7 @@ namespace ts { currentText = node.text; const visited = visitSourceFile(node); - addEmitHelpers(visited, context.readEmitHelpers()); + addEmitHelperRequests(visited, context.readEmitHelperRequests()); currentSourceFile = undefined!; currentText = undefined!; diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 6c3c8c1332acf..4ebaaa9c003d2 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -68,7 +68,7 @@ namespace ts { setContextFlag(ContextFlags.NonTopLevel, false); setContextFlag(ContextFlags.HasLexicalThis, !isEffectiveStrictModeSourceFile(node, compilerOptions)); const visited = visitEachChild(node, visitor, context); - addEmitHelpers(visited, context.readEmitHelpers()); + addEmitHelperRequests(visited, context.readEmitHelperRequests()); return visited; } diff --git a/src/compiler/transformers/es2018.ts b/src/compiler/transformers/es2018.ts index 41fcc6c25d9b9..d89ec9bfcd285 100644 --- a/src/compiler/transformers/es2018.ts +++ b/src/compiler/transformers/es2018.ts @@ -111,7 +111,7 @@ namespace ts { currentSourceFile = node; const visited = visitSourceFile(node); - addEmitHelpers(visited, context.readEmitHelpers()); + addEmitHelperRequests(visited, context.readEmitHelperRequests()); currentSourceFile = undefined!; taggedTemplateStringDeclarations = undefined!; diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 6ae08275e3a02..6c7c0c8553b20 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -301,7 +301,7 @@ namespace ts { const visited = visitEachChild(node, visitor, context); - addEmitHelpers(visited, context.readEmitHelpers()); + addEmitHelperRequests(visited, context.readEmitHelperRequests()); return visited; } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 9814f29b2a28f..8d13b4735e115 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -76,7 +76,7 @@ namespace ts { currentFileState = {}; currentFileState.importSpecifier = getJSXImplicitImportBase(compilerOptions, node); let visited = visitEachChild(node, visitor, context); - addEmitHelpers(visited, context.readEmitHelpers()); + addEmitHelperRequests(visited, context.readEmitHelperRequests()); let statements: readonly Statement[] = visited.statements; if (currentFileState.filenameDeclaration) { statements = insertStatementAfterCustomPrologue(statements.slice(), factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([currentFileState.filenameDeclaration], NodeFlags.Const))); diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index d2f4917d50b7b..9355414f250d2 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -120,7 +120,7 @@ namespace ts { insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment()); const updated = factory.updateSourceFile(node, setTextRange(factory.createNodeArray(statements), node.statements)); - addEmitHelpers(updated, context.readEmitHelpers()); + addEmitHelperRequests(updated, context.readEmitHelperRequests()); return updated; } @@ -207,7 +207,7 @@ namespace ts { ) ); - addEmitHelpers(updated, context.readEmitHelpers()); + addEmitHelperRequests(updated, context.readEmitHelperRequests()); return updated; } @@ -347,7 +347,7 @@ namespace ts { ) ); - addEmitHelpers(updated, context.readEmitHelpers()); + addEmitHelperRequests(updated, context.readEmitHelperRequests()); return updated; } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 9cc9c1c3fc92e..bf4a60f3764c4 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -117,7 +117,7 @@ namespace ts { currentSourceFile = node; const visited = saveStateAndInvoke(node, visitSourceFile); - addEmitHelpers(visited, context.readEmitHelpers()); + addEmitHelperRequests(visited, context.readEmitHelperRequests()); currentSourceFile = undefined!; return visited; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4352e2f2171e3..f8858927e1216 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6539,7 +6539,7 @@ namespace ts { constantValue?: string | number; // The constant value of an expression externalHelpersModuleName?: Identifier; // The local name for an imported helpers module externalHelpers?: boolean; - helpers?: EmitHelper[]; // Emit helpers for the node + helperRequests?: EmitHelperRequest[]; // Emit helper requests for the node startsOnNewLine?: boolean; // If the node should begin on a new line } @@ -6586,6 +6586,12 @@ namespace ts { readonly dependencies?: EmitHelper[] } + /*@internal*/ + export interface EmitHelperRequest { + readonly helper: EmitHelper; + readonly directlyUsed: boolean; + } + export interface UnscopedEmitHelper extends EmitHelper { readonly scoped: false; // Indicates whether the helper MUST be emitted in the current scope. /* @internal */ @@ -7510,6 +7516,9 @@ namespace ts { /*@internal*/ getEmitHost(): EmitHost; /*@internal*/ getEmitHelperFactory(): EmitHelperFactory; + /*@internal*/ forwardEmitHelperRequest(request: EmitHelperRequest): void; + /*@internal*/ readEmitHelperRequests(): EmitHelperRequest[] | undefined; + /** Records a request for a non-scoped emit helper in the current context. */ requestEmitHelper(helper: EmitHelper): void; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index be9c82b1e1d9f..5121bd06a793c 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4331,7 +4331,7 @@ declare namespace ts { /** * Gets the EmitHelpers of a node. */ - function getEmitHelpers(node: Node): EmitHelper[] | undefined; + function getEmitHelpers(node: Node, directlyUsedOnly?: boolean): EmitHelper[] | undefined; /** * Moves matching emit helpers from a source node to a target node. */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index f8f86d5b88fb2..56eaa45a4bee6 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4331,7 +4331,7 @@ declare namespace ts { /** * Gets the EmitHelpers of a node. */ - function getEmitHelpers(node: Node): EmitHelper[] | undefined; + function getEmitHelpers(node: Node, directlyUsedOnly?: boolean): EmitHelper[] | undefined; /** * Moves matching emit helpers from a source node to a target node. */ diff --git a/tests/baselines/reference/importHelpersOnlyDirectESM.js b/tests/baselines/reference/importHelpersOnlyDirectESM.js new file mode 100644 index 0000000000000..3088f96be0844 --- /dev/null +++ b/tests/baselines/reference/importHelpersOnlyDirectESM.js @@ -0,0 +1,16 @@ +//// [tests/cases/compiler/importHelpersOnlyDirectESM.ts] //// + +//// [a.ts] +const a = [1, 2]; +const b = [3, 4] +export const c = [...a, ...b]; + +//// [tslib.d.ts] +export declare function __read(o: any, n?: number): any[]; +export declare function __spread(...args: any[][]): any[]; + +//// [a.js] +import { __spread } from "tslib"; +var a = [1, 2]; +var b = [3, 4]; +export var c = __spread(a, b); diff --git a/tests/baselines/reference/importHelpersOnlyDirectESM.symbols b/tests/baselines/reference/importHelpersOnlyDirectESM.symbols new file mode 100644 index 0000000000000..b1ce29c0352bc --- /dev/null +++ b/tests/baselines/reference/importHelpersOnlyDirectESM.symbols @@ -0,0 +1,22 @@ +=== tests/cases/compiler/a.ts === +const a = [1, 2]; +>a : Symbol(a, Decl(a.ts, 0, 5)) + +const b = [3, 4] +>b : Symbol(b, Decl(a.ts, 1, 5)) + +export const c = [...a, ...b]; +>c : Symbol(c, Decl(a.ts, 2, 12)) +>a : Symbol(a, Decl(a.ts, 0, 5)) +>b : Symbol(b, Decl(a.ts, 1, 5)) + +=== tests/cases/compiler/tslib.d.ts === +export declare function __read(o: any, n?: number): any[]; +>__read : Symbol(__read, Decl(tslib.d.ts, --, --)) +>o : Symbol(o, Decl(tslib.d.ts, --, --)) +>n : Symbol(n, Decl(tslib.d.ts, --, --)) + +export declare function __spread(...args: any[][]): any[]; +>__spread : Symbol(__spread, Decl(tslib.d.ts, --, --)) +>args : Symbol(args, Decl(tslib.d.ts, --, --)) + diff --git a/tests/baselines/reference/importHelpersOnlyDirectESM.types b/tests/baselines/reference/importHelpersOnlyDirectESM.types new file mode 100644 index 0000000000000..87d61784175a9 --- /dev/null +++ b/tests/baselines/reference/importHelpersOnlyDirectESM.types @@ -0,0 +1,31 @@ +=== tests/cases/compiler/a.ts === +const a = [1, 2]; +>a : number[] +>[1, 2] : number[] +>1 : 1 +>2 : 2 + +const b = [3, 4] +>b : number[] +>[3, 4] : number[] +>3 : 3 +>4 : 4 + +export const c = [...a, ...b]; +>c : number[] +>[...a, ...b] : number[] +>...a : number +>a : number[] +>...b : number +>b : number[] + +=== tests/cases/compiler/tslib.d.ts === +export declare function __read(o: any, n?: number): any[]; +>__read : (o: any, n?: number) => any[] +>o : any +>n : number + +export declare function __spread(...args: any[][]): any[]; +>__spread : (...args: any[][]) => any[] +>args : any[][] + diff --git a/tests/cases/compiler/importHelpersOnlyDirectESM.ts b/tests/cases/compiler/importHelpersOnlyDirectESM.ts new file mode 100644 index 0000000000000..e17ea29ae4fea --- /dev/null +++ b/tests/cases/compiler/importHelpersOnlyDirectESM.ts @@ -0,0 +1,12 @@ +// @importHelpers: true +// @module: es2015 +// @target: es5 +// @downlevelIteration: true +// @filename: a.ts +const a = [1, 2]; +const b = [3, 4] +export const c = [...a, ...b]; + +// @filename: tslib.d.ts +export declare function __read(o: any, n?: number): any[]; +export declare function __spread(...args: any[][]): any[]; \ No newline at end of file From 78affd44e7f66827b51f741da5fbc89a5f9041d1 Mon Sep 17 00:00:00 2001 From: Daniel Fischer Date: Sun, 15 Nov 2020 19:38:52 +0100 Subject: [PATCH 2/6] Remove unused method --- src/compiler/transformer.ts | 8 -------- src/compiler/types.ts | 1 - 2 files changed, 9 deletions(-) diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index cb97158447524..2a8d41899898a 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -178,7 +178,6 @@ namespace ts { hoistVariableDeclaration, hoistFunctionDeclaration, addInitializationStatement, - forwardEmitHelperRequest, requestEmitHelper, readEmitHelpers, readEmitHelperRequests, @@ -471,12 +470,6 @@ namespace ts { return lexicalEnvironmentFlags; } - function forwardEmitHelperRequest(request: EmitHelperRequest) { - Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization."); - Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed."); - emitHelperRequests = append(emitHelperRequests, request); - } - function requestEmitHelper(helper: EmitHelper, directlyUsed = true): void { Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization."); Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed."); @@ -547,7 +540,6 @@ namespace ts { onSubstituteNode: notImplemented, readEmitHelpers: notImplemented, readEmitHelperRequests: notImplemented, - forwardEmitHelperRequest: noop, requestEmitHelper: noop, resumeLexicalEnvironment: noop, startLexicalEnvironment: noop, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f8858927e1216..bcc62bda24a14 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7516,7 +7516,6 @@ namespace ts { /*@internal*/ getEmitHost(): EmitHost; /*@internal*/ getEmitHelperFactory(): EmitHelperFactory; - /*@internal*/ forwardEmitHelperRequest(request: EmitHelperRequest): void; /*@internal*/ readEmitHelperRequests(): EmitHelperRequest[] | undefined; /** Records a request for a non-scoped emit helper in the current context. */ From 56b8f17655f098993b02e6146e185e89e5d321cb Mon Sep 17 00:00:00 2001 From: Daniel Fischer Date: Sun, 15 Nov 2020 19:39:55 +0100 Subject: [PATCH 3/6] Other small things my IDE found --- src/compiler/transformer.ts | 2 +- src/compiler/types.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 2a8d41899898a..2640b86b7b9bb 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -140,7 +140,7 @@ namespace ts { * @param host The emit host object used to interact with the file system. * @param options Compiler options to surface in the `TransformationContext`. * @param nodes An array of nodes to transform. - * @param transforms An array of `TransformerFactory` callbacks. + * @param transformers An array of `TransformerFactory` callbacks. * @param allowDtsFiles A value indicating whether to allow the transformation of .d.ts files. */ export function transformNodes(resolver: EmitResolver | undefined, host: EmitHost | undefined, factory: NodeFactory, options: CompilerOptions, nodes: readonly T[], transformers: readonly TransformerFactory[], allowDtsFiles: boolean): TransformationResult { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index bcc62bda24a14..dfed7ee945204 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3297,7 +3297,6 @@ namespace ts { | FlowStart | FlowLabel | FlowAssignment - | FlowCall | FlowCondition | FlowSwitchClause | FlowArrayMutation From 5c0bdb07fbcc3a5d450eb59ed6ffdbb845a017a6 Mon Sep 17 00:00:00 2001 From: Daniel Fischer Date: Sun, 15 Nov 2020 20:01:36 +0100 Subject: [PATCH 4/6] Accept baselines --- tests/baselines/reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 5121bd06a793c..da3c1d3f5876d 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1872,7 +1872,7 @@ declare namespace ts { Label = 12, Condition = 96 } - export type FlowNode = FlowStart | FlowLabel | FlowAssignment | FlowCall | FlowCondition | FlowSwitchClause | FlowArrayMutation | FlowCall | FlowReduceLabel; + export type FlowNode = FlowStart | FlowLabel | FlowAssignment | FlowCondition | FlowSwitchClause | FlowArrayMutation | FlowCall | FlowReduceLabel; export interface FlowNodeBase { flags: FlowFlags; id?: number; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 56eaa45a4bee6..2bd55ae3b51a0 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1872,7 +1872,7 @@ declare namespace ts { Label = 12, Condition = 96 } - export type FlowNode = FlowStart | FlowLabel | FlowAssignment | FlowCall | FlowCondition | FlowSwitchClause | FlowArrayMutation | FlowCall | FlowReduceLabel; + export type FlowNode = FlowStart | FlowLabel | FlowAssignment | FlowCondition | FlowSwitchClause | FlowArrayMutation | FlowCall | FlowReduceLabel; export interface FlowNodeBase { flags: FlowFlags; id?: number; From b1d903b2a1d16f282e8dd7df598d9cbc26031ad8 Mon Sep 17 00:00:00 2001 From: Daniel Fischer Date: Sun, 15 Nov 2020 20:25:16 +0100 Subject: [PATCH 5/6] Appease linter --- src/compiler/factory/emitNode.ts | 6 ++++-- src/compiler/factory/nodeFactory.ts | 3 ++- src/compiler/factory/utilities.ts | 2 +- src/compiler/transformer.ts | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/compiler/factory/emitNode.ts b/src/compiler/factory/emitNode.ts index 488c2c4f85e27..cd1c32b1db85d 100644 --- a/src/compiler/factory/emitNode.ts +++ b/src/compiler/factory/emitNode.ts @@ -206,7 +206,8 @@ namespace ts { const foundRequestIndex: number = findIndex(emitNode.helperRequests, existingRequest => existingRequest.helper === request.helper); if (foundRequestIndex === -1) { emitNode.helperRequests = append(emitNode.helperRequests, request); - } else { + } + else { emitNode.helperRequests = replaceElement(emitNode.helperRequests, foundRequestIndex, { ...emitNode.helperRequests[foundRequestIndex], directlyUsed: emitNode.helperRequests[foundRequestIndex].directlyUsed || request.directlyUsed @@ -268,7 +269,8 @@ namespace ts { const foundRequestIndex: number = findIndex(targetEmitNode.helperRequests, targetRequest => request.helper === targetRequest.helper); if (foundRequestIndex === -1) { targetEmitNode.helperRequests = append(targetEmitNode.helperRequests, request); - } else { + } + else { targetEmitNode.helperRequests = replaceElement(targetEmitNode.helperRequests, foundRequestIndex, { ...targetEmitNode.helperRequests[foundRequestIndex], directlyUsed: request.directlyUsed || targetEmitNode.helperRequests[foundRequestIndex].directlyUsed diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 026177436f015..e1e1f07aaf688 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -6395,7 +6395,8 @@ namespace ts { const foundRequestIndex: number = findIndex(destEmitNode.helperRequests, destRequest => request.helper === destRequest.helper); if (foundRequestIndex === -1) { destEmitNode.helperRequests = append(destEmitNode.helperRequests, request); - } else { + } + else { destEmitNode.helperRequests = replaceElement(destEmitNode.helperRequests, foundRequestIndex, { ...destEmitNode.helperRequests[foundRequestIndex], directlyUsed: request.directlyUsed || destEmitNode.helperRequests[foundRequestIndex].directlyUsed diff --git a/src/compiler/factory/utilities.ts b/src/compiler/factory/utilities.ts index 4bbfefb4e52cd..14ef8e2ce6860 100644 --- a/src/compiler/factory/utilities.ts +++ b/src/compiler/factory/utilities.ts @@ -407,7 +407,7 @@ namespace ts { const moduleKind = getEmitModuleKind(compilerOptions); if (moduleKind >= ModuleKind.ES2015 && moduleKind <= ModuleKind.ESNext) { // use named imports - const helpers = getEmitHelpers(sourceFile, true); + const helpers = getEmitHelpers(sourceFile, /*directlyUsedOnly*/ true); if (helpers) { const helperNames: string[] = []; for (const helper of helpers) { diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 2640b86b7b9bb..ca80759fdd802 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -476,7 +476,7 @@ namespace ts { Debug.assert(!helper.scoped, "Cannot request a scoped emit helper."); if (helper.dependencies) { for (const h of helper.dependencies) { - requestEmitHelper(h, false); + requestEmitHelper(h, /*directlyUsed*/ false); } } emitHelperRequests = append(emitHelperRequests, { helper, directlyUsed }); From a91000f89e3ef4da9637d5f81f85093b6cb1822b Mon Sep 17 00:00:00 2001 From: Daniel Fischer Date: Tue, 12 Jan 2021 17:53:31 +0100 Subject: [PATCH 6/6] remove test --- .../reference/importHelpersOnlyDirectESM.js | 16 ---------- .../importHelpersOnlyDirectESM.symbols | 22 ------------- .../importHelpersOnlyDirectESM.types | 31 ------------------- .../compiler/importHelpersOnlyDirectESM.ts | 12 ------- 4 files changed, 81 deletions(-) delete mode 100644 tests/baselines/reference/importHelpersOnlyDirectESM.js delete mode 100644 tests/baselines/reference/importHelpersOnlyDirectESM.symbols delete mode 100644 tests/baselines/reference/importHelpersOnlyDirectESM.types delete mode 100644 tests/cases/compiler/importHelpersOnlyDirectESM.ts diff --git a/tests/baselines/reference/importHelpersOnlyDirectESM.js b/tests/baselines/reference/importHelpersOnlyDirectESM.js deleted file mode 100644 index 3088f96be0844..0000000000000 --- a/tests/baselines/reference/importHelpersOnlyDirectESM.js +++ /dev/null @@ -1,16 +0,0 @@ -//// [tests/cases/compiler/importHelpersOnlyDirectESM.ts] //// - -//// [a.ts] -const a = [1, 2]; -const b = [3, 4] -export const c = [...a, ...b]; - -//// [tslib.d.ts] -export declare function __read(o: any, n?: number): any[]; -export declare function __spread(...args: any[][]): any[]; - -//// [a.js] -import { __spread } from "tslib"; -var a = [1, 2]; -var b = [3, 4]; -export var c = __spread(a, b); diff --git a/tests/baselines/reference/importHelpersOnlyDirectESM.symbols b/tests/baselines/reference/importHelpersOnlyDirectESM.symbols deleted file mode 100644 index b1ce29c0352bc..0000000000000 --- a/tests/baselines/reference/importHelpersOnlyDirectESM.symbols +++ /dev/null @@ -1,22 +0,0 @@ -=== tests/cases/compiler/a.ts === -const a = [1, 2]; ->a : Symbol(a, Decl(a.ts, 0, 5)) - -const b = [3, 4] ->b : Symbol(b, Decl(a.ts, 1, 5)) - -export const c = [...a, ...b]; ->c : Symbol(c, Decl(a.ts, 2, 12)) ->a : Symbol(a, Decl(a.ts, 0, 5)) ->b : Symbol(b, Decl(a.ts, 1, 5)) - -=== tests/cases/compiler/tslib.d.ts === -export declare function __read(o: any, n?: number): any[]; ->__read : Symbol(__read, Decl(tslib.d.ts, --, --)) ->o : Symbol(o, Decl(tslib.d.ts, --, --)) ->n : Symbol(n, Decl(tslib.d.ts, --, --)) - -export declare function __spread(...args: any[][]): any[]; ->__spread : Symbol(__spread, Decl(tslib.d.ts, --, --)) ->args : Symbol(args, Decl(tslib.d.ts, --, --)) - diff --git a/tests/baselines/reference/importHelpersOnlyDirectESM.types b/tests/baselines/reference/importHelpersOnlyDirectESM.types deleted file mode 100644 index 87d61784175a9..0000000000000 --- a/tests/baselines/reference/importHelpersOnlyDirectESM.types +++ /dev/null @@ -1,31 +0,0 @@ -=== tests/cases/compiler/a.ts === -const a = [1, 2]; ->a : number[] ->[1, 2] : number[] ->1 : 1 ->2 : 2 - -const b = [3, 4] ->b : number[] ->[3, 4] : number[] ->3 : 3 ->4 : 4 - -export const c = [...a, ...b]; ->c : number[] ->[...a, ...b] : number[] ->...a : number ->a : number[] ->...b : number ->b : number[] - -=== tests/cases/compiler/tslib.d.ts === -export declare function __read(o: any, n?: number): any[]; ->__read : (o: any, n?: number) => any[] ->o : any ->n : number - -export declare function __spread(...args: any[][]): any[]; ->__spread : (...args: any[][]) => any[] ->args : any[][] - diff --git a/tests/cases/compiler/importHelpersOnlyDirectESM.ts b/tests/cases/compiler/importHelpersOnlyDirectESM.ts deleted file mode 100644 index e17ea29ae4fea..0000000000000 --- a/tests/cases/compiler/importHelpersOnlyDirectESM.ts +++ /dev/null @@ -1,12 +0,0 @@ -// @importHelpers: true -// @module: es2015 -// @target: es5 -// @downlevelIteration: true -// @filename: a.ts -const a = [1, 2]; -const b = [3, 4] -export const c = [...a, ...b]; - -// @filename: tslib.d.ts -export declare function __read(o: any, n?: number): any[]; -export declare function __spread(...args: any[][]): any[]; \ No newline at end of file