Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

RFC: Lazy make for reexports in side effects free barrel file#11273

ahabhgk started this conversation inRFC
Discussion options

ahabhgk
Aug 4, 2025
Collaborator

Summary

Lazily build reexports in side-effect-free barrel files to minimize performance overhead from excessive module inclusion, avoiding unnecessary resolve and build processes for these modules.

Motivation

Optimize Rspack's build performance when processing barrel files, with the main benefits seen in project scenarios that import libraries, improving Rspack's overall build performance.

For caseimport { Button } from "antd"; Button; (release build of rspack and added resolving/building log when module starts resolve/build):
before: compiled in 528ms, 12689 lines of resolving/building logs
after: compiled in 82ms, 1003 lines of resolving/building logs

Prior Arts

Glossary

  • barrel file: a file that imports modules from other files and exports the said modules

    export{valueasA}from"./A";export{defaultasB}from"./B";export*asCfrom"./C";export*from"./D";module.exports.E=require("./E").value;module.exports.F=require("./F");_export_star(require("./a"),exports);// _export_star is from@swc/helpers

User Guide

  • Controlled byexperiments.lazyBarrel during experimental phase.
  • Will be enabled by default when sideEffects is turned on after stable, this feature depends onfactory_meta.side_effects_free ("sideEffects": false in package.json or configured bymodule.rule.sideEffects) and thus requires sideEffects configuration.
constrspackConfig={// ...module:{rules:[{test:/specific-lib/,sideEffects:true,// add `factory_meta.side_effects_free = true` for specific module}],},optimization:{sideEffects:"flag"// or true},experiments:{lazyBarrel:true,}}

Detailed Design

image
  1. When importing specifiers from barrel file modules, the imported specifiers are collected asforwarded_ids and passed to the barrel file module.
  2. When collecting dependencies during barrel file parsing, if the barrel file module'sfactory_meta.side_effects_free is true, all re-export dependencies will be marked as lazy.
  3. After the barrel file is built, the used re-export dependencies will be determined based on theforwarded_ids passed in. The used re-export dependencies will have their lazy mark removed and be added to the process dependencies queue for building, while unused re-export dependencies will remain lazy and won't be built (no resolve, no build, and no loaders...).
    1. To maintain consistency with the dependency relationships when this feature is disabled, all dependencies (whether lazy or not) will be stored in the barrel file's correspondingmgm.all_dependencies. The data storage remains completely identical - the only difference being some dependencies are marked as lazy.
    2. These lazy-marked dependencies will also be marked as weak (weak-marked dependencies will be skipped during rendering if there are no strong (non-weak) references, ensuring these modules won't appear in the final output). This guarantees correctly rendering (avoiding issues like panics when failing to get modules from dependencies).
    3. The process of "The used re-export dependencies will have their lazy mark removed and be added to the process dependencies queue for building" will be abstracted into a new Task:ProcessUnlazyDependenciesTask.
    4. The barrel file's export dependencies need to be stored as anartifact to ensure correctness during incremental builds and hot builds with persistent cache enabled.
image
  1. When another module imports the barrel file again, the barrel file will be revisited. During the revisit, the previously collected re-export dependencies from the barrel file build will be matched against theforwarded_ids. Those that matched will be considered as used, have their lazy mark removed, and be added to the process dependencies queue for building.
image
  1. forwarded_ids will first attempt to match against known named exports (export { x as A } from "./a";,export * as A from "./a",export { a, b },export const A,export default A). When no match is found, it indicates the specifier comes from star reexports (export * from "./a"), at which point the remaining star reexports will have their lazy marks removed and be added to the process dependencies queue for building.
image
  1. Re-exports can still continue to passforwarded_ids downstream for subsequent barrel files to use.export { x as A } from "./a" will passforwarded_ids: x downstream, and the subsequent barrel file will only build the re-export of x while keeping other re-export dependencies lazy.export * from "./a" andexport * as A from "./a" will passforwarded_ids: * downstream, requiring the subsequent barrel file to build all re-export dependencies.

Drawbacks

  • Modules marked as lazy will skip processes like factorize, resolve, build, and loader execution
    • These modules won't trigger related hooks or loaders, meaning side effects in plugins and loaders won't execute (given the differences between Rspack's incremental make and webpack's make, there have been no reported issues caused by unexecuted side effects in plugins and loaders)
    • Related errors won't be reported either, such as: resolve errors caused by missing modules, loader errors thrown during loader execution, etc.

FAQ

  1. Can this also supports CommonJS?

    Yes, theoretically this can also support CommonJS, but before that we need to improve Rspack's CJS tree shaking first, the static analyze part specifically, so currently we will only support ESM, and support CJS in the future PR.

  2. Why depend on"sideEffects": false in package.json, can Rspack automatically analyze a module is side effects free or not?

    Rspack has the ability to analyze a module is side effects free or not of course, it's already used byoptimization.sideEffects to tree shaking side effects free modules. But these analyzed modules still require checking whether their dependencies have side effects - only when all dependencies are also side effects free can the module be considered truly side effects free. However, during the make phase, dependencies must first be built before their side effects can be analyzed, and thelazyBarrel is intend to avoid to build those dependencies."sideEffects": false in package.json ormodule.rule.sideEffects doesn't require dependency checking, as it signifies the entire package is side effects free. Therefore,lazyBarrel can only rely on this marker rather than automatic analysis.

  3. Soexport * from "./x" is not a problem any more?

    No,export * from "./x" is still a bad practice for your build performance,lazyBarrel won't build the star re-export only when you import a specifier, and the side effects free barrel file's named exports contains the specifier (export { x as A } from "./a";,export * as A from "./a",export { a, b },export const A,export default A (the name is default)), which means the star re-export is not used. So Rspack will still build the star re-export if:

    1. barrel file is not side effects free
    2. the star re-export is used
You must be logged in to vote

Replies: 2 comments 1 reply

Comment options

ahabhgk
Aug 4, 2025
Collaborator Author

Implementation PR:#11117

You must be logged in to vote
0 replies
Comment options

Hi, just wondering — would this kind of change still cause a rebuild of all files in util.ts

// util.tsexport*from'./date'export*from'./number'export*from'./price'
// re-export.tsexport*from'./util'
// main.tsimport{foo}from're-export'
You must be logged in to vote
1 reply
@ahabhgk
Comment options

ahabhgkAug 27, 2025
Collaborator Author

Unfortunately yes, in this case Rspack can't locate thefoo export inutil.ts, so it has to build./date,./number, and./price to locate thefoo export.

As long as Rspack can locate thefoo export in./util.ts, there's no need to build./util.ts's dependencies:

For example in this case Rspack will only build./date, and won't build./number, and./price.

//util.tsexport{foo}from'./date'export*from'./number'export*from'./price'

Or in this case Rspack even won't build./util and its dependencies, unless there is anotherbar exported from./util and imported by./main.ts.

// re-exports.tsexportconstfoo=1export*from'./util'
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Category
RFC
Labels
None yet
2 participants
@ahabhgk@rikisamurai

[8]ページ先頭

©2009-2025 Movatter.jp