Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@TrickyPi
Copy link
Member

This PR contains:

  • bugfix
  • feature
  • refactor
  • documentation
  • other

Are tests included?

  • yes (bugfixes and features will not be merged without tests)
  • no

Breaking Changes?

  • yes (breaking changes will not be merged unless absolutely necessary)
  • no

List any relevant issue numbers:
relevant: #6073

Description

Technically, an await expression should always have side effects, since it delays the execution of the following code. For example:

async function a() {
  await 1;
  console.log('a');
}
async function b() {
  console.log('b');
}
a();
b();

Here, 'b' is logged before 'a'.
But if the argument to await is an import() expression, I think the await expression could be safely removed. In that case, the execution timing of the following code is not deterministic anyway, since it is affected by file I/O or network requests.

@vercel
Copy link

vercel bot commented Sep 21, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
rollup Ready Ready Preview, Comment Dec 29, 2025 6:51am

@github-actions
Copy link

github-actions bot commented Sep 21, 2025

Thank you for your contribution! ❤️

You can try out this pull request locally by installing Rollup via

npm install rollup/rollup#feat/enhance-tree-shaking-for-dynamic-imports

Notice: Ensure you have installed the latest nightly Rust toolchain. If you haven't installed it yet, please see https://www.rust-lang.org/tools/install to learn how to download Rustup and install Rust.

or load it into the REPL:
https://rollup-jgqfhnv6e-rollup-js.vercel.app/repl/?pr=6120

@github-actions
Copy link

github-actions bot commented Sep 21, 2025

Performance report

  • BUILD: 6883ms, 844 MB
    • initialize: 0ms, 24.2 MB
    • generate module graph: 2605ms, 631 MB
      • generate ast: 1386ms, 623 MB
    • sort and bind modules: 402ms, 687 MB
    • mark included statements: 3885ms, 844 MB
      • treeshaking pass 1: 2277ms, 836 MB
      • treeshaking pass 2: 457ms, 816 MB
      • treeshaking pass 3: 387ms, 842 MB
      • treeshaking pass 4: 376ms, 819 MB
      • treeshaking pass 5: 373ms, 844 MB
  • GENERATE: 677ms, 915 MB
    • initialize render: 0ms, 844 MB
    • generate chunks: 46ms, 821 MB
      • optimize chunks: 0ms, 821 MB
    • render chunks: 615ms, 888 MB
    • transform chunks: 15ms, 915 MB
    • generate bundle: 0ms, 915 MB

@codecov
Copy link

codecov bot commented Sep 21, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.74%. Comparing base (fe42b2d) to head (ddbdfef).
⚠️ Report is 17 commits behind head on master.

⚠️ Current head ddbdfef differs from pull request most recent head f73c9df

Please upload reports for the commit f73c9df to get more accurate results.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6120      +/-   ##
==========================================
- Coverage   98.81%   98.74%   -0.07%     
==========================================
  Files         272      271       -1     
  Lines       10684    10652      -32     
  Branches     2862     2853       -9     
==========================================
- Hits        10557    10518      -39     
- Misses         85       89       +4     
- Partials       42       45       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@lukastaegert
Copy link
Member

lukastaegert commented Sep 21, 2025

In that case, the execution timing of the following code is not deterministic anyway

You are mostly right, but it will never be synchronous, and some logic may rely on the fact that it is async. As this is hard to detect, I would suggest instead to replace it with something compact like an await 0. Which in general might be a good simplification for await without other side effects.

@TrickyPi
Copy link
Member Author

Oh, I misunderstood before. I get it now—if an async function doesn’t have an await, its execution behaves like synchronous code. Thanks for reminding me!

@TrickyPi
Copy link
Member Author

TrickyPi commented Sep 27, 2025

After thinking it through, it seems difficult to replace an awaiting import expression with 0. First, the AwaitExpression’s hasEffects function has to return true, otherwise, the render function won't run, and the replacement logic inside it won't be executed. But once we do that, the inclusion of await expressions will always be triggered, and we can’t stop this inclusion from propagating to its argument.

I originally thought shouldBeIncluded could stop the propagation, but it turns out it misses some cases. The CI failure revealed the reason: some of the propagation is triggered by the inclusion of other variables.

Maybe we have to narrow the scope of this PR and only take import expressions into account.

@lukastaegert
Copy link
Member

and we can’t stop this inclusion from propagating to its argument.

That is not true. For instance, a BlockStatement also does not propagate the inclusion to its children, and I would have hoped this can work similarly. It is wrong, though, to have conditional logic in includeNode as it only runs once. Not yet sure what the correct solution would be here.

@lukastaegert lukastaegert force-pushed the master branch 2 times, most recently from 5369863 to 96b5453 Compare November 7, 2025 21:32
@lukastaegert
Copy link
Member

I had another thought about this and I think we should not implement this.
There is another use-case for having a non-awaited or chained dynamic import: To pre-load code earlier so that it is loaded when needed at a later stage.
For that reason, I think we should never remove dynamic imports unless the entire import target is tree-shaken, which is slightly different from the goal of this pull request.

Improving handling of completely tree-shaken modules is still a useful thing, though. Consider the exact scenario where someone uses a bare dynamic import of a module to preload it. With the current logic, this would not include any exports of the target, and if there are not further side effects and there is no other import of that module for whatever reason, we would currently generate something like

Promise.resolve().then(function () { return dep; });

var dep = /*#__PURE__*/Object.freeze({
	__proto__: null
});

and no further chunk. This could just be completely removed. But maybe we should close this PR and address it in another PR if we want.

@TrickyPi
Copy link
Member Author

Thank you so much for still remembering this PR!

There is another use-case for having a non-awaited or chained dynamic import: To pre-load code earlier so that it is loaded when needed at a later stage.

Oh, that’s definitely a reason to remove this feature — I totally overlooked that before.

Improving handling of completely tree-shaken modules is still a useful thing, though. Consider the exact scenario where someone uses a bare dynamic import of a module to preload it. With the current logic, this would not include any exports of the target, and if there are not further side effects and there is no other import of that module for whatever reason, we would currently generate something like

Promise.resolve().then(function () { return dep; });

var dep = /*#__PURE__*/Object.freeze({
	__proto__: null
});

and no further chunk. This could just be completely removed. But maybe we should close this PR and address it in another PR if we want.

Yeah, that definitely removes the module import. Thanks for the work in PR #6230. I haven’t dug into the full implementation yet, but it’s clear that a lot of thought and effort went into it.

@TrickyPi TrickyPi closed this Jan 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants