diff --git a/src/ast/nodes/ImportExpression.ts b/src/ast/nodes/ImportExpression.ts index 785d6b679f4..fd726f437c4 100644 --- a/src/ast/nodes/ImportExpression.ts +++ b/src/ast/nodes/ImportExpression.ts @@ -42,6 +42,7 @@ export default class ImportExpression extends NodeBase { declare type: NodeType.tImportExpression; declare sourceAstNode: AstNode; + private accessedPropKey = new Set(); private attributes: string | null | true = null; private mechanism: DynamicImportMechanism | null = null; private namespaceExportName: string | false | undefined = undefined; @@ -82,9 +83,13 @@ export default class ImportExpression extends NodeBase { // Case 1: const { foo } = await import('bar') if (parent2 instanceof VariableDeclarator) { const declaration = parent2.id; - return declaration instanceof ObjectPattern - ? getDeterministicObjectDestructure(declaration) - : undefined; + if (declaration instanceof Identifier) { + return this.accessedPropKey.size > 0 ? [...this.accessedPropKey] : undefined; + } + if (declaration instanceof ObjectPattern) { + return getDeterministicObjectDestructure(declaration); + } + return undefined; } // Case 2: (await import('bar')).foo @@ -147,6 +152,10 @@ export default class ImportExpression extends NodeBase { } } + addAccessedPropKey(key: string): void { + this.accessedPropKey.add(key); + } + hasEffects(): boolean { return true; } diff --git a/src/ast/nodes/MemberExpression.ts b/src/ast/nodes/MemberExpression.ts index 5c32a896b3d..330bae39545 100644 --- a/src/ast/nodes/MemberExpression.ts +++ b/src/ast/nodes/MemberExpression.ts @@ -27,9 +27,12 @@ import { } from '../utils/PathTracker'; import { UNDEFINED_EXPRESSION } from '../values'; import ExternalVariable from '../variables/ExternalVariable'; +import LocalVariable from '../variables/LocalVariable'; import type NamespaceVariable from '../variables/NamespaceVariable'; import type Variable from '../variables/Variable'; +import AwaitExpression from './AwaitExpression'; import Identifier from './Identifier'; +import type ImportExpression from './ImportExpression'; import Literal from './Literal'; import type * as NodeType from './NodeType'; import type PrivateIdentifier from './PrivateIdentifier'; @@ -46,6 +49,10 @@ import { import type { ChainElement, ExpressionNode, IncludeChildren } from './shared/Node'; import { NodeBase } from './shared/Node'; +function isImportExpression(node: ExpressionNode): node is ImportExpression { + return node.type === 'ImportExpression'; +} + // To avoid infinite recursions const MAX_PATH_DEPTH = 7; @@ -161,6 +168,16 @@ export default class MemberExpression } else { super.bind(); } + if (baseVariable instanceof LocalVariable) { + const init = baseVariable.init; + if ( + init instanceof AwaitExpression && + isImportExpression(init.argument) && + typeof this.propertyKey === 'string' + ) { + init.argument.addAccessedPropKey(this.propertyKey); + } + } } deoptimizeArgumentsOnInteractionAtPath( diff --git a/src/ast/variables/LocalVariable.ts b/src/ast/variables/LocalVariable.ts index fe5bd488492..c5dbf2991a5 100644 --- a/src/ast/variables/LocalVariable.ts +++ b/src/ast/variables/LocalVariable.ts @@ -28,6 +28,9 @@ import Variable from './Variable'; export default class LocalVariable extends Variable { calledFromTryStatement = false; + + init: ExpressionEntity; + readonly declarations: (Identifier | ExportDefaultDeclaration)[]; readonly module: Module; readonly kind: VariableKind; @@ -41,13 +44,14 @@ export default class LocalVariable extends Variable { constructor( name: string, declarator: Identifier | ExportDefaultDeclaration | null, - private init: ExpressionEntity, + init: ExpressionEntity, context: AstContext, kind: VariableKind ) { super(name); this.declarations = declarator ? [declarator] : []; this.deoptimizationTracker = context.deoptimizationTracker; + this.init = init; this.module = context.module; this.kind = kind; } diff --git a/test/chunking-form/samples/dynamic-import-identifier/_config.js b/test/chunking-form/samples/dynamic-import-identifier/_config.js new file mode 100644 index 00000000000..7ef5235a00c --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-identifier/_config.js @@ -0,0 +1,3 @@ +module.exports = defineTest({ + description: 'tree-shakes dynamic imports with identifier' +}); diff --git a/test/chunking-form/samples/dynamic-import-identifier/_expected/amd/generated-module.js b/test/chunking-form/samples/dynamic-import-identifier/_expected/amd/generated-module.js new file mode 100644 index 00000000000..3123adef9b9 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-identifier/_expected/amd/generated-module.js @@ -0,0 +1,7 @@ +define(['exports'], (function (exports) { 'use strict'; + + const foo = () => {}; + + exports.foo = foo; + +})); diff --git a/test/chunking-form/samples/dynamic-import-identifier/_expected/amd/main.js b/test/chunking-form/samples/dynamic-import-identifier/_expected/amd/main.js new file mode 100644 index 00000000000..f8410dc1a82 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-identifier/_expected/amd/main.js @@ -0,0 +1,8 @@ +define(['require'], (function (require) { 'use strict'; + + (async () => { + const module = await new Promise(function (resolve, reject) { require(['./generated-module'], resolve, reject); }); + module.foo(); + })(); + +})); diff --git a/test/chunking-form/samples/dynamic-import-identifier/_expected/cjs/generated-module.js b/test/chunking-form/samples/dynamic-import-identifier/_expected/cjs/generated-module.js new file mode 100644 index 00000000000..02ce8a98f32 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-identifier/_expected/cjs/generated-module.js @@ -0,0 +1,5 @@ +'use strict'; + +const foo = () => {}; + +exports.foo = foo; diff --git a/test/chunking-form/samples/dynamic-import-identifier/_expected/cjs/main.js b/test/chunking-form/samples/dynamic-import-identifier/_expected/cjs/main.js new file mode 100644 index 00000000000..f79ab7fe7f0 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-identifier/_expected/cjs/main.js @@ -0,0 +1,6 @@ +'use strict'; + +(async () => { + const module = await Promise.resolve().then(function () { return require('./generated-module.js'); }); + module.foo(); +})(); diff --git a/test/chunking-form/samples/dynamic-import-identifier/_expected/es/generated-module.js b/test/chunking-form/samples/dynamic-import-identifier/_expected/es/generated-module.js new file mode 100644 index 00000000000..4ac31b619b3 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-identifier/_expected/es/generated-module.js @@ -0,0 +1,3 @@ +const foo = () => {}; + +export { foo }; diff --git a/test/chunking-form/samples/dynamic-import-identifier/_expected/es/main.js b/test/chunking-form/samples/dynamic-import-identifier/_expected/es/main.js new file mode 100644 index 00000000000..a48883052f1 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-identifier/_expected/es/main.js @@ -0,0 +1,4 @@ +(async () => { + const module = await import('./generated-module.js'); + module.foo(); +})(); diff --git a/test/chunking-form/samples/dynamic-import-identifier/_expected/system/generated-module.js b/test/chunking-form/samples/dynamic-import-identifier/_expected/system/generated-module.js new file mode 100644 index 00000000000..72f26099c8c --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-identifier/_expected/system/generated-module.js @@ -0,0 +1,10 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + const foo = exports("foo", () => {}); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-identifier/_expected/system/main.js b/test/chunking-form/samples/dynamic-import-identifier/_expected/system/main.js new file mode 100644 index 00000000000..a3a97190b45 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-identifier/_expected/system/main.js @@ -0,0 +1,13 @@ +System.register([], (function (exports, module) { + 'use strict'; + return { + execute: (function () { + + (async () => { + const module$1 = await module.import('./generated-module.js'); + module$1.foo(); + })(); + + }) + }; +})); diff --git a/test/chunking-form/samples/dynamic-import-identifier/main.js b/test/chunking-form/samples/dynamic-import-identifier/main.js new file mode 100644 index 00000000000..5f4f8982c41 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-identifier/main.js @@ -0,0 +1,4 @@ +(async () => { + const module = await import('./module'); + module.foo(); +})(); diff --git a/test/chunking-form/samples/dynamic-import-identifier/module.js b/test/chunking-form/samples/dynamic-import-identifier/module.js new file mode 100644 index 00000000000..8beef0c8742 --- /dev/null +++ b/test/chunking-form/samples/dynamic-import-identifier/module.js @@ -0,0 +1,2 @@ +export const foo = () => {}; +export const bar = () => {};