Skip to content

[experiment] implement tc39 Defer Module Evaluation #16567

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Jack-Works
Copy link
Contributor

@Jack-Works Jack-Works commented Dec 14, 2022

Does this PR introduce a breaking change?

no?

What needs to be documented once your changes are merged?

Defer import

This implements the semantics of the TC39 proposal Deferring Module Evaluation.

New options:

experiments.deferImport: true

To use the native syntax, install acorn-import-defer as a peer dependency.

// index.mjs
import defer * as ns from './mod.mjs'

function some_work() {
    console.log('deferred import value', ns.f)
}
console.log('entry runs')
setTimeout(some_work, 1000)

// mod.mjs
export function f() {
}
console.log('init cost!')

Result:

entry runs
// 1000ms later
init cost!
deferred import value, function f() {}

Other things to notice

Alternative syntax

import defer * as ns from './mod.mjs'

can be written as

import * as ns from /* webpackDefer: true */ './mod.mjs'

I suggest putting the magic comment after the from keyword, which is compatible with TypeScript, Babel, SWC, and Flow if the necessary option to preserve comments is enabled.

Please do not put it after the "import" keyword (like import /* webpackDefer: true */ * as ns from './mod.mjs'), the filesystem cache is broken, and I don't know why.

ESBuild is not compatible, please read evanw/esbuild#1439 and evanw/esbuild#309. Esbuild may support this feature ad-hoc like other webpack magic comments.

@alexander-akait
Copy link
Member

Yeah, we can implement this under the experimental flag, feel free to ping when it's ready

@webpack-bot
Copy link
Contributor

For maintainers only:

  • This needs to be documented (issue in webpack/webpack.js.org will be filed when merged)
  • This needs to be backported to webpack 4 (issue will be created when merged)

@Jack-Works Jack-Works force-pushed the lazy-import branch 2 times, most recently from 7420ab0 to 32408f7 Compare December 16, 2022 14:21
@Jack-Works

This comment was marked as resolved.

@alexander-akait
Copy link
Member

Just update snaphots (-u):

FAIL test/StatsTestCases.basictest.js (11.925 s, 87 MB heap size)
  ● Console

@alexander-akait
Copy link
Member

Test suite failed to run

Unable to find JEST_STATE_SYMBOL in Symbol(jest-native-promise), Symbol(jest-native-now), Symbol(jest-native-read-file), Symbol(jest-native-write-file), Symbol(jest-native-exists-file), Symbol($$jest-matchers-object), Symbol(graceful-fs.queue)

Is it locally?

@Jack-Works

This comment was marked as resolved.

@alexander-akait
Copy link
Member

@Jack-Works Sounds like a bug in jest... do you use the same steps as in our CI file? Based on CI report you just need to update snapshot, no more

@alexander-akait
Copy link
Member

Can you update StatsTestCases using -u flag for jest, thank you

@Jack-Works

This comment was marked as resolved.

@alexander-akait
Copy link
Member

alexander-akait commented Dec 20, 2022

@Jack-Works What is node version? You can try to change, also just check you have the same versions as in the lock file

@Jack-Works

This comment was marked as resolved.

@Jack-Works

This comment was marked as resolved.

@alexander-akait
Copy link
Member

Looks like 18.0.0 is the first breaking Node version. I don't know what happened exactly.

Weird, I don't have problem with jest locally... Okay, we can try to update jest (maybe new versions resolve this problem)

Also can you use Node@16 to update snapshots?

@Jack-Works

This comment was marked as resolved.

@Jack-Works
Copy link
Contributor Author

I tried to add some test cases (6041d87) but it seems like does not work... Did I do right?

@alexander-akait
Copy link
Member

That will be a little pain, I need to adjust my setup (installing nvm-for-windows seems to require uninstalling all npm globals and the current node install), I'll use this as the last solution joy

You can use virtual machine here 😄

@Jack-Works

This comment was marked as resolved.

@Jack-Works

This comment was marked as resolved.

@Jack-Works Jack-Works force-pushed the lazy-import branch 2 times, most recently from 9161b0f to 5250fd6 Compare March 23, 2023 11:09
@Jack-Works
Copy link
Contributor Author

I have updated the doc, added the following content:

I suggest putting the magic comment after the from keyword, which is compatible with TypeScript, Babel, SWC, and Flow if the necessary option to preserve comments is enabled.

Please do not put it after the "import" keyword (like import /* webpackDefer: true */ * as ns from './mod.mjs'), the filesystem cache is broken, and I don't know why.

ESBuild is not compatible, please read evanw/esbuild#1439 and evanw/esbuild#309. Esbuild may support this feature ad-hoc like other webpack magic comments.

@Jack-Works Jack-Works force-pushed the lazy-import branch 2 times, most recently from 46a4bd2 to 5ceff95 Compare April 2, 2025 19:18
@Jack-Works Jack-Works marked this pull request as draft April 2, 2025 19:27
@Jack-Works

