Skip to content

importHelpers: only import direct dependencies with ESM #41545

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/factory/emitHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -897,4 +897,4 @@ namespace ts {
&& (getEmitFlags(firstSegment.expression) & EmitFlags.HelperName)
&& firstSegment.expression.escapedText === helperName;
}
}
}
79 changes: 56 additions & 23 deletions src/compiler/factory/emitNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,28 @@ namespace ts {
*/
export function addEmitHelper<T extends Node>(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<T extends Node>(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;
}

Expand All @@ -202,10 +223,7 @@ namespace ts {
*/
export function addEmitHelpers<T extends Node>(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;
}
Expand All @@ -214,43 +232,58 @@ 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;
}

/**
* 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);
}

/**
* Moves matching emit helpers from a source node to a target node.
*/
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;
}
}

Expand All @@ -259,4 +292,4 @@ namespace ts {
getOrCreateEmitNode(node).flags |= EmitFlags.IgnoreSourceNewlines;
return node;
}
}
}
18 changes: 14 additions & 4 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6377,7 +6377,7 @@ namespace ts {
sourceMapRange,
tokenSourceMapRanges,
constantValue,
helpers,
helperRequests,
startsOnNewLine,
} = sourceEmitNode;
if (!destEmitNode) destEmitNode = {} as EmitNode;
Expand All @@ -6389,9 +6389,19 @@ 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;
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/factory/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, /*directlyUsedOnly*/ true);
if (helpers) {
const helperNames: string[] = [];
for (const helper of helpers) {
Expand Down
26 changes: 18 additions & 8 deletions src/compiler/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T extends Node>(resolver: EmitResolver | undefined, host: EmitHost | undefined, factory: NodeFactory, options: CompilerOptions, nodes: readonly T[], transformers: readonly TransformerFactory<T>[], allowDtsFiles: boolean): TransformationResult<T> {
Expand All @@ -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;
Expand All @@ -180,6 +180,7 @@ namespace ts {
addInitializationStatement,
requestEmitHelper,
readEmitHelpers,
readEmitHelperRequests,
enableSubstitution,
enableEmitNotification,
isSubstitutionEnabled,
Expand Down Expand Up @@ -469,23 +470,31 @@ namespace ts {
return lexicalEnvironmentFlags;
}

function requestEmitHelper(helper: EmitHelper): void {
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, /*directlyUsed*/ 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;
}

Expand All @@ -503,7 +512,7 @@ namespace ts {
lexicalEnvironmentFunctionDeclarationsStack = undefined!;
onSubstituteNode = undefined!;
onEmitNode = undefined!;
emitHelpers = undefined;
emitHelperRequests = undefined;

// Prevent further use of the transformation result.
state = TransformationState.Disposed;
Expand All @@ -530,6 +539,7 @@ namespace ts {
onEmitNode: noop,
onSubstituteNode: notImplemented,
readEmitHelpers: notImplemented,
readEmitHelperRequests: notImplemented,
requestEmitHelper: noop,
resumeLexicalEnvironment: noop,
startLexicalEnvironment: noop,
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformers/classFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ namespace ts {
return node;
}
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());
addEmitHelperRequests(visited, context.readEmitHelperRequests());
return visited;
}

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformers/es2015.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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!;
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformers/es2017.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformers/es2018.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ namespace ts {

currentSourceFile = node;
const visited = visitSourceFile(node);
addEmitHelpers(visited, context.readEmitHelpers());
addEmitHelperRequests(visited, context.readEmitHelperRequests());

currentSourceFile = undefined!;
taggedTemplateStringDeclarations = undefined!;
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformers/generators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ namespace ts {


const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());
addEmitHelperRequests(visited, context.readEmitHelperRequests());
return visited;
}

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformers/jsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/transformers/module/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -207,7 +207,7 @@ namespace ts {
)
);

addEmitHelpers(updated, context.readEmitHelpers());
addEmitHelperRequests(updated, context.readEmitHelperRequests());
return updated;
}

Expand Down Expand Up @@ -347,7 +347,7 @@ namespace ts {
)
);

addEmitHelpers(updated, context.readEmitHelpers());
addEmitHelperRequests(updated, context.readEmitHelperRequests());
return updated;
}

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformers/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 9 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3297,7 +3297,6 @@ namespace ts {
| FlowStart
| FlowLabel
| FlowAssignment
| FlowCall
| FlowCondition
| FlowSwitchClause
| FlowArrayMutation
Expand Down Expand Up @@ -6539,7 +6538,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
}

Expand Down Expand Up @@ -6586,6 +6585,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 */
Expand Down Expand Up @@ -7510,6 +7515,8 @@ namespace ts {
/*@internal*/ getEmitHost(): EmitHost;
/*@internal*/ getEmitHelperFactory(): EmitHelperFactory;

/*@internal*/ readEmitHelperRequests(): EmitHelperRequest[] | undefined;

/** Records a request for a non-scoped emit helper in the current context. */
requestEmitHelper(helper: EmitHelper): void;

Expand Down
Loading