From 04cd599af9546789afa645bdcb4a6b467245544f Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 20 Apr 2023 22:37:55 +0200 Subject: [PATCH] feat: new cases --- src/ast/nodes/ImportExpression.ts | 76 +++++++++++++------ .../_config.js | 2 +- .../_expected.js | 3 + .../treeshake-static-dynamic-import/main.js | 5 +- 4 files changed, 61 insertions(+), 25 deletions(-) diff --git a/src/ast/nodes/ImportExpression.ts b/src/ast/nodes/ImportExpression.ts index 4c401a84aec..941b8c457b1 100644 --- a/src/ast/nodes/ImportExpression.ts +++ b/src/ast/nodes/ImportExpression.ts @@ -12,7 +12,10 @@ import { findFirstOccurrenceOutsideComment, type RenderOptions } from '../../uti import type { InclusionContext } from '../ExecutionContext'; import type ChildScope from '../scopes/ChildScope'; import type NamespaceVariable from '../variables/NamespaceVariable'; +import type ArrowFunctionExpression from './ArrowFunctionExpression'; import type AwaitExpression from './AwaitExpression'; +import type CallExpression from './CallExpression'; +import type FunctionDeclaration from './FunctionDeclaration'; import type Identifier from './Identifier'; import type MemberExpression from './MemberExpression'; import type * as NodeType from './NodeType'; @@ -55,41 +58,55 @@ export default class ImportExpression extends NodeBase { * * - `const { foo } = await import('bar')`. * - `(await import('bar')).foo` + * - `import('bar').then(({ foo }) => {})` * * Returns undefined if it's not deterministic. */ getDeterministicImportedNames(): string[] | undefined { - if (this.parent?.type !== 'AwaitExpression') return; + if (this.parent?.type === 'AwaitExpression') { + const awaitExpression = this.parent as AwaitExpression; - const awaitExpression = this.parent as AwaitExpression; + // Case 1: const { foo } = await import('bar') + if (awaitExpression.parent?.type === 'VariableDeclarator') { + const variableDeclarator = awaitExpression.parent as VariableDeclarator; + if (variableDeclarator.id?.type !== 'ObjectPattern') return; - // Case 1: const { foo } = await import('bar') - if (awaitExpression.parent?.type === 'VariableDeclarator') { - const variableDeclarator = awaitExpression.parent as VariableDeclarator; - if (variableDeclarator.id?.type !== 'ObjectPattern') return; - - const objectPattern = variableDeclarator.id as ObjectPattern; + return getDeterministicObjectDestructure(variableDeclarator.id as ObjectPattern); + } + // Case 2: (await import('bar')).foo + else { + if (awaitExpression.parent?.type !== 'MemberExpression') return; - const variables: string[] = []; + const memberExpression = awaitExpression.parent as MemberExpression; + if (memberExpression.computed || memberExpression.property.type !== 'Identifier') return; - for (const property of objectPattern.properties) { - if (property.type === 'RestElement') return; - if (property.computed) return; - if (property.key.type !== 'Identifier') return; - variables.push((property.key as Identifier).name); + return [(memberExpression.property as Identifier).name]; } - - return variables; } - // Case 2: (await import('bar')).foo - else { - if (awaitExpression.parent?.type !== 'MemberExpression') return; + // Case 3: import('bar').then(({ foo }) => {}) + else if (this.parent?.type === 'MemberExpression') { + const memberExpression = this.parent as MemberExpression; + if ( + memberExpression.property.type !== 'Identifier' || + (memberExpression.property as Identifier).name !== 'then' || + memberExpression.parent?.type !== 'CallExpression' + ) + return; + + const callExpression = memberExpression.parent as CallExpression; + + if ( + callExpression.arguments.length !== 1 || + !['ArrowFunctionExpression', 'FunctionDeclaration'].includes( + callExpression.arguments[0].type + ) + ) + return; - const memberExpression = awaitExpression.parent as MemberExpression; - if (memberExpression.computed) return; - if (memberExpression.property.type !== 'Identifier') return; + const callback = callExpression.arguments[0] as ArrowFunctionExpression | FunctionDeclaration; + if (callback.params.length !== 1 || callback.params[0].type !== 'ObjectPattern') return; - return [(memberExpression.property as Identifier).name]; + return getDeterministicObjectDestructure(callback.params[0] as ObjectPattern); } } @@ -339,6 +356,19 @@ function getInteropHelper( : null; } +function getDeterministicObjectDestructure(objectPattern: ObjectPattern): string[] | undefined { + const variables: string[] = []; + + for (const property of objectPattern.properties) { + if (property.type === 'RestElement') return; + if (property.computed) return; + if (property.key.type !== 'Identifier') return; + variables.push((property.key as Identifier).name); + } + + return variables; +} + const accessedImportGlobals: Record = { amd: ['require'], cjs: ['require'], diff --git a/test/form/samples/treeshake-static-dynamic-import/_config.js b/test/form/samples/treeshake-static-dynamic-import/_config.js index d8fa4163017..762f200f152 100644 --- a/test/form/samples/treeshake-static-dynamic-import/_config.js +++ b/test/form/samples/treeshake-static-dynamic-import/_config.js @@ -1,5 +1,5 @@ module.exports = { - description: 'treeshakes dynamic imports when the target is statically known', + description: 'treeshakes dynamic imports when the target is deterministic', options: { output: { inlineDynamicImports: true diff --git a/test/form/samples/treeshake-static-dynamic-import/_expected.js b/test/form/samples/treeshake-static-dynamic-import/_expected.js index 292b6b35a2c..0548ff86958 100644 --- a/test/form/samples/treeshake-static-dynamic-import/_expected.js +++ b/test/form/samples/treeshake-static-dynamic-import/_expected.js @@ -4,6 +4,9 @@ async function entry() { (await Promise.resolve().then(function () { return sub2; })).bar3(); await Promise.resolve().then(function () { return sub2; }); await Promise.resolve().then(function () { return sub2; }); + Promise.resolve().then(function () { return sub2; }).then(({ baz3 }) => { + console.log(baz3); + }); // bail out await Promise.resolve().then(function () { return bail1$1; }); diff --git a/test/form/samples/treeshake-static-dynamic-import/main.js b/test/form/samples/treeshake-static-dynamic-import/main.js index fb19970135e..12774cc0c69 100644 --- a/test/form/samples/treeshake-static-dynamic-import/main.js +++ b/test/form/samples/treeshake-static-dynamic-import/main.js @@ -4,8 +4,11 @@ export async function entry() { // multiple ;(await import('./sub2.js')).bar3() - const { foo3, baz3 } = await import('./sub2.js'); + const { foo3 } = await import('./sub2.js'); const { foo4 } = await import('./sub2.js'); + import('./sub2.js').then(({ baz3 }) => { + console.log(baz3); + }) // bail out const { bail1 } = await import('./bail1.js');