This comment was marked as resolved.

@Jack-Works Jack-Works force-pushed the lazy-import branch 3 times, most recently from 25760b8 to d69c016 Compare April 3, 2025 07:12
@Jack-Works Jack-Works marked this pull request as ready for review April 3, 2025 07:27
@Jack-Works
Copy link
Contributor Author

hi @alexander-akait @nicolo-ribaudo this is ready for review

@alexander-akait
Copy link
Member

/cc @nicolo-ribaudo Can you take a look, you have more experience with this, an extra look never hurts, thank you

if (exportName.length > 0 && exportName[0] === "default") {
if (defer && exportsType !== "namespace") {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this for the removed export defer * case, or is it for import defer * as ns; export { ns }?

Copy link
Contributor Author

@Jack-Works Jack-Works Apr 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can recall, it is use when you import (not export) a CommonJS module (exportsType !== namespace)

runtime,
runtimeTemplate,
runtimeRequirements
) {
const importedModule = /** @type {Module} */ (moduleGraph.getModule(dep));
const importVar = dep.getImportVar(moduleGraph);

if (
(mode.type === "reexport-namespace-object" ||

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question for this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is for import defer * as ns from '...'; export { ns }.

@Jack-Works Jack-Works force-pushed the lazy-import branch 2 times, most recently from bd56317 to 721d60e Compare April 8, 2025 10:43
Copy link

@nicolo-ribaudo nicolo-ribaudo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went through this one more time, and it looks good. Just a few questions/highlights that maybe should be answered by @alexander-akait:

  • I see that acorn-import-defer is an optional peer dependency. When Webpack implemented import attributes, acorn-import-assertions was added as a normal dependency, so that the syntax would be available to all users. Should we do something similar here?
  • Should we support the comment, even now that we support the syntax? Prettier already supports this syntax, and it will hopefully be included in the next minor release of TypeScript.
  • Does webpack have a way to run test262 tests? There are a bunch of them for import defer at https://github.com/tc39/test262/tree/main/test/language/import/import-defer

@alexander-akait
Copy link
Member

@nicolo-ribaudo

I see that acorn-import-defer is an optional peer dependency. When Webpack implemented import attributes, acorn-import-assertions was added as a normal dependency, so that the syntax would be available to all users. Should we do something similar here?

Yes we need to move it to dependencies

Should we support the comment, even now that we support the syntax? Prettier already supports this syntax, and it will hopefully be included in the next minor release of TypeScript.

I think it's a question of syntax, if we are sure that it won't change we can avoid comment syntax (hope we will not repeat assert/with history - import attributes, when it was renamed after long discussions), on the other hand some tools may take more time to implement this, for example es;int, which will make problems...

Anyway it is experiment, so we can introduce comment syntax later

Does webpack have a way to run test262 tests? There are a bunch of them for import defer at https://github.com/tc39/test262/tree/main/test/language/import/import-defer

Good question, we have some parts of tests, but not all, I'll mark this as a task for myself

@alexander-akait
Copy link
Member

@Jack-Works Thank you for this great work, can you make rebase, I think we will merge it for the next minor release

@alexander-akait alexander-akait moved this from On Hold to Ready for Merge in webpack 5/6 Apr 23, 2025
@Jack-Works Jack-Works force-pushed the lazy-import branch 3 times, most recently from bc3f543 to a707099 Compare April 23, 2025 15:41
Co-authored-by: Nicolo Ribaudo <hello@nicr.dev>
@alexander-akait
Copy link
Member

@Jack-Works

Please do not put it after the "import" keyword (like import /* webpackDefer: true */ * as ns from './mod.mjs'), the filesystem cache is broken, and I don't know why.

Can you provide feedback on this? Maybe we miss something to serialize? What error do you have?

@Jack-Works
Copy link
Contributor Author

@Jack-Works

Please do not put it after the "import" keyword (like import /* webpackDefer: true */ * as ns from './mod.mjs'), the filesystem cache is broken, and I don't know why.

Can you provide feedback on this? Maybe we miss something to serialize? What error do you have?

If the comment is put after import, the cache created from .a is not hit in .b ("Pack got invalid because of write to ..."). As I can recall, the hash of the module is changed.

@Jack-Works
Copy link
Contributor Author

Not sure why Github Actions / integration (ubuntu-latest, 10.x, b) (pull_request) and Github Actions / integration (windows-latest, 10.x, b) (pull_request) fails with Error: Cannot find module 'acorn-import-defer'. Maybe it is related to CI cache (I changed it from devDep to dep), or is it ok to inline it cc @nicolo-ribaudo? Import attributes is currently inlined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Ready for Merge
Development

Successfully merging this pull request may close these issues.

6 participants