Skip to content
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

Treeshake dynamic imports based on destructuring #16872

Open
Tracked by #14800
TheLarkInn opened this issue Mar 27, 2023 · 11 comments
Open
Tracked by #14800

Treeshake dynamic imports based on destructuring #16872

TheLarkInn opened this issue Mar 27, 2023 · 11 comments

Comments

@TheLarkInn
Copy link
Member

TheLarkInn commented Mar 27, 2023

Given the following code:

import('lodash-es').then(({uniq}) => uniq)

When webpack bundles this code, it should only include the module tree for uniq and tree-shake the rest of the exports in lodash-es as unused.

@TheLarkInn TheLarkInn changed the title async import like import('a').then(({ b }) => b) Treeshake dynamic imports based on Destructuring Mar 27, 2023
@TheLarkInn TheLarkInn changed the title Treeshake dynamic imports based on Destructuring Treeshake dynamic imports based on destructuring Mar 27, 2023
@TheLarkInn
Copy link
Member Author

Question:

Do we need to support this syntax style also:

const {uniq} = await import('lodash-es');

@vankop
Copy link
Member

vankop commented Mar 27, 2023

with then it is very tricky. we definitely could not support all cases. I thought about async/await support or simply -> only assigning support, like:

import * as ns from 'a';
 const {b} = ns;
 const {c} = await import('c');
 const {d} = process.env; // or any defined value

@TheLarkInn
Copy link
Member Author

What are the scenarios which couldn't be supported with .then()?

@icy0307
Copy link

icy0307 commented Mar 28, 2023

We are facing the same issue too, for a really long time.
What I currently do to bypass this is to add an intermediate file that imports statically and re-export.
like

import { uniq} from 'lodash-es' 
export uniq

I understand that object deconstruction dead code elimination is very difficult, which means performing dead code on object property.
As we wrote const {uniq} = await import('lodash-es');, a module namespace object has already been created.
A single-line analyzation of const {uniq} = await import('lodash-es'); would not be that hard? @vankop
What is tricky is when using babel to compile
A simple

async function main() {
    const {uniq} = await import('lodash-es);
    console.log(uniq);
}
main();

would be transpiled to

import _regeneratorRuntime from "@babel/runtime/helpers/regeneratorRuntime";
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
function main() {
  return _main.apply(this, arguments);
}
function _main() {
  _main = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
    var _yield$import, uniq;
    return _regeneratorRuntime().wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.next = 2;
            return import('lodash-es');
          case 2:
            _yield$import = _context.sent;
            uniq = _yield$import.uniq;
            console.log(uniq);
          case 5:
          case "end":
            return _context.stop();
        }
      }
    }, _callee);
  }));
  return _main.apply(this, arguments);
}
main();

which uses "switch case" based on runtime value, making it impossible to statically analyze.

@icy0307
Copy link

icy0307 commented Mar 28, 2023

BUT, Treeshake dynamic imports based on destructuring are still pretty important for bundlers because of the possible code size it could save.
Maybe we could

  1. Somehow add intermediate files automatically by user's plugin , which creates a virtual module that statically imports and re-exports the used exports and changes this source code before handing it to babel.
  2. Do javascript parsing before any loaders for js, and mark unused export before any loaders.
    @vankop @alexander-akait @sokra
    What is your opinions on this?

@vankop
Copy link
Member

vankop commented Mar 28, 2023

@TheLarkInn because of javascript complexity:

const a = import('a');
a.then(({b}) => {});
a.then(({c}) => {}); // this will work I guess

a.then(v => {
  const {c} = v;
  return v;
}).then(() => {}); // quite difficult to catch all thens here

we could support only this syntax like:

import().then((__destructing_here__) => {});

problem here we introduce some "non-stable" build in terms of tree-shaking. e.g. like in example user will "refactor" destructing to destructing from variable. in case of async/await:

const b = await import('b');
const { a, c } = b;

should work as I remember ( we have something like variable replacement in JavascriptParser )

@alexander-akait
Copy link
Member

What about (await import('./index.js')).meta?

@alexander-akait
Copy link
Member

Note - we have /* webpackExports: ["abc", "default"] */ for non analizable imports

@TheLarkInn
Copy link
Member Author

So there definitely appears to be a variety of scenarios which would be hard to analyze treeshaking.

HOWEVER we can encourage people to achieve treeshaking from dynamic imports by using a particular set of conventions. For now let's just call it "inline import() destructuring":

Using .then()

import("lodash-es").then(({uniq}) => {
  uniq([1,5,6,7,7,4,2]);
});

Using await

const {uniq} = await import("lodash-es");

uniq([1,1,1,34,5,6]);

We can guide people to sticking with "inline import() destructuring". Others can setup lint rules, etc if they want to enforce bundle-size performance-forward patterns.

@webpack-bot
Copy link
Contributor

This issue had no activity for at least three months.

It's subject to automatic issue closing if there is no activity in the next 15 days.

@webpack-bot
Copy link
Contributor

Issue was closed because of inactivity.

If you think this is still a valid issue, please file a new issue with additional information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment