diff --git a/AGENTS.md b/AGENTS.md index 5ad62c3c2ade..8c92bc2cebb4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,6 +36,8 @@ Rspack is a high-performance JavaScript bundler written in Rust that offers stro - Update snapshots: `npm run test -- -u` - Filter tests: `npm run test -- -t configCases/asset` +Depends on what you have modified, you need to rebuild by `pnpm run build:js` or `pnpm run build:binding:dev` or `pnpm run build:cli:dev` first, then run testing commands to verify the modification. + ## Debugging - **VS Code**: `.vscode/launch.json` with `Debug Rspack` and `Attach` options diff --git a/Cargo.lock b/Cargo.lock index 6df527c76b33..875f7c064b48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1307,6 +1307,12 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" +[[package]] +name = "dragonbox_ecma" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a5577f010d4e1bb3f3c4d6081e05718eb6992cf20119cab4d3abadff198b5ae" + [[package]] name = "dtoa" version = "1.0.9" @@ -5639,9 +5645,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "swc" -version = "52.0.0" +version = "53.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d330c7945664f5fe9594443dbaea3abfa275a1d6513d0888d163cc580ddab408" +checksum = "54d8a1ae04e3a6aa68590bdf49f908a2e6d66f2c06d33a0e489de2e0e7a235f8" dependencies = [ "anyhow", "base64", @@ -5748,9 +5754,9 @@ dependencies = [ [[package]] name = "swc_compiler_base" -version = "45.0.0" +version = "46.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05764fac0b452c050eec49135de597223166590b9992ce9096628db06fce5ae3" +checksum = "c5891ef751db4d8257714d78792b0100eedeb8ba229e7be2e90e26074db58920" dependencies = [ "anyhow", "base64", @@ -5807,9 +5813,9 @@ dependencies = [ [[package]] name = "swc_core" -version = "54.0.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f361c932ffe029754a10512ee217f550109ee80dda1fe6ec90772440bfc0a68" +checksum = "b4017f75dd2c60b9840edd096ee7d0b86e250b7604746f00060272161bbf5aa1" dependencies = [ "par-core", "swc", @@ -5836,9 +5842,9 @@ dependencies = [ [[package]] name = "swc_ecma_ast" -version = "19.0.0" +version = "20.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "724195600825cbdd2a899d5473d2ce1f24ae418bff1231f160ecf38a3bc81f46" +checksum = "4682d975a3b4ea33f351e7938db4db41f757d9abe537af4780611ef006531698" dependencies = [ "bitflags 2.9.1", "cbor4ii", @@ -5857,25 +5863,24 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "21.0.0" +version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c77d9d21345ca986ae3b5ff1a4fa3607b15b07ed397506e6dba32e867cf40fd" +checksum = "c5bee5594d86feee801134048e926748a31ad10e7befef507322b056c113e148" dependencies = [ "ascii", "compact_str", + "dragonbox_ecma", "memchr", "num-bigint", "once_cell", "regex", "rustc-hash", - "ryu-js", "serde", "swc_allocator", "swc_atoms", "swc_common", "swc_ecma_ast", "swc_ecma_codegen_macros", - "swc_sourcemap", "tracing", ] @@ -5892,9 +5897,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_bugfixes" -version = "39.0.0" +version = "40.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75920017b78c5231b47250c329930b76031f703ebfb14ead7b466f43374d6828" +checksum = "a4bc37853b9131944aabc14f78870514dbd6dd45bf90bb14df27f435ed78a863" dependencies = [ "rustc-hash", "swc_atoms", @@ -5910,9 +5915,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_common" -version = "30.0.0" +version = "31.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb4c2493374ec4d5c456ebdf5de3168d56993147b3888ec47d837334898eb4b" +checksum = "d5b2d61ac98952d5a1cf1b71b83f424e5a95146d94a6855777d08116818a9061" dependencies = [ "swc_common", "swc_ecma_ast", @@ -5922,9 +5927,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2015" -version = "39.0.0" +version = "40.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e654cd8be79bc1aba940135d600d975dcec9047e8935897db9f53fa8bd94c3e" +checksum = "e8b5620d55a7916f36478231b160c7763513708de1c4f8ffa2befe4be1db4282" dependencies = [ "arrayvec", "indexmap", @@ -5949,9 +5954,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2016" -version = "35.0.0" +version = "36.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ed1b54c134f5c972dabe6d153dab0ec26db0e6ca31ad7b9ec1824a46c595a0" +checksum = "cfa4e8572155e752c98a6155d4b5fab965d6d7ca665889994c7934280c7137f0" dependencies = [ "swc_ecma_ast", "swc_ecma_transformer", @@ -5962,9 +5967,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2017" -version = "35.0.0" +version = "36.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b18078fcaf7bf8f16d41d644383d97879bf6677aacbd97a2c4426b2a6c85dd" +checksum = "5ed2e895823c6863bf08ee50f8bc42c3ea1e11fe12124b1bac518ad123417f1a" dependencies = [ "serde", "swc_common", @@ -5977,9 +5982,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2018" -version = "36.0.0" +version = "37.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25c6661887ef18abf36746dfc4aea1c6bb8f006656a1db11fbcaefd340764492" +checksum = "e41a9161a34e4c763254d7008ab69e50f084d652cc8d18d6284be26bc3a8d1e4" dependencies = [ "serde", "swc_ecma_ast", @@ -5991,9 +5996,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2019" -version = "35.0.0" +version = "36.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6d2040d966a2db719193c13739765bb4026d746ed34767323fbc4ecc241dd4" +checksum = "e3b8a4a95459fa57bbe1e85b3ce494d41db37c2f5b74173da0c03f6a9a9d601d" dependencies = [ "swc_common", "swc_ecma_ast", @@ -6005,9 +6010,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2020" -version = "37.0.0" +version = "38.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368df3e1713a0b2cecf51e1f5d2ed2f99367e21e6dbb3b4c1b7603664bc4854f" +checksum = "668053c35dff7726323974a3c359c59dcdec51b46029fb32de935b2808179b49" dependencies = [ "serde", "swc_common", @@ -6022,9 +6027,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2021" -version = "35.0.0" +version = "36.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "485ea701061eaa7934461dd4874deda80e6b0f254c672c232fc14a4e3b6e6008" +checksum = "3314c63ddc46044214d328f4a820735e1ce0318e3dedfca8c508e7ce2b799c09" dependencies = [ "swc_ecma_ast", "swc_ecma_transformer", @@ -6035,9 +6040,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2022" -version = "37.0.0" +version = "38.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee87fab5c61b2147798343581ca60b5320362b82a0140faa080757efc6498d1e" +checksum = "ef462e62ce35b87c1ab2aff8926549308c5505e6d7ac9acc90af618d05734f42" dependencies = [ "rustc-hash", "swc_atoms", @@ -6055,9 +6060,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es3" -version = "26.0.0" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5107a2c40d6779f7c0f7e5dd7522304b499d2310526450cd9f49c3ce3072281" +checksum = "0c1dd219e6e1b668223ae4e41141c0eeb78d14cfd178b97d6c532b93a27b3fe5" dependencies = [ "swc_common", "swc_ecma_ast", @@ -6069,9 +6074,9 @@ dependencies = [ [[package]] name = "swc_ecma_ext_transforms" -version = "25.0.0" +version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5ec1b4d8740ccf1b56f0910648bdc23b23623514a88ce39a071b79e825080c" +checksum = "2a6aed670f87cde675f40dcd0ff5196f3cec9b2bc7e6fed12adf5b808f18eb69" dependencies = [ "phf", "swc_common", @@ -6082,9 +6087,9 @@ dependencies = [ [[package]] name = "swc_ecma_hooks" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac98742c42ad8bdca8ebeccf6b7a420369dab3a1cb88ef1f017eb1e588014ba8" +checksum = "8c9adff1f01550ef653e262ad709a2914d3dd6cfd559aea2e28eeeab8f895981" dependencies = [ "swc_atoms", "swc_common", @@ -6116,9 +6121,9 @@ dependencies = [ [[package]] name = "swc_ecma_minifier" -version = "42.0.0" +version = "43.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55ced848cae0fa5e33e465540bbeafd70645f4099b8b9b3ca66e66d95f52300" +checksum = "fa6c2bc0cd5a04a2be11accdc58f0497f97bd44943c03d76371729694cf601e3" dependencies = [ "arrayvec", "bitflags 2.9.1", @@ -6152,9 +6157,9 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "32.0.0" +version = "33.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d0c36843109fff178bbedc439b4190daa865d78e553134243a4df220329fdd" +checksum = "3cb866d9ca2df54daa4a460bdd105cd641d780f0578928cb1f7f4e54574bb2bb" dependencies = [ "bitflags 2.9.1", "either", @@ -6172,9 +6177,9 @@ dependencies = [ [[package]] name = "swc_ecma_preset_env" -version = "45.0.0" +version = "46.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4f1f6c815a26faa019a3629890c4ca8b6657b4b3682237fabf948899fa81e91" +checksum = "4415d2eb6277081b91daffedcd4ac9701bc6b03427764b28a7f14da26aaae224" dependencies = [ "anyhow", "foldhash 0.1.5", @@ -6197,9 +6202,9 @@ dependencies = [ [[package]] name = "swc_ecma_quote_macros" -version = "32.0.0" +version = "33.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d70a7f12dd07015e00bf30770497de4ab45db2cf34ca9e870aece9e456166032" +checksum = "b0f033afe55c4bc19e1a305c7d9e7cce50baf7a3df242e3da26948674b26dd9f" dependencies = [ "anyhow", "proc-macro2", @@ -6215,9 +6220,9 @@ dependencies = [ [[package]] name = "swc_ecma_transformer" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e2aaef402a685c66f7cfb983b7b218ca42cd3de15c8aaf912f10ea8cd77402" +checksum = "8f920b9afa7b4354b697fdaa2120da4abfe00dc45a9e5ca1a2a91113012151db" dependencies = [ "rustc-hash", "swc_atoms", @@ -6232,9 +6237,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms" -version = "44.0.0" +version = "45.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3bba9c21bfc599f9a7aae0e2f9107b0b3156ee86bbdb796fd36ef8c265ff8c0" +checksum = "a913f58b3288afbe700aed8110cb98c4fddec471aa60d5a6079caad7b1994b22" dependencies = [ "par-core", "swc_common", @@ -6251,9 +6256,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "35.0.0" +version = "36.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f9f0dee4466e6eeb7042f2a0fc6c84298dfa914baff5bcfb44beb9f24b215f" +checksum = "acdca4cb85a06e5831afb59a3bf6b642aed19b6ea965df3b90ad43d2984e2d46" dependencies = [ "better_scoped_tls", "indexmap", @@ -6274,9 +6279,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_classes" -version = "35.0.0" +version = "36.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc00cb0ee27cb78fc719482a7bf5697b5052b878dbd5ba6b65249c1ddcf8832" +checksum = "3567a1a95c2f6d5f7c3ca59051cf099c5ac54cf8f293bcfb43b385477b9f80a9" dependencies = [ "swc_common", "swc_ecma_ast", @@ -6287,9 +6292,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_compat" -version = "40.0.0" +version = "41.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cbf17a99fdb5d8604a2c5109fa6980f59b9b5827e715358ec24249449e74d7" +checksum = "747972c4b18286ee79206dc70aef0f1a6593b62eb45364eccf7f34734e2148ec" dependencies = [ "indexmap", "par-core", @@ -6328,9 +6333,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_module" -version = "38.0.0" +version = "39.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a884810184e70414a38b7572c4ea6ef66fed036cfdcd9f9804ce83e14b1ad76" +checksum = "6ce560526cbd4e3cbd1c9183d099954ebd2399099ab8d6b9651f83728ef1f622" dependencies = [ "Inflector", "anyhow", @@ -6356,9 +6361,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_optimization" -version = "37.0.0" +version = "38.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bc5e01b1e5c696b4784a133a5ee28ca8b309800d858ab63509069df8fe30181" +checksum = "ab76533e9da38c8f13ca1a9d0a6edd6b3de328a281441aee9810281e1b7ba4e9" dependencies = [ "bytes-str", "dashmap 5.5.3", @@ -6380,9 +6385,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_proposal" -version = "35.0.0" +version = "36.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8f121d69b5ec24359e611be11075734f0fd535b0ce2f8f2887b0cafb0166ef" +checksum = "35a8e84e073ab1675784257ef2f707f5d914724b147144a3617905d2eb81f93c" dependencies = [ "either", "rustc-hash", @@ -6398,9 +6403,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_react" -version = "38.0.0" +version = "39.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "652b77238937631359f964e53d53ca2b92f9fa29c6a28c9d3b054c44e18a17b9" +checksum = "e21590a3c184b98ea3bae1717e58e02c64597c087429a635c6301f41f09ceaaf" dependencies = [ "base64", "bytes-str", @@ -6414,6 +6419,7 @@ dependencies = [ "swc_common", "swc_config", "swc_ecma_ast", + "swc_ecma_hooks", "swc_ecma_parser", "swc_ecma_transforms_base", "swc_ecma_utils", @@ -6422,9 +6428,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_typescript" -version = "38.0.0" +version = "39.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c32bbafb42b8f0f2013e94a4b4981d643a645b1a7f9c20931cbca5bf78c1538" +checksum = "8ce02fbf3b0bd4f69eb1d020973d9a6e7c4bb062d464fade653f6bc8a075f592" dependencies = [ "bytes-str", "rustc-hash", @@ -6440,9 +6446,9 @@ dependencies = [ [[package]] name = "swc_ecma_usage_analyzer" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5215c48b139422dcf54958d746b5e9a7b31bab8fc126f28e4049c4985505d5" +checksum = "b7aec80849cab609d92af5dd2670f18fc760f7029beb11beebcef3fed9ba3466" dependencies = [ "bitflags 2.9.1", "indexmap", @@ -6458,16 +6464,16 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "25.0.0" +version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dd5ee449d21110a271e73d0a9f7640a8854a62cb0e2cb0c9db3445383598e21" +checksum = "58e74834be7fece99481e2f59b91b0c90ae790582ef3190b79e5ec050f4550e4" dependencies = [ + "dragonbox_ecma", "indexmap", "num_cpus", "once_cell", "par-core", "rustc-hash", - "ryu-js", "swc_atoms", "swc_common", "swc_ecma_ast", @@ -6477,9 +6483,9 @@ dependencies = [ [[package]] name = "swc_ecma_visit" -version = "19.0.0" +version = "20.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69d63f7f704a2ec937edef90a3eba1f64602eceb60c8deb260c01131f680e8b" +checksum = "e971a258717db3dc8939c38410d8b8cb8126f1b05b9758672daa7cae3e0248c2" dependencies = [ "new_debug_unreachable", "num-bigint", @@ -6516,9 +6522,9 @@ dependencies = [ [[package]] name = "swc_experimental_ast_macros" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45db26cc495a8695f8034277ceb6a44e52eb702d6c64ea5c7156dd673ea8c98" +checksum = "4ea34d308ac6d6275f8db1b17ad97f418252881e5571aebb00b25bdd76b1a61a" dependencies = [ "proc-macro2", "quote", @@ -6527,9 +6533,9 @@ dependencies = [ [[package]] name = "swc_experimental_ecma_ast" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23564af8c9c028bebb6a0d40ec6ac81c1167c4b6dfcc3268db96d07b11d82576" +checksum = "20d6b8909beb446633ca6b01cf713d07d899a0b36c611f42e295011305a90372" dependencies = [ "num-bigint", "oxc_index", @@ -6540,9 +6546,9 @@ dependencies = [ [[package]] name = "swc_experimental_ecma_parser" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb177bdc3f39cc4d69c3b41b5c5e40c10f48a77ac3d9b9c11d282b2d36afa7c5" +checksum = "1a56639b88f4a05370335f4df2d9148de794d8469d380722d1364d16adbcbf20" dependencies = [ "bitflags 2.9.1", "either", @@ -6557,9 +6563,9 @@ dependencies = [ [[package]] name = "swc_experimental_ecma_semantic" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cec03726324631cc59fb5e89f26a9e005c99825d0a96217ed6430a29f49099d" +checksum = "2ab0f625c6b01151edbf67cb5a07cece1280cb19610d547c9de13ed58a6d709e" dependencies = [ "bitflags 2.9.1", "oxc_index", @@ -6571,9 +6577,9 @@ dependencies = [ [[package]] name = "swc_experimental_ecma_visit" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0893ca54f257f32286b5a8a8d58cf3a25126897e9ab2549f4327c1381023eea" +checksum = "dcd5577d8b36074bf0f5ecf1bceaa9f2cd7449c7b1b2f8ef7a0ca6b0e5fd5230" dependencies = [ "swc_experimental_ecma_ast", ] @@ -6630,9 +6636,9 @@ dependencies = [ [[package]] name = "swc_html_minifier" -version = "42.0.0" +version = "43.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5c9393dfb081b8658b030d2b52f5afe10c529773f574e6fc51b2fe0446c4d87" +checksum = "982adda9ac4530c6bdbaa52dd821571339b8b0876d78227f4a0b48ca84a90b41" dependencies = [ "once_cell", "rustc-hash", @@ -6718,9 +6724,9 @@ dependencies = [ [[package]] name = "swc_plugin_proxy" -version = "19.0.0" +version = "20.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f976dc63cd8b1916f265ee7d2647c28a85b433640099a5bf07153346dedffda" +checksum = "e78e1a9d26be495289cb07b9d73222f92f60b74d47904169d3ce4977600ec11e" dependencies = [ "better_scoped_tls", "cbor4ii", @@ -6733,9 +6739,9 @@ dependencies = [ [[package]] name = "swc_plugin_runner" -version = "23.0.0" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea88e7208565cc3d9c23fdd0ab4cf3896fb857f27ebb6d100805556fd0e46b10" +checksum = "9a48f119fba50bb6bbf54366fc37a7e0c4d94a641936a082330cc6ab8e3f947d" dependencies = [ "anyhow", "blake3", diff --git a/Cargo.toml b/Cargo.toml index b78563c84690..857140f5b8e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -135,19 +135,19 @@ rkyv = { version = "=0.8.8", default-features = false, features = ["std", " # Must be pinned with the same swc versions pnp = { version = "0.12.7", default-features = false } -swc = { version = "52.0.0", default-features = false } +swc = { version = "53.0.0", default-features = false } swc_config = { version = "3.1.2", default-features = false } -swc_core = { version = "54.0.0", default-features = false, features = ["parallel_rayon"] } -swc_ecma_minifier = { version = "42.0.0", default-features = false } +swc_core = { version = "55.0.0", default-features = false, features = ["parallel_rayon"] } +swc_ecma_minifier = { version = "43.0.0", default-features = false } swc_error_reporters = { version = "20.0.0", default-features = false } swc_html = { version = "30.0.0", default-features = false } -swc_html_minifier = { version = "42.0.0", default-features = false } +swc_html_minifier = { version = "43.0.0", default-features = false } swc_node_comments = { version = "18.0.0", default-features = false } -swc_plugin_runner = { version = "23.0.0", default-features = false } +swc_plugin_runner = { version = "24.0.0", default-features = false } -swc_experimental_ecma_ast = { version = "0.4.0", default-features = false } -swc_experimental_ecma_parser = { version = "0.4.0", default-features = false } -swc_experimental_ecma_semantic = { version = "0.4.0", default-features = false } +swc_experimental_ecma_ast = { version = "0.4.1", default-features = false } +swc_experimental_ecma_parser = { version = "0.4.1", default-features = false } +swc_experimental_ecma_semantic = { version = "0.4.1", default-features = false } rspack_dojang = { version = "0.1.11", default-features = false } tracy-client = { version = "=0.18.4", default-features = false, features = [ diff --git a/crates/rspack_cacheable/src/with/as_string.rs b/crates/rspack_cacheable/src/with/as_string.rs index 0063f77c2a6e..17dd1e53e875 100644 --- a/crates/rspack_cacheable/src/with/as_string.rs +++ b/crates/rspack_cacheable/src/with/as_string.rs @@ -59,17 +59,3 @@ where AsStringConverter::from_str(field.as_str()) } } - -// for pathbuf -use std::path::PathBuf; -impl AsStringConverter for PathBuf { - fn to_string(&self) -> Result { - Ok(self.to_string_lossy().to_string()) - } - fn from_str(s: &str) -> Result - where - Self: Sized, - { - Ok(PathBuf::from(s)) - } -} diff --git a/crates/rspack_cacheable/src/with/custom.rs b/crates/rspack_cacheable/src/with/custom.rs index 8d871bc3a1c7..eaeec7f0ca24 100644 --- a/crates/rspack_cacheable/src/with/custom.rs +++ b/crates/rspack_cacheable/src/with/custom.rs @@ -1,3 +1,5 @@ +use std::hash::Hash; + use rkyv::{ Archive, Deserialize, Place, Serialize, de::Pooling, @@ -5,8 +7,9 @@ use rkyv::{ ser::Sharing, with::{ArchiveWith, DeserializeWith, SerializeWith}, }; +use rspack_cacheable_macros::enable_cacheable as cacheable; -use crate::{ContextGuard, Error, Result, cacheable}; +use crate::{ContextGuard, Error, Result}; /// A trait for writing custom serialization and deserialization. /// @@ -27,6 +30,32 @@ pub struct Custom; #[cacheable(crate=crate)] pub struct DataBox(T); +// impl hashable for ArchivedDataBox +impl Hash for ArchivedDataBox +where + T: Archive, + T::Archived: Hash, +{ + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} +impl PartialEq for ArchivedDataBox +where + T: Archive, + T::Archived: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} +impl Eq for ArchivedDataBox +where + T: Archive, + T::Archived: Eq, +{ +} + pub struct CustomResolver { resolver: DataBoxResolver, value: DataBox, diff --git a/crates/rspack_collections/src/identifier.rs b/crates/rspack_collections/src/identifier.rs index 3cec6a0ac319..9b5cfea1a958 100644 --- a/crates/rspack_collections/src/identifier.rs +++ b/crates/rspack_collections/src/identifier.rs @@ -9,7 +9,11 @@ use std::{ use dashmap::{DashMap, DashSet}; use hashlink::{LinkedHashMap, LinkedHashSet}; use indexmap::{IndexMap, IndexSet}; -use rspack_cacheable::{cacheable, with, with::AsPreset}; +use rspack_cacheable::{ + ContextGuard, Error as CacheableError, cacheable, + utils::PortableString, + with::{Custom, CustomConverter}, +}; use serde::Serialize; use ustr::Ustr; @@ -34,10 +38,10 @@ pub type IdentifierIndexSet = IndexSet>; pub type IdentifierLinkedSet = LinkedHashSet>; -#[cacheable(hashable)] +#[cacheable(with=Custom, hashable)] #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)] #[cfg_attr(allocative, derive(allocative::Allocative))] -pub struct Identifier(#[cacheable(with=AsPreset)] Ustr); +pub struct Identifier(Ustr); impl Deref for Identifier { type Target = Ustr; @@ -99,14 +103,12 @@ impl fmt::Display for Identifier { } // for Identifier -impl with::AsRefStrConverter for Identifier { - fn as_str(&self) -> &str { - self.0.as_str() +impl CustomConverter for Identifier { + type Target = PortableString; + fn serialize(&self, guard: &ContextGuard) -> Result { + Ok(PortableString::new(self.as_str(), guard.project_root())) } - fn from_str(s: &str) -> Self - where - Self: Sized, - { - s.into() + fn deserialize(data: Self::Target, guard: &ContextGuard) -> Result { + Ok(Self::from(data.into_path_string(guard.project_root()))) } } diff --git a/crates/rspack_core/src/artifacts/async_modules_artifact.rs b/crates/rspack_core/src/artifacts/async_modules_artifact.rs new file mode 100644 index 000000000000..de7c389c845c --- /dev/null +++ b/crates/rspack_core/src/artifacts/async_modules_artifact.rs @@ -0,0 +1,47 @@ +use std::ops::{Deref, DerefMut}; + +use rspack_collections::IdentifierSet; + +#[derive(Debug, Default, Clone)] +pub struct AsyncModulesArtifact(IdentifierSet); + +impl Deref for AsyncModulesArtifact { + type Target = IdentifierSet; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for AsyncModulesArtifact { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From for AsyncModulesArtifact { + fn from(value: IdentifierSet) -> Self { + Self(value) + } +} + +impl From for IdentifierSet { + fn from(value: AsyncModulesArtifact) -> Self { + value.0 + } +} + +impl FromIterator<::Item> for AsyncModulesArtifact { + fn from_iter::Item>>(iter: T) -> Self { + Self(IdentifierSet::from_iter(iter)) + } +} + +impl IntoIterator for AsyncModulesArtifact { + type Item = ::Item; + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} diff --git a/crates/rspack_core/src/artifacts/cgc_runtime_requirements_artifact.rs b/crates/rspack_core/src/artifacts/cgc_runtime_requirements_artifact.rs new file mode 100644 index 000000000000..97736e8d5332 --- /dev/null +++ b/crates/rspack_core/src/artifacts/cgc_runtime_requirements_artifact.rs @@ -0,0 +1,55 @@ +use std::ops::{Deref, DerefMut}; + +use rspack_collections::UkeyMap; + +use crate::{ChunkUkey, RuntimeGlobals}; + +#[derive(Debug, Default, Clone)] +pub struct CgcRuntimeRequirementsArtifact(UkeyMap); + +impl Deref for CgcRuntimeRequirementsArtifact { + type Target = UkeyMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for CgcRuntimeRequirementsArtifact { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From> for CgcRuntimeRequirementsArtifact { + fn from(value: UkeyMap) -> Self { + Self(value) + } +} + +impl From for UkeyMap { + fn from(value: CgcRuntimeRequirementsArtifact) -> Self { + value.0 + } +} + +impl FromIterator< as IntoIterator>::Item> + for CgcRuntimeRequirementsArtifact +{ + fn from_iter< + T: IntoIterator as IntoIterator>::Item>, + >( + iter: T, + ) -> Self { + Self(UkeyMap::from_iter(iter)) + } +} + +impl IntoIterator for CgcRuntimeRequirementsArtifact { + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} diff --git a/crates/rspack_core/src/artifacts/chunk_render_artifact.rs b/crates/rspack_core/src/artifacts/chunk_render_artifact.rs new file mode 100644 index 000000000000..31e98c9cdf94 --- /dev/null +++ b/crates/rspack_core/src/artifacts/chunk_render_artifact.rs @@ -0,0 +1,55 @@ +use std::ops::{Deref, DerefMut}; + +use rspack_collections::UkeyMap; + +use crate::{ChunkRenderResult, ChunkUkey}; + +#[derive(Debug, Default, Clone)] +pub struct ChunkRenderArtifact(UkeyMap); + +impl Deref for ChunkRenderArtifact { + type Target = UkeyMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ChunkRenderArtifact { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From> for ChunkRenderArtifact { + fn from(value: UkeyMap) -> Self { + Self(value) + } +} + +impl From for UkeyMap { + fn from(value: ChunkRenderArtifact) -> Self { + value.0 + } +} + +impl FromIterator< as IntoIterator>::Item> + for ChunkRenderArtifact +{ + fn from_iter< + T: IntoIterator as IntoIterator>::Item>, + >( + iter: T, + ) -> Self { + Self(UkeyMap::from_iter(iter)) + } +} + +impl IntoIterator for ChunkRenderArtifact { + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} diff --git a/crates/rspack_core/src/artifacts/dependencies_diagnostics_artifact.rs b/crates/rspack_core/src/artifacts/dependencies_diagnostics_artifact.rs new file mode 100644 index 000000000000..92889c2960c0 --- /dev/null +++ b/crates/rspack_core/src/artifacts/dependencies_diagnostics_artifact.rs @@ -0,0 +1,58 @@ +use std::ops::{Deref, DerefMut}; + +use rspack_collections::IdentifierMap; +use rspack_error::Diagnostic; + +#[derive(Debug, Default, Clone)] +pub struct DependenciesDiagnosticsArtifact(IdentifierMap>); + +impl DependenciesDiagnosticsArtifact { + pub fn into_values(self) -> impl Iterator> { + self.0.into_values() + } +} + +impl Deref for DependenciesDiagnosticsArtifact { + type Target = IdentifierMap>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for DependenciesDiagnosticsArtifact { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From>> for DependenciesDiagnosticsArtifact { + fn from(value: IdentifierMap>) -> Self { + Self(value) + } +} + +impl From for IdentifierMap> { + fn from(value: DependenciesDiagnosticsArtifact) -> Self { + value.0 + } +} + +impl FromIterator<> as IntoIterator>::Item> + for DependenciesDiagnosticsArtifact +{ + fn from_iter> as IntoIterator>::Item>>( + iter: T, + ) -> Self { + Self(IdentifierMap::from_iter(iter)) + } +} + +impl IntoIterator for DependenciesDiagnosticsArtifact { + type Item = > as IntoIterator>::Item; + type IntoIter = > as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} diff --git a/crates/rspack_core/src/artifacts/imported_by_defer_modules_artifact.rs b/crates/rspack_core/src/artifacts/imported_by_defer_modules_artifact.rs new file mode 100644 index 000000000000..48c168a25eee --- /dev/null +++ b/crates/rspack_core/src/artifacts/imported_by_defer_modules_artifact.rs @@ -0,0 +1,47 @@ +use std::ops::{Deref, DerefMut}; + +use rspack_collections::IdentifierSet; + +#[derive(Debug, Default, Clone)] +pub struct ImportedByDeferModulesArtifact(IdentifierSet); + +impl Deref for ImportedByDeferModulesArtifact { + type Target = IdentifierSet; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ImportedByDeferModulesArtifact { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From for ImportedByDeferModulesArtifact { + fn from(value: IdentifierSet) -> Self { + Self(value) + } +} + +impl From for IdentifierSet { + fn from(value: ImportedByDeferModulesArtifact) -> Self { + value.0 + } +} + +impl FromIterator<::Item> for ImportedByDeferModulesArtifact { + fn from_iter::Item>>(iter: T) -> Self { + Self(IdentifierSet::from_iter(iter)) + } +} + +impl IntoIterator for ImportedByDeferModulesArtifact { + type Item = ::Item; + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} diff --git a/crates/rspack_core/src/artifacts/mod.rs b/crates/rspack_core/src/artifacts/mod.rs index bdc364889edf..211fef3e38c6 100644 --- a/crates/rspack_core/src/artifacts/mod.rs +++ b/crates/rspack_core/src/artifacts/mod.rs @@ -1,31 +1,31 @@ -use rspack_collections::{IdentifierMap, IdentifierSet, UkeyMap}; -use rspack_error::Diagnostic; - -use crate::{ChunkRenderResult, ChunkUkey, ModuleId, RuntimeGlobals}; - +mod async_modules_artifact; +mod cgc_runtime_requirements_artifact; mod cgm_hash_artifact; mod cgm_runtime_requirement_artifact; mod chunk_hashes_artifact; mod chunk_ids_artifact; +mod chunk_render_artifact; mod chunk_render_cache_artifact; mod code_generation_results; +mod dependencies_diagnostics_artifact; +mod imported_by_defer_modules_artifact; mod module_graph_cache_artifact; +mod module_ids_artifact; mod module_static_cache_artifact; mod side_effects_do_optimize_artifact; +pub use async_modules_artifact::AsyncModulesArtifact; +pub use cgc_runtime_requirements_artifact::CgcRuntimeRequirementsArtifact; pub use cgm_hash_artifact::*; pub use cgm_runtime_requirement_artifact::*; pub use chunk_hashes_artifact::*; pub use chunk_ids_artifact::*; +pub use chunk_render_artifact::ChunkRenderArtifact; pub use chunk_render_cache_artifact::ChunkRenderCacheArtifact; pub use code_generation_results::*; +pub use dependencies_diagnostics_artifact::DependenciesDiagnosticsArtifact; +pub use imported_by_defer_modules_artifact::ImportedByDeferModulesArtifact; pub use module_graph_cache_artifact::*; +pub use module_ids_artifact::ModuleIdsArtifact; pub use module_static_cache_artifact::*; pub use side_effects_do_optimize_artifact::*; - -pub type AsyncModulesArtifact = IdentifierSet; -pub type ImportedByDeferModulesArtifact = IdentifierSet; -pub type DependenciesDiagnosticsArtifact = IdentifierMap>; -pub type ModuleIdsArtifact = IdentifierMap; -pub type CgcRuntimeRequirementsArtifact = UkeyMap; -pub type ChunkRenderArtifact = UkeyMap; diff --git a/crates/rspack_core/src/artifacts/module_ids_artifact.rs b/crates/rspack_core/src/artifacts/module_ids_artifact.rs new file mode 100644 index 000000000000..22ed6e161c7c --- /dev/null +++ b/crates/rspack_core/src/artifacts/module_ids_artifact.rs @@ -0,0 +1,51 @@ +use std::ops::{Deref, DerefMut}; + +use rspack_collections::IdentifierMap; + +use crate::ModuleId; + +#[derive(Debug, Default, Clone)] +pub struct ModuleIdsArtifact(IdentifierMap); + +impl Deref for ModuleIdsArtifact { + type Target = IdentifierMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ModuleIdsArtifact { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From> for ModuleIdsArtifact { + fn from(value: IdentifierMap) -> Self { + Self(value) + } +} + +impl From for IdentifierMap { + fn from(value: ModuleIdsArtifact) -> Self { + value.0 + } +} + +impl FromIterator< as IntoIterator>::Item> for ModuleIdsArtifact { + fn from_iter as IntoIterator>::Item>>( + iter: T, + ) -> Self { + Self(IdentifierMap::from_iter(iter)) + } +} + +impl IntoIterator for ModuleIdsArtifact { + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} diff --git a/crates/rspack_core/src/cache/persistent/build_dependencies/mod.rs b/crates/rspack_core/src/cache/persistent/build_dependencies/mod.rs index 1b3622c1e63f..35aa5123d46b 100644 --- a/crates/rspack_core/src/cache/persistent/build_dependencies/mod.rs +++ b/crates/rspack_core/src/cache/persistent/build_dependencies/mod.rs @@ -9,6 +9,7 @@ use rustc_hash::FxHashSet as HashSet; use self::helper::{Helper, is_node_package_path}; use super::{ + codec::CacheCodec, snapshot::{Snapshot, SnapshotOptions}, storage::Storage, }; @@ -40,6 +41,7 @@ impl BuildDeps { snapshot_options: &SnapshotOptions, fs: Arc, storage: Arc, + codec: Arc, ) -> Self { Self { added: Default::default(), @@ -49,6 +51,7 @@ impl BuildDeps { snapshot_options.clone(), fs.clone(), storage.clone(), + codec, ), storage, fs, @@ -112,7 +115,10 @@ mod test { use rspack_fs::{MemoryFileSystem, WritableFileSystem}; use rspack_storage::Storage; - use super::{super::storage::MemoryStorage, BuildDeps, SCOPE, SnapshotOptions}; + use super::{ + super::{codec::CacheCodec, storage::MemoryStorage}, + BuildDeps, SCOPE, SnapshotOptions, + }; #[tokio::test] async fn build_dependencies_test() { let fs = Arc::new(MemoryFileSystem::default()); @@ -151,13 +157,26 @@ mod test { let options = vec![PathBuf::from("/index.js"), PathBuf::from("/configs")]; let snapshot_options = SnapshotOptions::default(); let storage = Arc::new(MemoryStorage::default()); - let mut build_deps = BuildDeps::new(&options, &snapshot_options, fs.clone(), storage.clone()); + let codec = Arc::new(CacheCodec::new(None)); + let mut build_deps = BuildDeps::new( + &options, + &snapshot_options, + fs.clone(), + storage.clone(), + codec.clone(), + ); let warnings = build_deps.add(vec![].into_iter()).await; assert_eq!(warnings.len(), 1); let data = storage.load(SCOPE).await.expect("should load success"); assert_eq!(data.len(), 9); - let mut build_deps = BuildDeps::new(&options, &snapshot_options, fs.clone(), storage.clone()); + let mut build_deps = BuildDeps::new( + &options, + &snapshot_options, + fs.clone(), + storage.clone(), + codec, + ); fs.write("/b.js".into(), r#"require("./c")"#.as_bytes()) .await .unwrap(); diff --git a/crates/rspack_core/src/cache/persistent/cacheable_context.rs b/crates/rspack_core/src/cache/persistent/cacheable_context.rs deleted file mode 100644 index b2e919abb23d..000000000000 --- a/crates/rspack_core/src/cache/persistent/cacheable_context.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[derive(Debug)] -pub struct CacheableContext; - -impl rspack_cacheable::CacheableContext for CacheableContext { - fn project_root(&self) -> Option<&std::path::Path> { - None - } -} diff --git a/crates/rspack_core/src/cache/persistent/codec.rs b/crates/rspack_core/src/cache/persistent/codec.rs new file mode 100644 index 000000000000..24791964c3d7 --- /dev/null +++ b/crates/rspack_core/src/cache/persistent/codec.rs @@ -0,0 +1,64 @@ +use std::path::Path; + +use rspack_cacheable::{ + __private::rkyv::{Archive, Deserialize, Serialize, bytecheck::CheckBytes}, + Deserializer, Serializer, Validator, from_bytes, to_bytes, +}; +use rspack_error::Result; +use rspack_paths::Utf8PathBuf; + +/// Internal cacheable context for serialization +#[derive(Debug, Clone)] +struct Context { + project_path: Option, +} + +impl rspack_cacheable::CacheableContext for Context { + fn project_root(&self) -> Option<&Path> { + self.project_path.as_ref().map(|p| p.as_std_path()) + } +} + +/// Cache codec for encoding and decoding cacheable data +/// +/// This struct encapsulates the serialization and deserialization logic, +/// automatically passing the project context to rspack_cacheable's to_bytes and from_bytes. +/// +/// # Example +/// +/// ```ignore +/// let codec = CacheCodec::new(project_path); +/// +/// // Encode data to bytes +/// let bytes = codec.encode(&my_data)?; +/// +/// // Decode bytes back to data +/// let my_data: MyType = codec.decode(&bytes)?; +/// ``` +#[derive(Debug, Clone)] +pub struct CacheCodec { + context: Context, +} + +impl CacheCodec { + pub fn new(project_path: Option) -> Self { + Self { + context: Context { project_path }, + } + } + + pub fn encode(&self, data: &T) -> Result> + where + T: for<'a> Serialize>, + { + to_bytes(data, &self.context).map_err(|e| rspack_error::error!(e.to_string())) + } + + pub fn decode(&self, bytes: &[u8]) -> Result + where + T: Archive, + T::Archived: for<'a> CheckBytes> + Deserialize, + { + from_bytes(bytes, &self.context).map_err(|e| rspack_error::error!(e.to_string())) + } +} diff --git a/crates/rspack_core/src/cache/persistent/mod.rs b/crates/rspack_core/src/cache/persistent/mod.rs index 6dde42607888..6d5558feecf5 100644 --- a/crates/rspack_core/src/cache/persistent/mod.rs +++ b/crates/rspack_core/src/cache/persistent/mod.rs @@ -1,5 +1,5 @@ pub mod build_dependencies; -mod cacheable_context; +pub mod codec; pub mod occasion; pub mod snapshot; pub mod storage; @@ -9,13 +9,18 @@ use std::{ sync::Arc, }; -pub use cacheable_context::CacheableContext; +use rspack_cacheable::{ + cacheable, + utils::PortablePath, + with::{As, AsVec}, +}; use rspack_fs::{IntermediateFileSystem, ReadableFileSystem}; use rspack_paths::ArcPathSet; use rspack_workspace::rspack_pkg_version; use self::{ build_dependencies::{BuildDeps, BuildDepsOptions}, + codec::CacheCodec, occasion::{MakeOccasion, MetaOccasion}, snapshot::{Snapshot, SnapshotOptions}, storage::{Storage, StorageOptions, create_storage}, @@ -26,8 +31,10 @@ use crate::{ compilation::build_module_graph::{BuildModuleGraphArtifact, BuildModuleGraphArtifactState}, }; +#[cacheable] #[derive(Debug, Clone, Hash)] pub struct PersistentCacheOptions { + #[cacheable(with=AsVec>)] pub build_dependencies: BuildDepsOptions, pub version: String, pub snapshot: SnapshotOptions, @@ -40,10 +47,10 @@ pub struct PersistentCache { initialized: bool, build_deps: BuildDeps, snapshot: Snapshot, - storage: Arc, make_occasion: MakeOccasion, meta_occasion: MetaOccasion, async_mode: bool, + storage: Arc, // TODO replace to logger and output warnings directly. warnings: Vec, } @@ -57,19 +64,23 @@ impl PersistentCache { intermediate_filesystem: Arc, ) -> Self { let async_mode = compiler_options.mode.is_development(); + let codec = Arc::new(CacheCodec::new(None)); + // use codec.encode to transform the absolute path in option, + // it will ensure that same project in different directory have the same version. + let option_bytes = codec + .encode(option) + .expect("should persistent cache options can be serialized"); let version = { let mut hasher = DefaultHasher::new(); compiler_path.hash(&mut hasher); - option.hash(&mut hasher); + option_bytes.hash(&mut hasher); rspack_pkg_version!().hash(&mut hasher); compiler_options.name.hash(&mut hasher); compiler_options.mode.hash(&mut hasher); hex::encode(hasher.finish().to_ne_bytes()) }; let storage = create_storage(option.storage.clone(), version, intermediate_filesystem); - let context = Arc::new(CacheableContext); - let make_occasion = MakeOccasion::new(storage.clone(), context); - let meta_occasion = MetaOccasion::new(storage.clone()); + Self { initialized: false, build_deps: BuildDeps::new( @@ -77,13 +88,19 @@ impl PersistentCache { &option.snapshot, input_filesystem.clone(), storage.clone(), + codec.clone(), ), - snapshot: Snapshot::new(option.snapshot.clone(), input_filesystem, storage.clone()), - storage, - make_occasion, - meta_occasion, - async_mode, + snapshot: Snapshot::new( + option.snapshot.clone(), + input_filesystem, + storage.clone(), + codec.clone(), + ), + make_occasion: MakeOccasion::new(storage.clone(), codec.clone()), + meta_occasion: MetaOccasion::new(storage.clone(), codec), warnings: Default::default(), + async_mode, + storage, } } diff --git a/crates/rspack_core/src/cache/persistent/occasion/make/mod.rs b/crates/rspack_core/src/cache/persistent/occasion/make/mod.rs index 7205525aa1f8..fc1cceeecaa0 100644 --- a/crates/rspack_core/src/cache/persistent/occasion/make/mod.rs +++ b/crates/rspack_core/src/cache/persistent/occasion/make/mod.rs @@ -8,7 +8,7 @@ use rspack_collections::IdentifierSet; use rspack_error::Result; use rustc_hash::FxHashSet; -use super::super::{Storage, cacheable_context::CacheableContext}; +use super::super::{Storage, codec::CacheCodec}; use crate::{ FactorizeInfo, compilation::build_module_graph::{BuildModuleGraphArtifact, BuildModuleGraphArtifactState}, @@ -18,13 +18,13 @@ use crate::{ /// Make Occasion is used to save MakeArtifact #[derive(Debug)] pub struct MakeOccasion { - context: Arc, + codec: Arc, storage: Arc, } impl MakeOccasion { - pub fn new(storage: Arc, context: Arc) -> Self { - Self { storage, context } + pub fn new(storage: Arc, codec: Arc) -> Self { + Self { storage, codec } } #[tracing::instrument(name = "Cache::Occasion::Make::save", skip_all)] @@ -65,14 +65,14 @@ impl MakeOccasion { affected_modules.removed(), &need_update_modules, &self.storage, - &self.context, + &self.codec, ); } #[tracing::instrument(name = "Cache::Occasion::Make::recovery", skip_all)] pub async fn recovery(&self) -> Result { let (mg, module_to_lazy_make, entry_dependencies) = - module_graph::recovery_module_graph(&self.storage, &self.context).await?; + module_graph::recovery_module_graph(&self.storage, &self.codec).await?; // regenerate statistical data // recovery make_failed_module diff --git a/crates/rspack_core/src/cache/persistent/occasion/make/module_graph.rs b/crates/rspack_core/src/cache/persistent/occasion/make/module_graph.rs index 6720d58ecf98..00e798d5e1ae 100644 --- a/crates/rspack_core/src/cache/persistent/occasion/make/module_graph.rs +++ b/crates/rspack_core/src/cache/persistent/occasion/make/module_graph.rs @@ -1,9 +1,7 @@ use std::sync::Arc; use rayon::prelude::*; -use rspack_cacheable::{ - Error as CacheableError, cacheable, from_bytes, to_bytes, utils::OwnedOrRef, -}; +use rspack_cacheable::{cacheable, utils::OwnedOrRef}; use rspack_collections::IdentifierSet; use rspack_error::Result; use rustc_hash::FxHashSet as HashSet; @@ -16,7 +14,7 @@ use crate::{ AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, BoxDependency, BoxModule, Dependency, DependencyId, DependencyParents, ExportsInfoData, ModuleGraph, ModuleGraphConnection, ModuleGraphModule, ModuleIdentifier, RayonConsumer, - cache::persistent::cacheable_context::CacheableContext, + cache::persistent::codec::CacheCodec, compilation::build_module_graph::{LazyDependencies, ModuleToLazyMake}, }; @@ -43,7 +41,7 @@ pub fn save_module_graph( removed_modules: &IdentifierSet, need_update_modules: &IdentifierSet, storage: &Arc, - context: &CacheableContext, + codec: &CacheCodec, ) { for identifier in removed_modules { storage.remove(SCOPE, identifier.as_bytes()); @@ -94,9 +92,9 @@ pub fn save_module_graph( blocks, lazy_info, }; - match to_bytes(&node, context) { + match codec.encode(&node) { Ok(bytes) => (identifier.as_bytes().to_vec(), bytes), - Err(err @ CacheableError::UnsupportedField) => { + Err(err) if err.to_string().contains("unsupported field") => { tracing::warn!("to bytes failed {:?}", err); // try use alternatives node.module = TempModule::transform_from(node.module); @@ -106,7 +104,7 @@ pub fn save_module_graph( .map(|(dep, _)| (TempDependency::transform_from(dep), None)) .collect(); node.blocks = vec![]; - if let Ok(bytes) = to_bytes(&node, context) { + if let Ok(bytes) = codec.encode(&node) { (identifier.as_bytes().to_vec(), bytes) } else { panic!("alternatives serialize failed") @@ -129,7 +127,7 @@ pub fn save_module_graph( #[tracing::instrument("Cache::Occasion::Make::ModuleGraph::recovery", skip_all)] pub async fn recovery_module_graph( storage: &Arc, - context: &CacheableContext, + codec: &CacheCodec, ) -> Result<(ModuleGraph, ModuleToLazyMake, HashSet)> { let mut need_check_dep = vec![]; let mut mg = ModuleGraph::default(); @@ -139,7 +137,8 @@ pub async fn recovery_module_graph( .await? .into_par_iter() .map(|(_, v)| { - from_bytes::(&v, context) + codec + .decode::(&v) .expect("unexpected module graph deserialize failed") }) .with_max_len(1) diff --git a/crates/rspack_core/src/cache/persistent/occasion/meta/mod.rs b/crates/rspack_core/src/cache/persistent/occasion/meta/mod.rs index da6a84e2a30c..41afc8a3b99a 100644 --- a/crates/rspack_core/src/cache/persistent/occasion/meta/mod.rs +++ b/crates/rspack_core/src/cache/persistent/occasion/meta/mod.rs @@ -1,10 +1,10 @@ use std::sync::Arc; -use rspack_cacheable::{cacheable, from_bytes, to_bytes}; +use rspack_cacheable::cacheable; use rspack_error::Result; use rspack_tasks::{get_current_dependency_id, set_current_dependency_id}; -use super::super::Storage; +use super::super::{Storage, codec::CacheCodec}; pub const SCOPE: &str = "meta"; @@ -18,11 +18,12 @@ struct Meta { #[derive(Debug)] pub struct MetaOccasion { storage: Arc, + codec: Arc, } impl MetaOccasion { - pub fn new(storage: Arc) -> Self { - Self { storage } + pub fn new(storage: Arc, codec: Arc) -> Self { + Self { storage, codec } } #[tracing::instrument("Cache::Occasion::Meta::save", skip_all)] @@ -33,7 +34,7 @@ impl MetaOccasion { self.storage.set( SCOPE, "default".as_bytes().to_vec(), - to_bytes(&meta, &()).expect("should to bytes success"), + self.codec.encode(&meta).expect("should encode success"), ); } @@ -43,7 +44,7 @@ impl MetaOccasion { return Ok(()); }; - let meta: Meta = from_bytes(&value, &()).expect("should from bytes success"); + let meta: Meta = self.codec.decode(&value).expect("should decode success"); if get_current_dependency_id() != 0 { panic!("The global dependency id generator is not 0 when the persistent cache is restored."); } diff --git a/crates/rspack_core/src/cache/persistent/snapshot/mod.rs b/crates/rspack_core/src/cache/persistent/snapshot/mod.rs index 6a6007402a3e..c0ef9a5bddc2 100644 --- a/crates/rspack_core/src/cache/persistent/snapshot/mod.rs +++ b/crates/rspack_core/src/cache/persistent/snapshot/mod.rs @@ -1,9 +1,8 @@ mod option; mod strategy; -use std::{path::Path, sync::Arc}; +use std::sync::Arc; -use rspack_cacheable::{from_bytes, to_bytes}; use rspack_error::Result; use rspack_fs::ReadableFileSystem; use rspack_paths::{ArcPath, ArcPathSet}; @@ -13,7 +12,7 @@ pub use self::{ option::{PathMatcher, SnapshotOptions}, strategy::Strategy, }; -use super::storage::Storage; +use super::{codec::CacheCodec, storage::Storage}; use crate::FutureConsumer; pub const SCOPE: &str = "snapshot"; @@ -28,6 +27,7 @@ pub struct Snapshot { options: Arc, fs: Arc, storage: Arc, + codec: Arc, } impl Snapshot { @@ -35,12 +35,14 @@ impl Snapshot { options: SnapshotOptions, fs: Arc, storage: Arc, + codec: Arc, ) -> Self { Self { scope: SCOPE, options: Arc::new(options), fs, storage, + codec, } } @@ -49,12 +51,14 @@ impl Snapshot { options: SnapshotOptions, fs: Arc, storage: Arc, + codec: Arc, ) -> Self { Self { scope, options: Arc::new(options), fs, storage, + codec, } } @@ -81,16 +85,18 @@ impl Snapshot { #[tracing::instrument("Cache::Snapshot::add", skip_all)] pub async fn add(&self, paths: impl Iterator) { let helper = Arc::new(StrategyHelper::new(self.fs.clone())); + let codec = self.codec.clone(); // TODO merge package version file paths .map(|path| { let helper = helper.clone(); let options = self.options.clone(); + let codec = codec.clone(); async move { let strategy = Self::calc_strategy(&options, &helper, &path).await?; Some(( - path.as_os_str().as_encoded_bytes().to_vec(), - to_bytes::<_, ()>(&strategy, &()).expect("should to bytes success"), + codec.encode(&path).expect("should encode success"), + codec.encode(&strategy).expect("should encode success"), )) } }) @@ -117,6 +123,7 @@ impl Snapshot { let mut deleted_path = ArcPathSet::default(); let mut no_change_path = ArcPathSet::default(); let helper = Arc::new(StrategyHelper::new(self.fs.clone())); + let codec = self.codec.clone(); let data = self.storage.load(self.scope).await?; let is_hot_start = !data.is_empty(); @@ -124,10 +131,10 @@ impl Snapshot { .into_iter() .map(|(key, value)| { let helper = helper.clone(); + let codec = codec.clone(); async move { - let path: ArcPath = Path::new(&*String::from_utf8_lossy(&key)).into(); - let strategy: Strategy = - from_bytes::(&value, &()).expect("should from bytes success"); + let path: ArcPath = codec.decode(&key).expect("should decode success"); + let strategy: Strategy = codec.decode(&value).expect("should decode success"); let validate = helper.validate(&path, &strategy).await; (path, validate) } @@ -156,7 +163,10 @@ mod tests { use rspack_fs::{MemoryFileSystem, WritableFileSystem}; use rspack_paths::ArcPath; - use super::{super::storage::MemoryStorage, PathMatcher, Snapshot, SnapshotOptions}; + use super::{ + super::{codec::CacheCodec, storage::MemoryStorage}, + PathMatcher, Snapshot, SnapshotOptions, + }; macro_rules! p { ($tt:tt) => { @@ -168,6 +178,7 @@ mod tests { async fn should_snapshot_work() { let fs = Arc::new(MemoryFileSystem::default()); let storage = Arc::new(MemoryStorage::default()); + let codec = Arc::new(CacheCodec::new(None)); let options = SnapshotOptions::new( vec![PathMatcher::String("constant".into())], vec![PathMatcher::String("node_modules/project".into())], @@ -201,7 +212,7 @@ mod tests { .await .unwrap(); - let snapshot = Snapshot::new(options, fs.clone(), storage); + let snapshot = Snapshot::new(options, fs.clone(), storage, codec); snapshot .add( diff --git a/crates/rspack_core/src/cache/persistent/snapshot/option.rs b/crates/rspack_core/src/cache/persistent/snapshot/option.rs index 6fdca9f6e6e5..c1bd94065e64 100644 --- a/crates/rspack_core/src/cache/persistent/snapshot/option.rs +++ b/crates/rspack_core/src/cache/persistent/snapshot/option.rs @@ -1,9 +1,11 @@ +use rspack_cacheable::{cacheable, utils::PortablePath, with::As}; use rspack_regex::RspackRegex; /// Use string or regex to match path +#[cacheable] #[derive(Debug, Clone, Hash)] pub enum PathMatcher { - String(String), + String(#[cacheable(with=As)] String), Regexp(RspackRegex), } @@ -17,6 +19,7 @@ impl PathMatcher { } /// Snapshot options +#[cacheable] #[derive(Debug, Default, Clone, Hash)] pub struct SnapshotOptions { /// immutable paths, snapshot will ignore them diff --git a/crates/rspack_core/src/cache/persistent/storage/mod.rs b/crates/rspack_core/src/cache/persistent/storage/mod.rs index d55fac9e6989..fce42c9e3c2a 100644 --- a/crates/rspack_core/src/cache/persistent/storage/mod.rs +++ b/crates/rspack_core/src/cache/persistent/storage/mod.rs @@ -4,6 +4,7 @@ mod memory; use std::{path::PathBuf, sync::Arc}; pub use memory::MemoryStorage; +use rspack_cacheable::{cacheable, utils::PortablePath, with::As}; use rspack_fs::IntermediateFileSystem; pub use rspack_storage::Storage; use rspack_storage::{BridgeFileSystem, PackStorage, PackStorageOptions}; @@ -12,9 +13,13 @@ use rspack_storage::{BridgeFileSystem, PackStorage, PackStorageOptions}; /// /// This enum contains all of supported storage options. /// Since MemoryStorage is only used in unit test, there is no need to add it here. +#[cacheable] #[derive(Debug, Clone, Hash)] pub enum StorageOptions { - FileSystem { directory: PathBuf }, + FileSystem { + #[cacheable(with=As)] + directory: PathBuf, + }, } pub fn create_storage( diff --git a/crates/rspack_core/src/compilation/after_seal/mod.rs b/crates/rspack_core/src/compilation/after_seal/mod.rs new file mode 100644 index 000000000000..fdda5bdb2163 --- /dev/null +++ b/crates/rspack_core/src/compilation/after_seal/mod.rs @@ -0,0 +1,20 @@ +use super::*; +use crate::logger::Logger; + +pub async fn after_seal_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("after seal"); + compilation.after_seal(plugin_driver).await?; + logger.time_end(start); + Ok(()) +} + +impl Compilation { + #[instrument("Compilation:after_seal", target=TRACING_BENCH_TARGET,skip_all)] + async fn after_seal(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { + plugin_driver.compilation_hooks.after_seal.call(self).await + } +} diff --git a/crates/rspack_core/src/compilation/assign_runtime_ids/mod.rs b/crates/rspack_core/src/compilation/assign_runtime_ids/mod.rs new file mode 100644 index 000000000000..19f63e10730c --- /dev/null +++ b/crates/rspack_core/src/compilation/assign_runtime_ids/mod.rs @@ -0,0 +1,42 @@ +use super::*; + +pub fn assign_runtime_ids(compilation: &mut Compilation) { + fn process_entrypoint( + entrypoint_ukey: &ChunkGroupUkey, + chunk_group_by_ukey: &ChunkGroupByUkey, + chunk_by_ukey: &ChunkByUkey, + chunk_graph: &mut ChunkGraph, + ) { + let entrypoint = chunk_group_by_ukey.expect_get(entrypoint_ukey); + let runtime = entrypoint + .kind + .get_entry_options() + .and_then(|o| match &o.runtime { + Some(EntryRuntime::String(s)) => Some(s.to_owned()), + _ => None, + }) + .or(entrypoint.name().map(|n| n.to_string())); + if let (Some(runtime), Some(chunk)) = ( + runtime, + chunk_by_ukey.get(&entrypoint.get_runtime_chunk(chunk_group_by_ukey)), + ) { + chunk_graph.set_runtime_id(runtime, chunk.id().map(|id| id.to_string())); + } + } + for i in compilation.entrypoints.iter() { + process_entrypoint( + i.1, + &compilation.chunk_group_by_ukey, + &compilation.chunk_by_ukey, + &mut compilation.chunk_graph, + ) + } + for i in compilation.async_entrypoints.iter() { + process_entrypoint( + i, + &compilation.chunk_group_by_ukey, + &compilation.chunk_by_ukey, + &mut compilation.chunk_graph, + ) + } +} diff --git a/crates/rspack_core/src/compilation/build_chunk_graph/mod.rs b/crates/rspack_core/src/compilation/build_chunk_graph/mod.rs index a2a60e24b443..e64652e1f4e7 100644 --- a/crates/rspack_core/src/compilation/build_chunk_graph/mod.rs +++ b/crates/rspack_core/src/compilation/build_chunk_graph/mod.rs @@ -7,6 +7,7 @@ use crate::{Compilation, incremental::IncrementalPasses}; pub(crate) mod artifact; pub(crate) mod code_splitter; pub(crate) mod incremental; +pub(crate) mod pass; #[instrument("Compilation:build_chunk_graph", skip_all)] pub fn build_chunk_graph(compilation: &mut Compilation) -> rspack_error::Result<()> { diff --git a/crates/rspack_core/src/compilation/build_chunk_graph/pass.rs b/crates/rspack_core/src/compilation/build_chunk_graph/pass.rs new file mode 100644 index 000000000000..f6a5380e1368 --- /dev/null +++ b/crates/rspack_core/src/compilation/build_chunk_graph/pass.rs @@ -0,0 +1,26 @@ +use rspack_error::Result; + +use crate::{ + compilation::{ + Compilation, + build_chunk_graph::{artifact::use_code_splitting_cache, build_chunk_graph}, + }, + logger::Logger, +}; + +pub async fn build_chunk_graph_pass(compilation: &mut Compilation) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + compilation.module_graph_cache_artifact.freeze(); + use_code_splitting_cache(compilation, |compilation| async { + let start = logger.time("rebuild chunk graph"); + build_chunk_graph(compilation)?; + compilation + .chunk_graph + .generate_dot(compilation, "after-code-splitting") + .await; + logger.time_end(start); + Ok(compilation) + }) + .await?; + Ok(()) +} diff --git a/crates/rspack_core/src/compilation/build_module_graph/mod.rs b/crates/rspack_core/src/compilation/build_module_graph/mod.rs index a7eb0bdcfac5..9afcfecd0800 100644 --- a/crates/rspack_core/src/compilation/build_module_graph/mod.rs +++ b/crates/rspack_core/src/compilation/build_module_graph/mod.rs @@ -3,7 +3,11 @@ mod graph_updater; mod lazy_barrel_artifact; mod module_executor; +use std::sync::atomic::Ordering; + use rspack_error::Result; +use rspack_util::tracing_preset::TRACING_BENCH_TARGET; +use tracing::instrument; pub use self::{ artifact::{BuildModuleGraphArtifact, BuildModuleGraphArtifactState}, @@ -13,7 +17,36 @@ pub use self::{ }, module_executor::{ExecuteModuleId, ExecutedRuntimeModule, ModuleExecutor}, }; -use crate::Compilation; +use crate::{Compilation, logger::Logger}; + +pub async fn build_module_graph_pass(compilation: &mut Compilation) -> Result<()> { + let logger = compilation.get_logger("rspack.Compiler"); + let start = logger.time("build module graph"); + compilation.do_build_module_graph().await?; + logger.time_end(start); + Ok(()) +} + +impl Compilation { + #[instrument("Compilation:build_module_graph",target=TRACING_BENCH_TARGET, skip_all)] + async fn do_build_module_graph(&mut self) -> Result<()> { + // run module_executor + if let Some(module_executor) = &mut self.module_executor { + let mut module_executor = std::mem::take(module_executor); + module_executor.hook_before_make(self).await?; + self.module_executor = Some(module_executor); + } + + let artifact = self.build_module_graph_artifact.take(); + self + .build_module_graph_artifact + .replace(build_module_graph(self, artifact).await?); + + self.in_finish_make.store(true, Ordering::Release); + + Ok(()) + } +} /// make module graph, support incremental rebuild /// diff --git a/crates/rspack_core/src/compilation/chunk_ids/mod.rs b/crates/rspack_core/src/compilation/chunk_ids/mod.rs new file mode 100644 index 000000000000..900093978765 --- /dev/null +++ b/crates/rspack_core/src/compilation/chunk_ids/mod.rs @@ -0,0 +1,38 @@ +use super::*; +use crate::logger::Logger; + +pub async fn chunk_ids_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("chunk ids"); + + // Check if CHUNK_IDS pass is disabled, and clear artifact if needed + if !compilation + .incremental + .passes_enabled(IncrementalPasses::CHUNK_IDS) + { + compilation.named_chunk_ids_artifact.clear(); + } + + let mut diagnostics = vec![]; + let mut chunk_by_ukey = mem::take(&mut compilation.chunk_by_ukey); + let mut named_chunk_ids_artifact = mem::take(&mut compilation.named_chunk_ids_artifact); + plugin_driver + .compilation_hooks + .chunk_ids + .call( + compilation, + &mut chunk_by_ukey, + &mut named_chunk_ids_artifact, + &mut diagnostics, + ) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.chunkIds"))?; + compilation.chunk_by_ukey = chunk_by_ukey; + compilation.named_chunk_ids_artifact = named_chunk_ids_artifact; + compilation.extend_diagnostics(diagnostics); + logger.time_end(start); + Ok(()) +} diff --git a/crates/rspack_core/src/compilation/code_generation/mod.rs b/crates/rspack_core/src/compilation/code_generation/mod.rs new file mode 100644 index 000000000000..cc8922cf06be --- /dev/null +++ b/crates/rspack_core/src/compilation/code_generation/mod.rs @@ -0,0 +1,208 @@ +use super::*; +use crate::logger::Logger; + +pub async fn code_generation_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("code generation"); + let code_generation_modules = if let Some(mutations) = compilation + .incremental + .mutations_read(IncrementalPasses::MODULES_CODEGEN) + && !compilation.code_generation_results.is_empty() + { + let revoked_modules = mutations.iter().filter_map(|mutation| match mutation { + Mutation::ModuleRemove { module } => Some(*module), + _ => None, + }); + for revoked_module in revoked_modules { + compilation.code_generation_results.remove(&revoked_module); + } + let modules: IdentifierSet = mutations + .iter() + .filter_map(|mutation| match mutation { + Mutation::ModuleSetHashes { module } => Some(*module), + _ => None, + }) + .collect(); + // also cleanup for updated modules, for `insert(); insert();` the second insert() won't override the first insert() on code_generation_results + for module in &modules { + compilation.code_generation_results.remove(module); + } + tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::MODULES_CODEGEN, %mutations); + let logger = compilation.get_logger("rspack.incremental.modulesCodegen"); + logger.log(format!( + "{} modules are affected, {} in total", + modules.len(), + compilation.get_module_graph().modules().len() + )); + modules + } else { + compilation.code_generation_results = Default::default(); + compilation + .get_module_graph() + .modules() + .keys() + .copied() + .collect() + }; + compilation.code_generation(code_generation_modules).await?; + + let mut diagnostics = vec![]; + plugin_driver + .compilation_hooks + .after_code_generation + .call(compilation, &mut diagnostics) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.afterCodeGeneration"))?; + compilation.extend_diagnostics(diagnostics); + + logger.time_end(start); + Ok(()) +} + +impl Compilation { + #[instrument("Compilation:code_generation",target=TRACING_BENCH_TARGET, skip_all)] + async fn code_generation(&mut self, modules: IdentifierSet) -> Result<()> { + let logger = self.get_logger("rspack.Compilation"); + let mut codegen_cache_counter = match self.options.cache { + CacheOptions::Disabled => None, + _ => Some(logger.cache("module code generation cache")), + }; + + let module_graph = self.get_module_graph(); + let mut no_codegen_dependencies_modules = IdentifierSet::default(); + let mut has_codegen_dependencies_modules = IdentifierSet::default(); + for module_identifier in modules { + let module = module_graph + .module_by_identifier(&module_identifier) + .expect("should have module"); + if module.get_code_generation_dependencies().is_none() { + no_codegen_dependencies_modules.insert(module_identifier); + } else { + has_codegen_dependencies_modules.insert(module_identifier); + } + } + + self + .code_generation_modules(&mut codegen_cache_counter, no_codegen_dependencies_modules) + .await?; + self + .code_generation_modules(&mut codegen_cache_counter, has_codegen_dependencies_modules) + .await?; + + if let Some(counter) = codegen_cache_counter { + logger.cache_end(counter); + } + + Ok(()) + } + + pub(crate) async fn code_generation_modules( + &mut self, + cache_counter: &mut Option, + modules: IdentifierSet, + ) -> Result<()> { + let chunk_graph = &self.chunk_graph; + let module_graph = self.get_module_graph(); + let mut jobs = Vec::new(); + for module in modules { + let mut map: HashMap = HashMap::default(); + for runtime in chunk_graph.get_module_runtimes_iter(module, &self.chunk_by_ukey) { + let hash = ChunkGraph::get_module_hash(self, module, runtime) + .expect("should have cgm.hash in code generation"); + let scope = self + .plugin_driver + .compilation_hooks + .concatenation_scope + .call(self, module) + .await?; + if let Some(job) = map.get_mut(hash) { + job.runtimes.push(runtime.clone()); + } else { + map.insert( + hash.clone(), + CodeGenerationJob { + module, + hash: hash.clone(), + runtime: runtime.clone(), + runtimes: vec![runtime.clone()], + scope, + }, + ); + } + } + jobs.extend(map.into_values()); + } + + let results = rspack_futures::scope::<_, _>(|token| { + jobs.into_iter().for_each(|job| { + // SAFETY: await immediately and trust caller to poll future entirely + let s = unsafe { token.used((&self, &module_graph, job)) }; + + s.spawn(|(this, module_graph, job)| async { + let options = &this.options; + let old_cache = &this.old_cache; + + let module = module_graph + .module_by_identifier(&job.module) + .expect("should have module"); + let codegen_res = old_cache + .code_generate_occasion + .use_cache(&job, || async { + module + .code_generation(this, Some(&job.runtime), job.scope.clone()) + .await + .map(|mut codegen_res| { + codegen_res.set_hash( + &options.output.hash_function, + &options.output.hash_digest, + &options.output.hash_salt, + ); + codegen_res + }) + }) + .await; + + (job.module, job.runtimes, codegen_res) + }) + }) + }) + .await; + let results = results + .into_iter() + .map(|res| res.to_rspack_result()) + .collect::>>()?; + + for (module, runtimes, (codegen_res, from_cache)) in results { + if let Some(counter) = cache_counter { + if from_cache { + counter.hit(); + } else { + counter.miss(); + } + } + let codegen_res = match codegen_res { + Ok(codegen_res) => codegen_res, + Err(err) => { + let mut diagnostic = Diagnostic::from(err); + diagnostic.module_identifier = Some(module); + self.push_diagnostic(diagnostic); + let mut codegen_res = CodeGenerationResult::default(); + codegen_res.set_hash( + &self.options.output.hash_function, + &self.options.output.hash_digest, + &self.options.output.hash_salt, + ); + codegen_res + } + }; + self + .code_generation_results + .insert(module, codegen_res, runtimes); + self.code_generated_modules.insert(module); + } + Ok(()) + } +} diff --git a/crates/rspack_core/src/compilation/create_chunk_assets/mod.rs b/crates/rspack_core/src/compilation/create_chunk_assets/mod.rs new file mode 100644 index 000000000000..69a7fe2074c6 --- /dev/null +++ b/crates/rspack_core/src/compilation/create_chunk_assets/mod.rs @@ -0,0 +1,171 @@ +use super::*; +use crate::logger::Logger; + +pub async fn create_chunk_assets_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("create chunk assets"); + compilation.create_chunk_assets(plugin_driver).await?; + logger.time_end(start); + Ok(()) +} + +impl Compilation { + #[instrument("Compilation::create_chunk_assets",target=TRACING_BENCH_TARGET, skip_all)] + async fn create_chunk_assets(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { + if (self.options.output.filename.has_hash_placeholder() + || self.options.output.chunk_filename.has_hash_placeholder() + || self.options.output.css_filename.has_hash_placeholder() + || self + .options + .output + .css_chunk_filename + .has_hash_placeholder()) + && let Some(diagnostic) = self.incremental.disable_passes( + IncrementalPasses::CHUNKS_RENDER, + "Chunk filename that dependent on full hash", + "chunk filename that dependent on full hash is not supported in incremental compilation", + ) + && let Some(diagnostic) = diagnostic + { + self.push_diagnostic(diagnostic); + } + + // Check if CHUNKS_RENDER pass is disabled, and clear artifact if needed + if !self + .incremental + .passes_enabled(IncrementalPasses::CHUNKS_RENDER) + { + self.chunk_render_artifact.clear(); + } + + let chunks = if let Some(mutations) = self + .incremental + .mutations_read(IncrementalPasses::CHUNKS_RENDER) + && !self.chunk_render_artifact.is_empty() + { + let removed_chunks = mutations.iter().filter_map(|mutation| match mutation { + Mutation::ChunkRemove { chunk } => Some(*chunk), + _ => None, + }); + for removed_chunk in removed_chunks { + self.chunk_render_artifact.remove(&removed_chunk); + } + self + .chunk_render_artifact + .retain(|chunk, _| self.chunk_by_ukey.contains(chunk)); + let chunks: UkeySet = mutations + .iter() + .filter_map(|mutation| match mutation { + Mutation::ChunkSetHashes { chunk } => Some(*chunk), + _ => None, + }) + .collect(); + tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::CHUNKS_RENDER, %mutations); + let logger = self.get_logger("rspack.incremental.chunksRender"); + logger.log(format!( + "{} chunks are affected, {} in total", + chunks.len(), + self.chunk_by_ukey.len() + )); + chunks + } else { + self.chunk_by_ukey.keys().copied().collect() + }; + let results = rspack_futures::scope::<_, Result<_>>(|token| { + chunks.iter().for_each(|chunk| { + // SAFETY: await immediately and trust caller to poll future entirely + let s = unsafe { token.used((&self, &plugin_driver, chunk)) }; + + s.spawn(|(this, plugin_driver, chunk)| async { + let mut manifests = Vec::new(); + let mut diagnostics = Vec::new(); + plugin_driver + .compilation_hooks + .render_manifest + .call(this, chunk, &mut manifests, &mut diagnostics) + .await?; + + rspack_error::Result::Ok(( + *chunk, + ChunkRenderResult { + manifests, + diagnostics, + }, + )) + }); + }) + }) + .await; + + let mut chunk_render_results = ChunkRenderArtifact::default(); + for result in results { + let item = result.to_rspack_result()?; + let (key, value) = item?; + chunk_render_results.insert(key, value); + } + let chunk_ukey_and_manifest = if self + .incremental + .passes_enabled(IncrementalPasses::CHUNKS_RENDER) + { + self.chunk_render_artifact.extend(chunk_render_results); + self.chunk_render_artifact.clone() + } else { + chunk_render_results + }; + + for ( + chunk_ukey, + ChunkRenderResult { + manifests, + diagnostics, + }, + ) in chunk_ukey_and_manifest + { + self.extend_diagnostics(diagnostics); + + for file_manifest in manifests { + let filename = file_manifest.filename; + let current_chunk = self.chunk_by_ukey.expect_get_mut(&chunk_ukey); + + current_chunk.set_rendered(true); + if file_manifest.auxiliary { + current_chunk.add_auxiliary_file(filename.clone()); + } else { + current_chunk.add_file(filename.clone()); + } + + self.emit_asset( + filename.clone(), + CompilationAsset::new(Some(file_manifest.source), file_manifest.info), + ); + + _ = self + .chunk_asset(chunk_ukey, &filename, plugin_driver.clone()) + .await; + } + } + + Ok(()) + } + + // #[instrument( + // name = "Compilation:chunk_asset", + // skip(self, plugin_driver, chunk_ukey) + // )] + async fn chunk_asset( + &self, + chunk_ukey: ChunkUkey, + filename: &str, + plugin_driver: SharedPluginDriver, + ) -> Result<()> { + plugin_driver + .compilation_hooks + .chunk_asset + .call(self, &chunk_ukey, filename) + .await?; + Ok(()) + } +} diff --git a/crates/rspack_core/src/compilation/create_hash/mod.rs b/crates/rspack_core/src/compilation/create_hash/mod.rs new file mode 100644 index 000000000000..25ab646b16d4 --- /dev/null +++ b/crates/rspack_core/src/compilation/create_hash/mod.rs @@ -0,0 +1,452 @@ +use super::*; +use crate::logger::Logger; + +pub struct ChunkHashResult { + pub hash: RspackHashDigest, + pub content_hash: ChunkContentHash, +} + +pub async fn create_hash_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("hashing"); + compilation.create_hash(plugin_driver).await?; + compilation.runtime_modules_code_generation().await?; + logger.time_end(start); + Ok(()) +} + +impl Compilation { + #[instrument(name = "Compilation:create_hash",target=TRACING_BENCH_TARGET, skip_all)] + pub async fn create_hash(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { + let logger = self.get_logger("rspack.Compilation"); + + // Check if there are any chunks that depend on full hash, usually only runtime chunks are + // possible to depend on full hash, but for library type commonjs/module, it's possible to + // have non-runtime chunks depend on full hash, the library format plugin is using + // dependent_full_hash hook to declare it. + let mut full_hash_chunks = UkeySet::default(); + for chunk_ukey in self.chunk_by_ukey.keys() { + let chunk_dependent_full_hash = plugin_driver + .compilation_hooks + .dependent_full_hash + .call(self, chunk_ukey) + .await? + .unwrap_or_default(); + if chunk_dependent_full_hash { + full_hash_chunks.insert(*chunk_ukey); + } + } + if !full_hash_chunks.is_empty() + && let Some(diagnostic) = self.incremental.disable_passes( + IncrementalPasses::CHUNKS_HASHES, + "Chunk content that dependent on full hash", + "it requires calculating the hashes of all the chunks, which is a global effect", + ) + && let Some(diagnostic) = diagnostic + { + self.push_diagnostic(diagnostic); + } + if !self + .incremental + .passes_enabled(IncrementalPasses::CHUNKS_HASHES) + { + self.chunk_hashes_artifact.clear(); + } + + let create_hash_chunks = if let Some(mutations) = self + .incremental + .mutations_read(IncrementalPasses::CHUNKS_HASHES) + && !self.chunk_hashes_artifact.is_empty() + { + let removed_chunks = mutations.iter().filter_map(|mutation| match mutation { + Mutation::ChunkRemove { chunk } => Some(*chunk), + _ => None, + }); + for removed_chunk in removed_chunks { + self.chunk_hashes_artifact.remove(&removed_chunk); + } + self + .chunk_hashes_artifact + .retain(|chunk, _| self.chunk_by_ukey.contains(chunk)); + let chunks = mutations.get_affected_chunks_with_chunk_graph(self); + tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::CHUNKS_HASHES, %mutations, ?chunks); + let logger = self.get_logger("rspack.incremental.chunksHashes"); + logger.log(format!( + "{} chunks are affected, {} in total", + chunks.len(), + self.chunk_by_ukey.len(), + )); + chunks + } else { + self.chunk_by_ukey.keys().copied().collect() + }; + + let mut compilation_hasher = RspackHash::from(&self.options.output); + + fn try_process_chunk_hash_results( + compilation: &mut Compilation, + chunk_hash_results: Vec>, + ) -> Result<()> { + for hash_result in chunk_hash_results { + let (chunk_ukey, chunk_hash_result) = hash_result?; + let chunk = compilation.chunk_by_ukey.expect_get(&chunk_ukey); + let chunk_hashes_changed = chunk.set_hashes( + &mut compilation.chunk_hashes_artifact, + chunk_hash_result.hash, + chunk_hash_result.content_hash, + ); + if chunk_hashes_changed + && let Some(mut mutations) = compilation.incremental.mutations_write() + { + mutations.add(Mutation::ChunkSetHashes { chunk: chunk_ukey }); + } + } + Ok(()) + } + + let unordered_runtime_chunks: UkeySet = self.get_chunk_graph_entries().collect(); + let start = logger.time("hashing: hash chunks"); + let other_chunks: Vec<_> = create_hash_chunks + .iter() + .filter(|key| !unordered_runtime_chunks.contains(key)) + .collect(); + + // create hash for runtime modules in other chunks + let other_chunk_runtime_module_hashes = rspack_futures::scope::<_, Result<_>>(|token| { + other_chunks + .iter() + .flat_map(|chunk| self.chunk_graph.get_chunk_runtime_modules_iterable(chunk)) + .for_each(|runtime_module_identifier| { + let s = unsafe { token.used((&self, runtime_module_identifier)) }; + s.spawn(|(compilation, runtime_module_identifier)| async { + let runtime_module = &compilation.runtime_modules[runtime_module_identifier]; + let digest = runtime_module.get_runtime_hash(compilation, None).await?; + Ok((*runtime_module_identifier, digest)) + }); + }) + }) + .await + .into_iter() + .map(|res| res.to_rspack_result()) + .collect::>>()?; + + for res in other_chunk_runtime_module_hashes { + let (runtime_module_identifier, digest) = res?; + self + .runtime_modules_hash + .insert(runtime_module_identifier, digest); + } + + // create hash for other chunks + let other_chunks_hash_results = rspack_futures::scope::<_, Result<_>>(|token| { + for chunk in other_chunks { + let s = unsafe { token.used((&self, chunk, &plugin_driver)) }; + s.spawn(|(compilation, chunk, plugin_driver)| async { + let hash_result = compilation + .process_chunk_hash(*chunk, plugin_driver) + .await?; + Ok((*chunk, hash_result)) + }); + } + }) + .await + .into_iter() + .map(|res| res.to_rspack_result()) + .collect::>>()?; + + try_process_chunk_hash_results(self, other_chunks_hash_results)?; + logger.time_end(start); + + // collect references for runtime chunks + let mut runtime_chunks_map: HashMap, u32)> = + unordered_runtime_chunks + .into_iter() + .map(|runtime_chunk| (runtime_chunk, (Vec::new(), 0))) + .collect(); + let mut remaining: u32 = 0; + for runtime_chunk_ukey in runtime_chunks_map.keys().copied().collect::>() { + let runtime_chunk = self.chunk_by_ukey.expect_get(&runtime_chunk_ukey); + let groups = runtime_chunk.get_all_referenced_async_entrypoints(&self.chunk_group_by_ukey); + for other in groups + .into_iter() + .map(|group| self.chunk_group_by_ukey.expect_get(&group)) + .map(|group| group.get_runtime_chunk(&self.chunk_group_by_ukey)) + { + let (other_referenced_by, _) = runtime_chunks_map + .get_mut(&other) + .expect("should in runtime_chunks_map"); + other_referenced_by.push(runtime_chunk_ukey); + let info = runtime_chunks_map + .get_mut(&runtime_chunk_ukey) + .expect("should in runtime_chunks_map"); + info.1 += 1; + remaining += 1; + } + } + // sort runtime chunks by its references + let mut runtime_chunks = Vec::with_capacity(runtime_chunks_map.len()); + for (runtime_chunk, (_, remaining)) in &runtime_chunks_map { + if *remaining == 0 { + runtime_chunks.push(*runtime_chunk); + } + } + let mut ready_chunks = Vec::new(); + + let mut i = 0; + while i < runtime_chunks.len() { + let chunk_ukey = runtime_chunks[i]; + let has_full_hash_modules = full_hash_chunks.contains(&chunk_ukey) + || self + .chunk_graph + .has_chunk_full_hash_modules(&chunk_ukey, &self.runtime_modules); + if has_full_hash_modules { + full_hash_chunks.insert(chunk_ukey); + } + let referenced_by = runtime_chunks_map + .get(&chunk_ukey) + .expect("should in runtime_chunks_map") + .0 + .clone(); + for other in referenced_by { + if has_full_hash_modules { + for runtime_module in self.chunk_graph.get_chunk_runtime_modules_iterable(&other) { + let runtime_module = self + .runtime_modules + .get(runtime_module) + .expect("should have runtime_module"); + if runtime_module.dependent_hash() { + full_hash_chunks.insert(other); + break; + } + } + } + remaining -= 1; + let (_, other_remaining) = runtime_chunks_map + .get_mut(&other) + .expect("should in runtime_chunks_map"); + *other_remaining -= 1; + if *other_remaining == 0 { + ready_chunks.push(other); + } + } + if !ready_chunks.is_empty() { + runtime_chunks.append(&mut ready_chunks); + } + i += 1; + } + // create warning for remaining circular references + if remaining > 0 { + let mut circular: Vec<_> = runtime_chunks_map + .iter() + .filter(|(_, (_, remaining))| *remaining != 0) + .map(|(chunk_ukey, _)| self.chunk_by_ukey.expect_get(chunk_ukey)) + .collect(); + circular.sort_unstable_by(|a, b| a.id().cmp(&b.id())); + runtime_chunks.extend(circular.iter().map(|chunk| chunk.ukey())); + let circular_names = circular + .iter() + .map(|chunk| { + chunk + .name() + .or(chunk.id().map(|id| id.as_str())) + .unwrap_or("no id chunk") + }) + .join(", "); + let error = rspack_error::Error::warning(format!( + "Circular dependency between chunks with runtime ({circular_names})\nThis prevents using hashes of each other and should be avoided." + )); + self.push_diagnostic(error.into()); + } + + // create hash for runtime chunks and the runtime modules within them + // The subsequent runtime chunks and runtime modules will depend on + // the hash results of the previous runtime chunks and runtime modules. + // Therefore, create hashes one by one in sequence. + let start = logger.time("hashing: hash runtime chunks"); + for runtime_chunk_ukey in runtime_chunks { + let runtime_module_hashes = rspack_futures::scope::<_, Result<_>>(|token| { + self + .chunk_graph + .get_chunk_runtime_modules_iterable(&runtime_chunk_ukey) + .for_each(|runtime_module_identifier| { + let s = unsafe { token.used((&self, runtime_module_identifier)) }; + s.spawn(|(compilation, runtime_module_identifier)| async { + let runtime_module = &compilation.runtime_modules[runtime_module_identifier]; + let digest = runtime_module.get_runtime_hash(compilation, None).await?; + Ok((*runtime_module_identifier, digest)) + }); + }) + }) + .await + .into_iter() + .map(|res| res.to_rspack_result()) + .collect::>>()?; + + for res in runtime_module_hashes { + let (mid, digest) = res?; + self.runtime_modules_hash.insert(mid, digest); + } + + let chunk_hash_result = self + .process_chunk_hash(runtime_chunk_ukey, &plugin_driver) + .await?; + let chunk = self.chunk_by_ukey.expect_get(&runtime_chunk_ukey); + let chunk_hashes_changed = chunk.set_hashes( + &mut self.chunk_hashes_artifact, + chunk_hash_result.hash, + chunk_hash_result.content_hash, + ); + if chunk_hashes_changed && let Some(mut mutations) = self.incremental.mutations_write() { + mutations.add(Mutation::ChunkSetHashes { + chunk: runtime_chunk_ukey, + }); + } + } + logger.time_end(start); + + // create full hash + self + .chunk_by_ukey + .values() + .sorted_unstable_by_key(|chunk| chunk.ukey()) + .filter_map(|chunk| chunk.hash(&self.chunk_hashes_artifact)) + .for_each(|hash| { + hash.hash(&mut compilation_hasher); + }); + self.hot_index.hash(&mut compilation_hasher); + self.hash = Some(compilation_hasher.digest(&self.options.output.hash_digest)); + + // re-create runtime chunk hash that depend on full hash + let start = logger.time("hashing: process full hash chunks"); + for chunk_ukey in full_hash_chunks { + for runtime_module_identifier in self + .chunk_graph + .get_chunk_runtime_modules_iterable(&chunk_ukey) + { + let runtime_module = &self.runtime_modules[runtime_module_identifier]; + if runtime_module.full_hash() || runtime_module.dependent_hash() { + let digest = runtime_module.get_runtime_hash(self, None).await?; + self + .runtime_modules_hash + .insert(*runtime_module_identifier, digest); + } + } + let chunk = self.chunk_by_ukey.expect_get(&chunk_ukey); + let new_chunk_hash = { + let chunk_hash = chunk + .hash(&self.chunk_hashes_artifact) + .expect("should have chunk hash"); + let mut hasher = RspackHash::from(&self.options.output); + chunk_hash.hash(&mut hasher); + self.hash.hash(&mut hasher); + hasher.digest(&self.options.output.hash_digest) + }; + let new_content_hash = { + let content_hash = chunk + .content_hash(&self.chunk_hashes_artifact) + .expect("should have content hash"); + content_hash + .iter() + .map(|(source_type, content_hash)| { + let mut hasher = RspackHash::from(&self.options.output); + content_hash.hash(&mut hasher); + self.hash.hash(&mut hasher); + ( + *source_type, + hasher.digest(&self.options.output.hash_digest), + ) + }) + .collect() + }; + let chunk_hashes_changed = chunk.set_hashes( + &mut self.chunk_hashes_artifact, + new_chunk_hash, + new_content_hash, + ); + if chunk_hashes_changed && let Some(mut mutations) = self.incremental.mutations_write() { + mutations.add(Mutation::ChunkSetHashes { chunk: chunk_ukey }); + } + } + logger.time_end(start); + Ok(()) + } + + #[instrument(skip_all)] + pub async fn runtime_modules_code_generation(&mut self) -> Result<()> { + let results = rspack_futures::scope::<_, Result<_>>(|token| { + self + .runtime_modules + .iter() + .for_each(|(runtime_module_identifier, runtime_module)| { + let s = unsafe { token.used((&self, runtime_module_identifier, runtime_module)) }; + s.spawn( + |(compilation, runtime_module_identifier, runtime_module)| async { + let result = runtime_module + .code_generation(compilation, None, None) + .await?; + let source = result + .get(&SourceType::Runtime) + .expect("should have source"); + Ok((*runtime_module_identifier, source.clone())) + }, + ) + }) + }) + .await + .into_iter() + .map(|res| res.to_rspack_result()) + .collect::>>()?; + + let mut runtime_module_sources = IdentifierMap::::default(); + for result in results { + let (runtime_module_identifier, source) = result?; + runtime_module_sources.insert(runtime_module_identifier, source); + } + + self.runtime_modules_code_generation_source = runtime_module_sources; + self + .code_generated_modules + .extend(self.runtime_modules.keys().copied()); + Ok(()) + } + + async fn process_chunk_hash( + &self, + chunk_ukey: ChunkUkey, + plugin_driver: &SharedPluginDriver, + ) -> Result { + let mut hasher = RspackHash::from(&self.options.output); + if let Some(chunk) = self.chunk_by_ukey.get(&chunk_ukey) { + chunk.update_hash(&mut hasher, self); + } + plugin_driver + .compilation_hooks + .chunk_hash + .call(self, &chunk_ukey, &mut hasher) + .await?; + let chunk_hash = hasher.digest(&self.options.output.hash_digest); + + let mut content_hashes: HashMap = HashMap::default(); + plugin_driver + .compilation_hooks + .content_hash + .call(self, &chunk_ukey, &mut content_hashes) + .await?; + + let content_hashes = content_hashes + .into_iter() + .map(|(t, mut hasher)| { + chunk_hash.hash(&mut hasher); + (t, hasher.digest(&self.options.output.hash_digest)) + }) + .collect(); + + Ok(ChunkHashResult { + hash: chunk_hash, + content_hash: content_hashes, + }) + } +} diff --git a/crates/rspack_core/src/compilation/create_module_assets/mod.rs b/crates/rspack_core/src/compilation/create_module_assets/mod.rs new file mode 100644 index 000000000000..1d029caa1138 --- /dev/null +++ b/crates/rspack_core/src/compilation/create_module_assets/mod.rs @@ -0,0 +1,53 @@ +use super::*; +use crate::logger::Logger; + +pub async fn create_module_assets_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("create module assets"); + compilation.create_module_assets(plugin_driver).await; + logger.time_end(start); + Ok(()) +} + +impl Compilation { + #[instrument("Compilation:create_module_assets",target=TRACING_BENCH_TARGET, skip_all)] + async fn create_module_assets(&mut self, _plugin_driver: SharedPluginDriver) { + let mut chunk_asset_map = vec![]; + let mut module_assets = vec![]; + let mg = self.get_module_graph(); + for (identifier, module) in mg.modules() { + let assets = &module.build_info().assets; + if assets.is_empty() { + continue; + } + + for (name, asset) in assets.as_ref() { + module_assets.push((name.clone(), asset.clone())); + } + // assets of executed modules are not in this compilation + if self + .chunk_graph + .chunk_graph_module_by_module_identifier + .contains_key(&identifier) + { + for chunk in self.chunk_graph.get_module_chunks(identifier).iter() { + for name in assets.keys() { + chunk_asset_map.push((*chunk, name.clone())) + } + } + } + } + + for (name, asset) in module_assets { + self.emit_asset(name, asset); + } + + for (chunk, asset_name) in chunk_asset_map { + let chunk = self.chunk_by_ukey.expect_get_mut(&chunk); + chunk.add_auxiliary_file(asset_name); + } + } +} diff --git a/crates/rspack_core/src/compilation/create_module_hashes/mod.rs b/crates/rspack_core/src/compilation/create_module_hashes/mod.rs new file mode 100644 index 000000000000..282833f8cbb1 --- /dev/null +++ b/crates/rspack_core/src/compilation/create_module_hashes/mod.rs @@ -0,0 +1,141 @@ +use super::*; +use crate::logger::Logger; + +pub async fn create_module_hashes_pass(compilation: &mut Compilation) -> Result<()> { + // Check if MODULES_HASHES pass is disabled, and clear artifact if needed + if !compilation + .incremental + .passes_enabled(IncrementalPasses::MODULES_HASHES) + { + compilation.cgm_hash_artifact.clear(); + } + + let create_module_hashes_modules = if let Some(mutations) = compilation + .incremental + .mutations_read(IncrementalPasses::MODULES_HASHES) + && !compilation.cgm_hash_artifact.is_empty() + { + let revoked_modules = mutations.iter().filter_map(|mutation| match mutation { + Mutation::ModuleRemove { module } => Some(*module), + _ => None, + }); + for revoked_module in revoked_modules { + compilation.cgm_hash_artifact.remove(&revoked_module); + } + let mut modules = mutations.get_affected_modules_with_chunk_graph(compilation); + + // check if module runtime changes + let mg = compilation.get_module_graph(); + for mi in mg.modules().keys() { + let module_runtimes = compilation + .chunk_graph + .get_module_runtimes(*mi, &compilation.chunk_by_ukey); + let module_runtime_keys = module_runtimes + .values() + .map(get_runtime_key) + .collect::>(); + + if let Some(runtime_map) = compilation.cgm_hash_artifact.get_runtime_map(mi) { + if module_runtimes.is_empty() { + // module has no runtime, skip + continue; + } + if module_runtimes.len() == 1 { + // single runtime + if !matches!(runtime_map.mode, RuntimeMode::SingleEntry) + || runtime_map + .single_runtime + .as_ref() + .expect("should have single runtime for single entry") + != module_runtimes + .values() + .next() + .expect("should have at least one runtime") + { + modules.insert(*mi); + } + } else { + // multiple runtimes + if matches!(runtime_map.mode, RuntimeMode::SingleEntry) { + modules.insert(*mi); + continue; + } + + if runtime_map.map.len() != module_runtimes.len() { + modules.insert(*mi); + continue; + } + + for runtime_key in runtime_map.map.keys() { + if !module_runtime_keys.contains(runtime_key) { + modules.insert(*mi); + break; + } + } + } + } + } + + tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::MODULES_HASHES, %mutations, ?modules); + let logger = compilation.get_logger("rspack.incremental.modulesHashes"); + logger.log(format!( + "{} modules are affected, {} in total", + modules.len(), + mg.modules().len() + )); + + modules + } else { + compilation + .get_module_graph() + .modules() + .keys() + .copied() + .collect() + }; + compilation + .create_module_hashes(create_module_hashes_modules) + .await +} + +impl Compilation { + #[instrument("Compilation:create_module_hashes", skip_all)] + pub async fn create_module_hashes(&mut self, modules: IdentifierSet) -> Result<()> { + let mg = self.get_module_graph(); + let chunk_graph = &self.chunk_graph; + let chunk_by_ukey = &self.chunk_by_ukey; + + let results = rspack_futures::scope::<_, Result<_>>(|token| { + for module_identifier in modules { + let s = unsafe { token.used((&*self, &mg, chunk_graph, chunk_by_ukey)) }; + s.spawn( + move |(compilation, mg, chunk_graph, chunk_by_ukey)| async move { + let mut hashes = RuntimeSpecMap::new(); + let module = mg + .module_by_identifier(&module_identifier) + .expect("should have module"); + for runtime in chunk_graph.get_module_runtimes_iter(module_identifier, chunk_by_ukey) { + let hash = module.get_runtime_hash(compilation, Some(runtime)).await?; + hashes.set(runtime.clone(), hash); + } + Ok((module_identifier, hashes)) + }, + ); + } + }) + .await + .into_iter() + .map(|r| r.to_rspack_result()) + .collect::>>()?; + + for result in results { + let (module, hashes) = result?; + if ChunkGraph::set_module_hashes(self, module, hashes) + && let Some(mut mutations) = self.incremental.mutations_write() + { + mutations.add(Mutation::ModuleSetHashes { module }); + } + } + Ok(()) + } +} diff --git a/crates/rspack_core/src/compilation/finish_make/mod.rs b/crates/rspack_core/src/compilation/finish_make/mod.rs new file mode 100644 index 000000000000..114414853a6a --- /dev/null +++ b/crates/rspack_core/src/compilation/finish_make/mod.rs @@ -0,0 +1,19 @@ +use rspack_error::Result; + +use crate::{Compilation, SharedPluginDriver, logger::Logger}; + +pub async fn finish_make_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compiler"); + let start = logger.time("finish make hook"); + plugin_driver + .compiler_hooks + .finish_make + .call(compilation) + .await?; + logger.time_end(start); + + Ok(()) +} diff --git a/crates/rspack_core/src/compilation/finish_module_graph/mod.rs b/crates/rspack_core/src/compilation/finish_module_graph/mod.rs new file mode 100644 index 000000000000..31b41da2dfa5 --- /dev/null +++ b/crates/rspack_core/src/compilation/finish_module_graph/mod.rs @@ -0,0 +1,45 @@ +use std::sync::atomic::Ordering; + +use rspack_error::Result; +use rspack_util::tracing_preset::TRACING_BENCH_TARGET; +use tracing::instrument; + +use crate::{ + Compilation, cache::Cache, compilation::build_module_graph::finish_build_module_graph, + logger::Logger, +}; + +pub async fn finish_module_graph_pass( + compilation: &mut Compilation, + cache: &mut dyn Cache, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compiler"); + let start = logger.time("finish compilation"); + compilation.finish_build_module_graph().await?; + cache + .after_build_module_graph(&compilation.build_module_graph_artifact) + .await; + logger.time_end(start); + + Ok(()) +} + +impl Compilation { + #[instrument("Compilation:finish",target=TRACING_BENCH_TARGET, skip_all)] + pub async fn finish_build_module_graph(&mut self) -> Result<()> { + self.in_finish_make.store(false, Ordering::Release); + // clean up the entry deps + let make_artifact = self.build_module_graph_artifact.take(); + self + .build_module_graph_artifact + .replace(finish_build_module_graph(self, make_artifact).await?); + // sync assets to module graph from module_executor + if let Some(module_executor) = &mut self.module_executor { + let mut module_executor = std::mem::take(module_executor); + module_executor.hook_after_finish_modules(self).await?; + self.module_executor = Some(module_executor); + } + // make finished, make artifact should be readonly thereafter. + Ok(()) + } +} diff --git a/crates/rspack_core/src/compilation/finish_modules/mod.rs b/crates/rspack_core/src/compilation/finish_modules/mod.rs new file mode 100644 index 000000000000..7d972011f6e5 --- /dev/null +++ b/crates/rspack_core/src/compilation/finish_modules/mod.rs @@ -0,0 +1,174 @@ +use rspack_error::Result; + +use super::*; +use crate::logger::Logger; + +pub async fn finish_modules_pass(compilation: &mut Compilation) -> Result<()> { + let dependencies_diagnostics_artifact = compilation.dependencies_diagnostics_artifact.clone(); + let async_modules_artifact = compilation.async_modules_artifact.clone(); + let diagnostics = compilation + .collect_build_module_graph_effects( + &mut dependencies_diagnostics_artifact.borrow_mut(), + &mut async_modules_artifact.borrow_mut(), + ) + .await?; + compilation.extend_diagnostics(diagnostics); + + Ok(()) +} + +impl Compilation { + #[tracing::instrument("Compilation:collect_build_module_graph_effects", skip_all)] + pub async fn collect_build_module_graph_effects( + &mut self, + dependencies_diagnostics_artifact: &mut DependenciesDiagnosticsArtifact, + async_modules_artifact: &mut AsyncModulesArtifact, + ) -> Result> { + let logger = self.get_logger("rspack.Compilation"); + if let Some(mut mutations) = self.incremental.mutations_write() { + mutations.extend( + self + .build_module_graph_artifact + .affected_dependencies + .updated() + .iter() + .map(|&dependency| Mutation::DependencyUpdate { dependency }), + ); + mutations.extend( + self + .build_module_graph_artifact + .affected_modules + .removed() + .iter() + .map(|&module| Mutation::ModuleRemove { module }), + ); + mutations.extend( + self + .build_module_graph_artifact + .affected_modules + .updated() + .iter() + .map(|&module| Mutation::ModuleUpdate { module }), + ); + mutations.extend( + self + .build_module_graph_artifact + .affected_modules + .added() + .iter() + .map(|&module| Mutation::ModuleAdd { module }), + ); + tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::MAKE, %mutations); + } + + let start = logger.time("finish modules"); + // finish_modules means the module graph (modules, connections, dependencies) are + // frozen and start to optimize (provided exports, infer async, etc.) based on the + // module graph, so any kind of change that affect these should be done before the + // finish_modules + self + .plugin_driver + .clone() + .compilation_hooks + .finish_modules + .call(self, async_modules_artifact) + .await?; + + logger.time_end(start); + + // https://github.com/webpack/webpack/blob/19ca74127f7668aaf60d59f4af8fcaee7924541a/lib/Compilation.js#L2988 + self.module_graph_cache_artifact.freeze(); + // Collect dependencies diagnostics at here to make sure: + // 1. after finish_modules: has provide exports info + // 2. before optimize dependencies: side effects free module hasn't been skipped + let mut all_diagnostics = + self.collect_dependencies_diagnostics(dependencies_diagnostics_artifact); + self.module_graph_cache_artifact.unfreeze(); + + // take make diagnostics + let diagnostics = self.build_module_graph_artifact.diagnostics(); + all_diagnostics.extend(diagnostics); + Ok(all_diagnostics) + } + + #[tracing::instrument("Compilation:collect_dependencies_diagnostics", skip_all)] + fn collect_dependencies_diagnostics( + &self, + dependencies_diagnostics_artifact: &mut DependenciesDiagnosticsArtifact, + ) -> Vec { + // Compute modules while holding the lock, then release it + let (modules, has_mutations) = { + let mutations = self + .incremental + .mutations_read(IncrementalPasses::DEPENDENCIES_DIAGNOSTICS); + + // TODO move diagnostic collect to make + if let Some(mutations) = mutations { + if !dependencies_diagnostics_artifact.is_empty() { + let revoked_modules = mutations.iter().filter_map(|mutation| match mutation { + Mutation::ModuleRemove { module } => Some(*module), + _ => None, + }); + for revoked_module in revoked_modules { + dependencies_diagnostics_artifact.remove(&revoked_module); + } + let modules = mutations.get_affected_modules_with_module_graph(self.get_module_graph()); + let logger = self.get_logger("rspack.incremental.dependenciesDiagnostics"); + logger.log(format!( + "{} modules are affected, {} in total", + modules.len(), + self.get_module_graph().modules().len() + )); + (modules, true) + } else { + ( + self.get_module_graph().modules().keys().copied().collect(), + true, + ) + } + } else { + ( + self.get_module_graph().modules().keys().copied().collect(), + false, + ) + } + }; + + let module_graph = self.get_module_graph(); + let module_graph_cache = &self.module_graph_cache_artifact; + let dependencies_diagnostics: DependenciesDiagnosticsArtifact = modules + .par_iter() + .map(|module_identifier| { + let mgm = module_graph + .module_graph_module_by_identifier(module_identifier) + .expect("should have mgm"); + let diagnostics = mgm + .all_dependencies + .iter() + .filter_map(|dependency_id| { + let dependency = module_graph.dependency_by_id(dependency_id); + dependency + .get_diagnostics(module_graph, module_graph_cache) + .map(|diagnostics| { + diagnostics.into_iter().map(|mut diagnostic| { + diagnostic.module_identifier = Some(*module_identifier); + diagnostic.loc = dependency.loc(); + diagnostic + }) + }) + }) + .flatten() + .collect::>(); + (*module_identifier, diagnostics) + }) + .collect::>>() + .into(); + let all_modules_diagnostics = if has_mutations { + dependencies_diagnostics_artifact.extend(dependencies_diagnostics); + dependencies_diagnostics_artifact.clone() + } else { + dependencies_diagnostics + }; + all_modules_diagnostics.into_values().flatten().collect() + } +} diff --git a/crates/rspack_core/src/compilation/make/mod.rs b/crates/rspack_core/src/compilation/make/mod.rs new file mode 100644 index 000000000000..a151ef1b2e6d --- /dev/null +++ b/crates/rspack_core/src/compilation/make/mod.rs @@ -0,0 +1,21 @@ +use rspack_error::Result; + +use crate::{Compilation, SharedPluginDriver, cache::Cache, logger::Logger}; + +pub async fn make_hook_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, + cache: &mut dyn Cache, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compiler"); + + cache + .before_build_module_graph(&mut compilation.build_module_graph_artifact) + .await; + + let start = logger.time("make hook"); + plugin_driver.compiler_hooks.make.call(compilation).await?; + logger.time_end(start); + + Ok(()) +} diff --git a/crates/rspack_core/src/compilation/mod.rs b/crates/rspack_core/src/compilation/mod.rs index 73025243b024..906cd85d485a 100644 --- a/crates/rspack_core/src/compilation/mod.rs +++ b/crates/rspack_core/src/compilation/mod.rs @@ -1,5 +1,28 @@ +mod after_seal; +mod assign_runtime_ids; pub mod build_chunk_graph; pub mod build_module_graph; +mod chunk_ids; +mod code_generation; +mod create_chunk_assets; +mod create_hash; +mod create_module_assets; +mod create_module_hashes; +mod finish_make; +mod finish_module_graph; +mod finish_modules; +mod make; +mod module_ids; +mod optimize_chunk_modules; +mod optimize_chunks; +mod optimize_code_generation; +mod optimize_dependencies; +mod optimize_modules; +mod optimize_tree; +mod process_assets; +mod run_passes; +mod runtime_requirements; +mod seal; use std::{ collections::{VecDeque, hash_map}, fmt::{self, Debug}, @@ -12,7 +35,6 @@ use std::{ }; use atomic_refcell::AtomicRefCell; -use build_chunk_graph::{artifact::use_code_splitting_cache, build_chunk_graph}; use dashmap::DashSet; use futures::future::BoxFuture; use indexmap::IndexMap; @@ -49,15 +71,14 @@ use crate::{ DependenciesDiagnosticsArtifact, DependencyCodeGeneration, DependencyTemplate, DependencyTemplateType, DependencyType, DerefOption, Entry, EntryData, EntryOptions, EntryRuntime, Entrypoint, ExecuteModuleId, Filename, ImportPhase, ImportVarMap, - ImportedByDeferModulesArtifact, Logger, MemoryGCStorage, ModuleFactory, ModuleGraph, + ImportedByDeferModulesArtifact, MemoryGCStorage, ModuleFactory, ModuleGraph, ModuleGraphCacheArtifact, ModuleIdentifier, ModuleIdsArtifact, ModuleStaticCacheArtifact, PathData, ResolverFactory, RuntimeGlobals, RuntimeKeyMap, RuntimeMode, RuntimeModule, RuntimeSpec, RuntimeSpecMap, RuntimeTemplate, SharedPluginDriver, SideEffectsOptimizeArtifact, SourceType, Stats, ValueCacheVersions, build_chunk_graph::artifact::BuildChunkGraphArtifact, compilation::build_module_graph::{ - BuildModuleGraphArtifact, ModuleExecutor, UpdateParam, build_module_graph, - finish_build_module_graph, update_module_graph, + BuildModuleGraphArtifact, ModuleExecutor, UpdateParam, update_module_graph, }, compiler::{CompilationRecords, CompilerId}, get_runtime_key, @@ -163,49 +184,6 @@ static COMPILATION_ID: AtomicU32 = AtomicU32::new(0); /// Use macro to prevent cargo shear from failing and reporting errors /// due to the inability to parse the async closure syntax /// https://github.com/Boshen/cargo-shear/issues/143 -macro_rules! process_runtime_requirement_hook_macro { - ($name: ident, $s: ty, $c: ty) => { - async fn $name( - self: $s, - requirements: &mut RuntimeGlobals, - call_hook: impl for<'a> Fn( - $c, - &'a RuntimeGlobals, - &'a RuntimeGlobals, - &'a mut RuntimeGlobals, - ) -> BoxFuture<'a, Result<()>>, - ) -> Result<()> { - let mut runtime_requirements_mut = *requirements; - let mut runtime_requirements; - - loop { - runtime_requirements = runtime_requirements_mut; - runtime_requirements_mut = RuntimeGlobals::default(); - // runtime_requirements: rt_requirements of last time - // runtime_requirements_mut: changed rt_requirements - // requirements: all rt_requirements - call_hook( - self, - requirements, - &runtime_requirements, - &mut runtime_requirements_mut, - ) - .await?; - - // check if we have changes to runtime_requirements - runtime_requirements_mut = - runtime_requirements_mut.difference(requirements.intersection(runtime_requirements_mut)); - if runtime_requirements_mut.is_empty() { - break; - } else { - requirements.insert(runtime_requirements_mut); - } - } - Ok(()) - } - }; -} - #[derive(Debug)] pub struct Compilation { /// get_compilation_hooks(compilation.id) @@ -1016,25 +994,6 @@ impl Compilation { ukey } - #[instrument("Compilation:build_module_graph",target=TRACING_BENCH_TARGET, skip_all)] - pub async fn build_module_graph(&mut self) -> Result<()> { - // run module_executor - if let Some(module_executor) = &mut self.module_executor { - let mut module_executor = std::mem::take(module_executor); - module_executor.hook_before_make(self).await?; - self.module_executor = Some(module_executor); - } - - let artifact = self.build_module_graph_artifact.take(); - self - .build_module_graph_artifact - .replace(build_module_graph(self, artifact).await?); - - self.in_finish_make.store(true, Ordering::Release); - - Ok(()) - } - pub async fn rebuild_module( &mut self, module_identifiers: IdentifierSet, @@ -1061,372 +1020,6 @@ impl Compilation { .collect::>())) } - #[instrument("Compilation:code_generation",target=TRACING_BENCH_TARGET, skip_all)] - async fn code_generation(&mut self, modules: IdentifierSet) -> Result<()> { - let logger = self.get_logger("rspack.Compilation"); - let mut codegen_cache_counter = match self.options.cache { - CacheOptions::Disabled => None, - _ => Some(logger.cache("module code generation cache")), - }; - - let module_graph = self.get_module_graph(); - let mut no_codegen_dependencies_modules = IdentifierSet::default(); - let mut has_codegen_dependencies_modules = IdentifierSet::default(); - for module_identifier in modules { - let module = module_graph - .module_by_identifier(&module_identifier) - .expect("should have module"); - if module.get_code_generation_dependencies().is_none() { - no_codegen_dependencies_modules.insert(module_identifier); - } else { - has_codegen_dependencies_modules.insert(module_identifier); - } - } - - self - .code_generation_modules(&mut codegen_cache_counter, no_codegen_dependencies_modules) - .await?; - self - .code_generation_modules(&mut codegen_cache_counter, has_codegen_dependencies_modules) - .await?; - - if let Some(counter) = codegen_cache_counter { - logger.cache_end(counter); - } - - Ok(()) - } - - pub(crate) async fn code_generation_modules( - &mut self, - cache_counter: &mut Option, - modules: IdentifierSet, - ) -> Result<()> { - let chunk_graph = &self.chunk_graph; - let module_graph = self.get_module_graph(); - let mut jobs = Vec::new(); - for module in modules { - let mut map: HashMap = HashMap::default(); - for runtime in chunk_graph.get_module_runtimes_iter(module, &self.chunk_by_ukey) { - let hash = ChunkGraph::get_module_hash(self, module, runtime) - .expect("should have cgm.hash in code generation"); - let scope = self - .plugin_driver - .compilation_hooks - .concatenation_scope - .call(self, module) - .await?; - if let Some(job) = map.get_mut(hash) { - job.runtimes.push(runtime.clone()); - } else { - map.insert( - hash.clone(), - CodeGenerationJob { - module, - hash: hash.clone(), - runtime: runtime.clone(), - runtimes: vec![runtime.clone()], - scope, - }, - ); - } - } - jobs.extend(map.into_values()); - } - - let results = rspack_futures::scope::<_, _>(|token| { - jobs.into_iter().for_each(|job| { - // SAFETY: await immediately and trust caller to poll future entirely - let s = unsafe { token.used((&self, &module_graph, job)) }; - - s.spawn(|(this, module_graph, job)| async { - let options = &this.options; - let old_cache = &this.old_cache; - - let module = module_graph - .module_by_identifier(&job.module) - .expect("should have module"); - let codegen_res = old_cache - .code_generate_occasion - .use_cache(&job, || async { - module - .code_generation(this, Some(&job.runtime), job.scope.clone()) - .await - .map(|mut codegen_res| { - codegen_res.set_hash( - &options.output.hash_function, - &options.output.hash_digest, - &options.output.hash_salt, - ); - codegen_res - }) - }) - .await; - - (job.module, job.runtimes, codegen_res) - }) - }) - }) - .await; - let results = results - .into_iter() - .map(|res| res.to_rspack_result()) - .collect::>>()?; - - for (module, runtimes, (codegen_res, from_cache)) in results { - if let Some(counter) = cache_counter { - if from_cache { - counter.hit(); - } else { - counter.miss(); - } - } - let codegen_res = match codegen_res { - Ok(codegen_res) => codegen_res, - Err(err) => { - let mut diagnostic = Diagnostic::from(err); - diagnostic.module_identifier = Some(module); - self.push_diagnostic(diagnostic); - let mut codegen_res = CodeGenerationResult::default(); - codegen_res.set_hash( - &self.options.output.hash_function, - &self.options.output.hash_digest, - &self.options.output.hash_salt, - ); - codegen_res - } - }; - self - .code_generation_results - .insert(module, codegen_res, runtimes); - self.code_generated_modules.insert(module); - } - Ok(()) - } - - #[instrument("Compilation:create_module_assets",target=TRACING_BENCH_TARGET, skip_all)] - async fn create_module_assets(&mut self, _plugin_driver: SharedPluginDriver) { - let mut chunk_asset_map = vec![]; - let mut module_assets = vec![]; - let mg = self.get_module_graph(); - for (identifier, module) in mg.modules() { - let assets = &module.build_info().assets; - if assets.is_empty() { - continue; - } - - for (name, asset) in assets.as_ref() { - module_assets.push((name.clone(), asset.clone())); - } - // assets of executed modules are not in this compilation - if self - .chunk_graph - .chunk_graph_module_by_module_identifier - .contains_key(&identifier) - { - for chunk in self.chunk_graph.get_module_chunks(identifier).iter() { - for name in assets.keys() { - chunk_asset_map.push((*chunk, name.clone())) - } - } - } - } - - for (name, asset) in module_assets { - self.emit_asset(name, asset); - } - - for (chunk, asset_name) in chunk_asset_map { - let chunk = self.chunk_by_ukey.expect_get_mut(&chunk); - chunk.add_auxiliary_file(asset_name); - } - } - - #[instrument("Compilation::create_chunk_assets",target=TRACING_BENCH_TARGET, skip_all)] - async fn create_chunk_assets(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { - if (self.options.output.filename.has_hash_placeholder() - || self.options.output.chunk_filename.has_hash_placeholder() - || self.options.output.css_filename.has_hash_placeholder() - || self - .options - .output - .css_chunk_filename - .has_hash_placeholder()) - && let Some(diagnostic) = self.incremental.disable_passes( - IncrementalPasses::CHUNKS_RENDER, - "Chunk filename that dependent on full hash", - "chunk filename that dependent on full hash is not supported in incremental compilation", - ) - && let Some(diagnostic) = diagnostic - { - self.push_diagnostic(diagnostic); - } - - // Check if CHUNKS_RENDER pass is disabled, and clear artifact if needed - if !self - .incremental - .passes_enabled(IncrementalPasses::CHUNKS_RENDER) - { - self.chunk_render_artifact.clear(); - } - - let chunks = if let Some(mutations) = self - .incremental - .mutations_read(IncrementalPasses::CHUNKS_RENDER) - && !self.chunk_render_artifact.is_empty() - { - let removed_chunks = mutations.iter().filter_map(|mutation| match mutation { - Mutation::ChunkRemove { chunk } => Some(*chunk), - _ => None, - }); - for removed_chunk in removed_chunks { - self.chunk_render_artifact.remove(&removed_chunk); - } - self - .chunk_render_artifact - .retain(|chunk, _| self.chunk_by_ukey.contains(chunk)); - let chunks: UkeySet = mutations - .iter() - .filter_map(|mutation| match mutation { - Mutation::ChunkSetHashes { chunk } => Some(*chunk), - _ => None, - }) - .collect(); - tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::CHUNKS_RENDER, %mutations); - let logger = self.get_logger("rspack.incremental.chunksRender"); - logger.log(format!( - "{} chunks are affected, {} in total", - chunks.len(), - self.chunk_by_ukey.len() - )); - chunks - } else { - self.chunk_by_ukey.keys().copied().collect() - }; - let results = rspack_futures::scope::<_, Result<_>>(|token| { - chunks.iter().for_each(|chunk| { - // SAFETY: await immediately and trust caller to poll future entirely - let s = unsafe { token.used((&self, &plugin_driver, chunk)) }; - - s.spawn(|(this, plugin_driver, chunk)| async { - let mut manifests = Vec::new(); - let mut diagnostics = Vec::new(); - plugin_driver - .compilation_hooks - .render_manifest - .call(this, chunk, &mut manifests, &mut diagnostics) - .await?; - - rspack_error::Result::Ok(( - *chunk, - ChunkRenderResult { - manifests, - diagnostics, - }, - )) - }); - }) - }) - .await; - - let mut chunk_render_results: UkeyMap = Default::default(); - for result in results { - let item = result.to_rspack_result()?; - let (key, value) = item?; - chunk_render_results.insert(key, value); - } - let chunk_ukey_and_manifest = if self - .incremental - .passes_enabled(IncrementalPasses::CHUNKS_RENDER) - { - self.chunk_render_artifact.extend(chunk_render_results); - self.chunk_render_artifact.clone() - } else { - chunk_render_results - }; - - for ( - chunk_ukey, - ChunkRenderResult { - manifests, - diagnostics, - }, - ) in chunk_ukey_and_manifest - { - self.extend_diagnostics(diagnostics); - - for file_manifest in manifests { - let filename = file_manifest.filename; - let current_chunk = self.chunk_by_ukey.expect_get_mut(&chunk_ukey); - - current_chunk.set_rendered(true); - if file_manifest.auxiliary { - current_chunk.add_auxiliary_file(filename.clone()); - } else { - current_chunk.add_file(filename.clone()); - } - - self.emit_asset( - filename.clone(), - CompilationAsset::new(Some(file_manifest.source), file_manifest.info), - ); - - _ = self - .chunk_asset(chunk_ukey, &filename, plugin_driver.clone()) - .await; - } - } - - Ok(()) - } - - #[instrument("Compilation:process_assets",target=TRACING_BENCH_TARGET, skip_all)] - async fn process_assets(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { - plugin_driver - .compilation_hooks - .process_assets - .call(self) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.processAssets")) - } - - #[instrument("Compilation:after_process_assets", skip_all)] - async fn after_process_assets(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { - let mut diagnostics: Vec = vec![]; - - let res = plugin_driver - .compilation_hooks - .after_process_assets - .call(self, &mut diagnostics) - .await; - - self.extend_diagnostics(diagnostics); - res - } - - #[instrument("Compilation:after_seal", target=TRACING_BENCH_TARGET,skip_all)] - async fn after_seal(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { - plugin_driver.compilation_hooks.after_seal.call(self).await - } - - // #[instrument( - // name = "Compilation:chunk_asset", - // skip(self, plugin_driver, chunk_ukey) - // )] - async fn chunk_asset( - &self, - chunk_ukey: ChunkUkey, - filename: &str, - plugin_driver: SharedPluginDriver, - ) -> Result<()> { - plugin_driver - .compilation_hooks - .chunk_asset - .call(self, &chunk_ukey, filename) - .await?; - Ok(()) - } - pub fn entry_modules(&self) -> IdentifierSet { let module_graph = self.get_module_graph(); self @@ -1454,660 +1047,6 @@ impl Compilation { self.chunk_group_by_ukey.expect_get_mut(ukey) } - #[instrument("Compilation:finish",target=TRACING_BENCH_TARGET, skip_all)] - pub async fn finish_build_module_graph(&mut self) -> Result<()> { - self.in_finish_make.store(false, Ordering::Release); - // clean up the entry deps - let make_artifact = self.build_module_graph_artifact.take(); - self - .build_module_graph_artifact - .replace(finish_build_module_graph(self, make_artifact).await?); - // sync assets to module graph from module_executor - if let Some(module_executor) = &mut self.module_executor { - let mut module_executor = std::mem::take(module_executor); - module_executor.hook_after_finish_modules(self).await?; - self.module_executor = Some(module_executor); - } - // make finished, make artifact should be readonly thereafter. - Ok(()) - } - // collect build module graph effects for incremental compilation - #[tracing::instrument("Compilation:collect_build_module_graph_effects", skip_all)] - pub async fn collect_build_module_graph_effects( - &mut self, - dependencies_diagnostics_artifact: &mut DependenciesDiagnosticsArtifact, - async_modules_artifact: &mut AsyncModulesArtifact, - ) -> Result> { - let logger = self.get_logger("rspack.Compilation"); - if let Some(mut mutations) = self.incremental.mutations_write() { - mutations.extend( - self - .build_module_graph_artifact - .affected_dependencies - .updated() - .iter() - .map(|&dependency| Mutation::DependencyUpdate { dependency }), - ); - mutations.extend( - self - .build_module_graph_artifact - .affected_modules - .removed() - .iter() - .map(|&module| Mutation::ModuleRemove { module }), - ); - mutations.extend( - self - .build_module_graph_artifact - .affected_modules - .updated() - .iter() - .map(|&module| Mutation::ModuleUpdate { module }), - ); - mutations.extend( - self - .build_module_graph_artifact - .affected_modules - .added() - .iter() - .map(|&module| Mutation::ModuleAdd { module }), - ); - tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::MAKE, %mutations); - } - - let start = logger.time("finish modules"); - // finish_modules means the module graph (modules, connections, dependencies) are - // frozen and start to optimize (provided exports, infer async, etc.) based on the - // module graph, so any kind of change that affect these should be done before the - // finish_modules - self - .plugin_driver - .clone() - .compilation_hooks - .finish_modules - .call(self, async_modules_artifact) - .await?; - - logger.time_end(start); - - // https://github.com/webpack/webpack/blob/19ca74127f7668aaf60d59f4af8fcaee7924541a/lib/Compilation.js#L2988 - self.module_graph_cache_artifact.freeze(); - // Collect dependencies diagnostics at here to make sure: - // 1. after finish_modules: has provide exports info - // 2. before optimize dependencies: side effects free module hasn't been skipped - let mut all_diagnostics = - self.collect_dependencies_diagnostics(dependencies_diagnostics_artifact); - self.module_graph_cache_artifact.unfreeze(); - - // take make diagnostics - let diagnostics = self.build_module_graph_artifact.diagnostics(); - all_diagnostics.extend(diagnostics); - Ok(all_diagnostics) - } - #[tracing::instrument("Compilation:collect_dependencies_diagnostics", skip_all)] - fn collect_dependencies_diagnostics( - &self, - dependencies_diagnostics_artifact: &mut DependenciesDiagnosticsArtifact, - ) -> Vec { - // Compute modules while holding the lock, then release it - let (modules, has_mutations) = { - let mutations = self - .incremental - .mutations_read(IncrementalPasses::DEPENDENCIES_DIAGNOSTICS); - - // TODO move diagnostic collect to make - if let Some(mutations) = mutations { - if !dependencies_diagnostics_artifact.is_empty() { - let revoked_modules = mutations.iter().filter_map(|mutation| match mutation { - Mutation::ModuleRemove { module } => Some(*module), - _ => None, - }); - for revoked_module in revoked_modules { - dependencies_diagnostics_artifact.remove(&revoked_module); - } - let modules = mutations.get_affected_modules_with_module_graph(self.get_module_graph()); - let logger = self.get_logger("rspack.incremental.dependenciesDiagnostics"); - logger.log(format!( - "{} modules are affected, {} in total", - modules.len(), - self.get_module_graph().modules().len() - )); - (modules, true) - } else { - ( - self.get_module_graph().modules().keys().copied().collect(), - true, - ) - } - } else { - ( - self.get_module_graph().modules().keys().copied().collect(), - false, - ) - } - }; - - let module_graph = self.get_module_graph(); - let module_graph_cache = &self.module_graph_cache_artifact; - let dependencies_diagnostics: DependenciesDiagnosticsArtifact = modules - .par_iter() - .map(|module_identifier| { - let mgm = module_graph - .module_graph_module_by_identifier(module_identifier) - .expect("should have mgm"); - let diagnostics = mgm - .all_dependencies - .iter() - .filter_map(|dependency_id| { - let dependency = module_graph.dependency_by_id(dependency_id); - dependency - .get_diagnostics(module_graph, module_graph_cache) - .map(|diagnostics| { - diagnostics.into_iter().map(|mut diagnostic| { - diagnostic.module_identifier = Some(*module_identifier); - diagnostic.loc = dependency.loc(); - diagnostic - }) - }) - }) - .flatten() - .collect::>(); - (*module_identifier, diagnostics) - }) - .collect(); - let all_modules_diagnostics = if has_mutations { - dependencies_diagnostics_artifact.extend(dependencies_diagnostics); - dependencies_diagnostics_artifact.clone() - } else { - dependencies_diagnostics - }; - all_modules_diagnostics.into_values().flatten().collect() - } - - #[instrument("Compilation:seal", skip_all)] - pub async fn seal(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { - // add a checkpoint here since we may modify module graph later in incremental compilation - // and we can recover to this checkpoint in the future - if self.incremental.passes_enabled(IncrementalPasses::MAKE) { - self.build_module_graph_artifact.module_graph.checkpoint(); - } - - if !self.options.mode.is_development() { - self.module_static_cache_artifact.freeze(); - } - - let logger = self.get_logger("rspack.Compilation"); - - // https://github.com/webpack/webpack/blob/main/lib/Compilation.js#L2809 - plugin_driver - .compilation_hooks - .seal - .call(self) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.seal"))?; - - let start = logger.time("optimize dependencies"); - // https://github.com/webpack/webpack/blob/d15c73469fd71cf98734685225250148b68ddc79/lib/Compilation.js#L2812-L2814 - - let mut diagnostics: Vec = vec![]; - let mut side_effects_optimize_artifact = self.side_effects_optimize_artifact.take(); - let mut build_module_graph_artifact = self.build_module_graph_artifact.take(); - while matches!( - plugin_driver - .compilation_hooks - .optimize_dependencies - .call( - self, - &mut side_effects_optimize_artifact, - &mut build_module_graph_artifact, - &mut diagnostics - ) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeDependencies"))?, - Some(true) - ) {} - self - .side_effects_optimize_artifact - .replace(side_effects_optimize_artifact); - self - .build_module_graph_artifact - .replace(build_module_graph_artifact); - self.extend_diagnostics(diagnostics); - - logger.time_end(start); - - // ModuleGraph is frozen for now on, we have a module graph that won't change - // so now we can start to create a chunk graph based on the module graph - - let start = logger.time("create chunks"); - self.module_graph_cache_artifact.freeze(); - use_code_splitting_cache(self, |compilation| async { - let start = logger.time("rebuild chunk graph"); - build_chunk_graph(compilation)?; - compilation - .chunk_graph - .generate_dot(compilation, "after-code-splitting") - .await; - logger.time_end(start); - Ok(compilation) - }) - .await?; - - let mut diagnostics = vec![]; - while matches!( - plugin_driver - .compilation_hooks - .optimize_modules - .call(self, &mut diagnostics) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeModules"))?, - Some(true) - ) {} - self.extend_diagnostics(diagnostics); - - plugin_driver - .compilation_hooks - .after_optimize_modules - .call(self) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.afterOptimizeModules"))?; - - while matches!( - plugin_driver - .compilation_hooks - .optimize_chunks - .call(self) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeChunks"))?, - Some(true) - ) {} - - logger.time_end(start); - - let start = logger.time("optimize"); - plugin_driver - .compilation_hooks - .optimize_tree - .call(self) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeTree"))?; - - plugin_driver - .compilation_hooks - .optimize_chunk_modules - .call(self) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeChunkModules"))?; - logger.time_end(start); - - // ChunkGraph is frozen for now on, we have a chunk graph that won't change - // so now we can start to generate assets based on the chunk graph - - let start = logger.time("module ids"); - - // Check if MODULE_IDS pass is disabled, and clear artifact if needed - if !self - .incremental - .passes_enabled(IncrementalPasses::MODULE_IDS) - { - self.module_ids_artifact.clear(); - } - - let mut diagnostics = vec![]; - let mut module_ids_artifact = mem::take(&mut self.module_ids_artifact); - plugin_driver - .compilation_hooks - .module_ids - .call(self, &mut module_ids_artifact, &mut diagnostics) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.moduleIds"))?; - self.module_ids_artifact = module_ids_artifact; - self.extend_diagnostics(diagnostics); - logger.time_end(start); - - let start = logger.time("chunk ids"); - - // Check if CHUNK_IDS pass is disabled, and clear artifact if needed - if !self - .incremental - .passes_enabled(IncrementalPasses::CHUNK_IDS) - { - self.named_chunk_ids_artifact.clear(); - } - - let mut diagnostics = vec![]; - let mut chunk_by_ukey = mem::take(&mut self.chunk_by_ukey); - let mut named_chunk_ids_artifact = mem::take(&mut self.named_chunk_ids_artifact); - plugin_driver - .compilation_hooks - .chunk_ids - .call( - self, - &mut chunk_by_ukey, - &mut named_chunk_ids_artifact, - &mut diagnostics, - ) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.chunkIds"))?; - self.chunk_by_ukey = chunk_by_ukey; - self.named_chunk_ids_artifact = named_chunk_ids_artifact; - self.extend_diagnostics(diagnostics); - logger.time_end(start); - - self.assign_runtime_ids(); - - let start = logger.time("optimize code generation"); - plugin_driver - .compilation_hooks - .optimize_code_generation - .call(self) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeCodeGeneration"))?; - logger.time_end(start); - - // Check if MODULES_HASHES pass is disabled, and clear artifact if needed - if !self - .incremental - .passes_enabled(IncrementalPasses::MODULES_HASHES) - { - self.cgm_hash_artifact.clear(); - } - - let create_module_hashes_modules = if let Some(mutations) = self - .incremental - .mutations_read(IncrementalPasses::MODULES_HASHES) - && !self.cgm_hash_artifact.is_empty() - { - let revoked_modules = mutations.iter().filter_map(|mutation| match mutation { - Mutation::ModuleRemove { module } => Some(*module), - _ => None, - }); - for revoked_module in revoked_modules { - self.cgm_hash_artifact.remove(&revoked_module); - } - let mut modules = mutations.get_affected_modules_with_chunk_graph(self); - - // check if module runtime changes - let mg = self.get_module_graph(); - for mi in mg.modules().keys() { - let module_runtimes = self - .chunk_graph - .get_module_runtimes(*mi, &self.chunk_by_ukey); - let module_runtime_keys = module_runtimes - .values() - .map(get_runtime_key) - .collect::>(); - - if let Some(runtime_map) = self.cgm_hash_artifact.get_runtime_map(mi) { - if module_runtimes.is_empty() { - // module has no runtime, skip - continue; - } - if module_runtimes.len() == 1 { - // single runtime - if !matches!(runtime_map.mode, RuntimeMode::SingleEntry) - || runtime_map - .single_runtime - .as_ref() - .expect("should have single runtime for single entry") - != module_runtimes - .values() - .next() - .expect("should have at least one runtime") - { - modules.insert(*mi); - } - } else { - // multiple runtimes - if matches!(runtime_map.mode, RuntimeMode::SingleEntry) { - modules.insert(*mi); - continue; - } - - if runtime_map.map.len() != module_runtimes.len() { - modules.insert(*mi); - continue; - } - - for runtime_key in runtime_map.map.keys() { - if !module_runtime_keys.contains(runtime_key) { - modules.insert(*mi); - break; - } - } - } - } - } - - tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::MODULES_HASHES, %mutations, ?modules); - let logger = self.get_logger("rspack.incremental.modulesHashes"); - logger.log(format!( - "{} modules are affected, {} in total", - modules.len(), - mg.modules().len() - )); - - modules - } else { - self.get_module_graph().modules().keys().copied().collect() - }; - self - .create_module_hashes(create_module_hashes_modules) - .await?; - - let start = logger.time("code generation"); - let code_generation_modules = if let Some(mutations) = self - .incremental - .mutations_read(IncrementalPasses::MODULES_CODEGEN) - && !self.code_generation_results.is_empty() - { - let revoked_modules = mutations.iter().filter_map(|mutation| match mutation { - Mutation::ModuleRemove { module } => Some(*module), - _ => None, - }); - for revoked_module in revoked_modules { - self.code_generation_results.remove(&revoked_module); - } - let modules: IdentifierSet = mutations - .iter() - .filter_map(|mutation| match mutation { - Mutation::ModuleSetHashes { module } => Some(*module), - _ => None, - }) - .collect(); - // also cleanup for updated modules, for `insert(); insert();` the second insert() won't override the first insert() on code_generation_results - for module in &modules { - self.code_generation_results.remove(module); - } - tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::MODULES_CODEGEN, %mutations); - let logger = self.get_logger("rspack.incremental.modulesCodegen"); - logger.log(format!( - "{} modules are affected, {} in total", - modules.len(), - self.get_module_graph().modules().len() - )); - modules - } else { - self.code_generation_results = Default::default(); - self.get_module_graph().modules().keys().copied().collect() - }; - self.code_generation(code_generation_modules).await?; - - let mut diagnostics = vec![]; - plugin_driver - .compilation_hooks - .after_code_generation - .call(self, &mut diagnostics) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.afterCodeGeneration"))?; - self.extend_diagnostics(diagnostics); - - logger.time_end(start); - - let start = logger.time("runtime requirements"); - let process_runtime_requirements_modules = if let Some(mutations) = self - .incremental - .mutations_read(IncrementalPasses::MODULES_RUNTIME_REQUIREMENTS) - && !self.cgm_runtime_requirements_artifact.is_empty() - { - let revoked_modules = mutations.iter().filter_map(|mutation| match mutation { - Mutation::ModuleRemove { module } => Some(*module), - _ => None, - }); - for revoked_module in revoked_modules { - self - .cgm_runtime_requirements_artifact - .remove(&revoked_module); - } - let modules: IdentifierSet = mutations - .iter() - .filter_map(|mutation| match mutation { - Mutation::ModuleSetHashes { module } => Some(*module), - _ => None, - }) - .collect(); - let logger = self.get_logger("rspack.incremental.modulesRuntimeRequirements"); - logger.log(format!( - "{} modules are affected, {} in total", - modules.len(), - self.get_module_graph().modules().len() - )); - modules - } else { - self.cgm_runtime_requirements_artifact = Default::default(); - self.get_module_graph().modules().keys().copied().collect() - }; - self - .process_modules_runtime_requirements( - process_runtime_requirements_modules, - plugin_driver.clone(), - ) - .await?; - let runtime_chunks = self.get_chunk_graph_entries().collect(); - - // Check if CHUNKS_RUNTIME_REQUIREMENTS pass is disabled, and clear artifact if needed - if !self - .incremental - .passes_enabled(IncrementalPasses::CHUNKS_RUNTIME_REQUIREMENTS) - { - self.cgc_runtime_requirements_artifact.clear(); - } - - let process_runtime_requirements_chunks = if let Some(mutations) = self - .incremental - .mutations_read(IncrementalPasses::CHUNKS_RUNTIME_REQUIREMENTS) - && !self.cgc_runtime_requirements_artifact.is_empty() - { - let removed_chunks = mutations.iter().filter_map(|mutation| match mutation { - Mutation::ChunkRemove { chunk } => Some(chunk), - _ => None, - }); - for removed_chunk in removed_chunks { - self.cgc_runtime_requirements_artifact.remove(removed_chunk); - } - let affected_chunks = mutations.get_affected_chunks_with_chunk_graph(self); - for affected_chunk in &affected_chunks { - self - .cgc_runtime_requirements_artifact - .remove(affected_chunk); - } - for runtime_chunk in &runtime_chunks { - self.cgc_runtime_requirements_artifact.remove(runtime_chunk); - } - self - .cgc_runtime_requirements_artifact - .retain(|chunk, _| self.chunk_by_ukey.contains(chunk)); - let logger = self.get_logger("rspack.incremental.chunksRuntimeRequirements"); - logger.log(format!( - "{} chunks are affected, {} in total", - affected_chunks.len(), - self.chunk_by_ukey.len() - )); - affected_chunks - } else { - self.chunk_by_ukey.keys().copied().collect() - }; - self - .process_chunks_runtime_requirements( - process_runtime_requirements_chunks, - runtime_chunks, - plugin_driver.clone(), - ) - .await?; - logger.time_end(start); - - let start = logger.time("hashing"); - self.create_hash(plugin_driver.clone()).await?; - self.runtime_modules_code_generation().await?; - logger.time_end(start); - - let start = logger.time("create module assets"); - self.create_module_assets(plugin_driver.clone()).await; - logger.time_end(start); - - let start = logger.time("create chunk assets"); - self.create_chunk_assets(plugin_driver.clone()).await?; - logger.time_end(start); - - let start = logger.time("process assets"); - self.process_assets(plugin_driver.clone()).await?; - logger.time_end(start); - - let start = logger.time("after process assets"); - self.after_process_assets(plugin_driver.clone()).await?; - logger.time_end(start); - - let start = logger.time("after seal"); - self.after_seal(plugin_driver).await?; - logger.time_end(start); - - if !self.options.mode.is_development() { - self.module_static_cache_artifact.unfreeze(); - } - Ok(()) - } - - pub fn assign_runtime_ids(&mut self) { - fn process_entrypoint( - entrypoint_ukey: &ChunkGroupUkey, - chunk_group_by_ukey: &ChunkGroupByUkey, - chunk_by_ukey: &ChunkByUkey, - chunk_graph: &mut ChunkGraph, - ) { - let entrypoint = chunk_group_by_ukey.expect_get(entrypoint_ukey); - let runtime = entrypoint - .kind - .get_entry_options() - .and_then(|o| match &o.runtime { - Some(EntryRuntime::String(s)) => Some(s.to_owned()), - _ => None, - }) - .or(entrypoint.name().map(|n| n.to_string())); - if let (Some(runtime), Some(chunk)) = ( - runtime, - chunk_by_ukey.get(&entrypoint.get_runtime_chunk(chunk_group_by_ukey)), - ) { - chunk_graph.set_runtime_id(runtime, chunk.id().map(|id| id.to_string())); - } - } - for i in self.entrypoints.iter() { - process_entrypoint( - i.1, - &self.chunk_group_by_ukey, - &self.chunk_by_ukey, - &mut self.chunk_graph, - ) - } - for i in self.async_entrypoints.iter() { - process_entrypoint( - i, - &self.chunk_group_by_ukey, - &self.chunk_by_ukey, - &mut self.chunk_graph, - ) - } - } - pub fn get_chunk_graph_entries(&self) -> impl Iterator + use<'_> { let entries = self.entrypoints.values().map(|entrypoint_ukey| { let entrypoint = self.chunk_group_by_ukey.expect_get(entrypoint_ukey); @@ -2119,727 +1058,6 @@ impl Compilation { }); entries.chain(async_entries) } - - #[instrument("Compilation:process_modules_runtime_requirements", skip_all)] - pub async fn process_modules_runtime_requirements( - &mut self, - modules: IdentifierSet, - plugin_driver: SharedPluginDriver, - ) -> Result<()> { - let logger = self.get_logger("rspack.Compilation"); - let start = logger.time("runtime requirements.modules"); - - let module_results = rspack_futures::scope::<_, Result<_>>(|token| { - modules - .into_iter() - .filter(|module| self.chunk_graph.get_number_of_module_chunks(*module) > 0) - .for_each(|module| { - let s = unsafe { token.used((&self, &plugin_driver)) }; - s.spawn(move |(compilation, plugin_driver)| async move { - let mut map = RuntimeSpecMap::new(); - let runtimes = compilation - .chunk_graph - .get_module_runtimes_iter(module, &compilation.chunk_by_ukey); - for runtime in runtimes { - let runtime_requirements = compilation - .old_cache - .process_runtime_requirements_occasion - .use_cache(module, runtime, compilation, || async { - let mut runtime_requirements = compilation - .code_generation_results - .get_runtime_requirements(&module, Some(runtime)); - - plugin_driver - .compilation_hooks - .additional_module_runtime_requirements - .call(compilation, &module, &mut runtime_requirements) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.additionalModuleRuntimeRequirements"))?; - - compilation - .process_runtime_requirement_hook(&mut runtime_requirements, { - let plugin_driver = plugin_driver.clone(); - move |compilation, - all_runtime_requirements, - runtime_requirements, - runtime_requirements_mut| { - Box::pin({ - let plugin_driver = plugin_driver.clone(); - async move { - plugin_driver - .compilation_hooks - .runtime_requirement_in_module - .call( - compilation, - &module, - all_runtime_requirements, - runtime_requirements, - runtime_requirements_mut, - ) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.runtimeRequirementInModule"))?; - Ok(()) - }}) - } - }) - .await?; - Ok(runtime_requirements) - }) - .await?; - map.set(runtime.clone(), runtime_requirements); - } - Ok((module, map)) - }); - }); - }) - .await - .into_iter() - .map(|r| r.to_rspack_result()) - .collect::>>()?; - - for entry in module_results { - let (module, map) = entry?; - ChunkGraph::set_module_runtime_requirements(self, module, map); - } - logger.time_end(start); - Ok(()) - } - - #[instrument(name = "Compilation:process_chunks_runtime_requirements", target=TRACING_BENCH_TARGET skip_all)] - pub async fn process_chunks_runtime_requirements( - &mut self, - chunks: UkeySet, - entries: UkeySet, - plugin_driver: SharedPluginDriver, - ) -> Result<()> { - let logger = self.get_logger("rspack.Compilation"); - let start = logger.time("runtime requirements.chunks"); - let chunk_requirements = chunks - .iter() - .chain(entries.iter()) - .par_bridge() - .map(|chunk_ukey| { - let mut set = RuntimeGlobals::default(); - for mid in self.chunk_graph.get_chunk_modules_identifier(chunk_ukey) { - let chunk = self.chunk_by_ukey.expect_get(chunk_ukey); - if let Some(runtime_requirements) = - ChunkGraph::get_module_runtime_requirements(self, *mid, chunk.runtime()) - { - set.insert(*runtime_requirements); - } - } - - (*chunk_ukey, set) - }) - .collect::>(); - - for (chunk_ukey, mut set) in chunk_requirements { - plugin_driver - .compilation_hooks - .additional_chunk_runtime_requirements - .call(self, &chunk_ukey, &mut set) - .await - .map_err(|e| { - e.wrap_err("caused by plugins in Compilation.hooks.additionalChunkRuntimeRequirements") - })?; - - self - .process_runtime_requirement_hook_mut(&mut set, { - let plugin_driver = plugin_driver.clone(); - move |compilation, - all_runtime_requirements, - runtime_requirements, - runtime_requirements_mut| { - Box::pin({ - let plugin_driver = plugin_driver.clone(); - async move { - plugin_driver - .compilation_hooks - .runtime_requirement_in_chunk - .call( - compilation, - &chunk_ukey, - all_runtime_requirements, - runtime_requirements, - runtime_requirements_mut, - ) - .await - .map_err(|e| { - e.wrap_err("caused by plugins in Compilation.hooks.runtimeRequirementInChunk") - })?; - Ok(()) - } - }) - } - }) - .await?; - - ChunkGraph::set_chunk_runtime_requirements(self, chunk_ukey, set); - } - logger.time_end(start); - - let start = logger.time("runtime requirements.entries"); - for &entry_ukey in &entries { - let entry = self.chunk_by_ukey.expect_get(&entry_ukey); - let mut set = RuntimeGlobals::default(); - for chunk_ukey in entry - .get_all_referenced_chunks(&self.chunk_group_by_ukey) - .iter() - { - let runtime_requirements = ChunkGraph::get_chunk_runtime_requirements(self, chunk_ukey); - set.insert(*runtime_requirements); - } - - plugin_driver - .compilation_hooks - .additional_tree_runtime_requirements - .call(self, &entry_ukey, &mut set) - .await - .map_err(|e| { - e.wrap_err("caused by plugins in Compilation.hooks.additionalTreeRuntimeRequirements") - })?; - - self - .process_runtime_requirement_hook_mut(&mut set, { - let plugin_driver = plugin_driver.clone(); - move |compilation, - all_runtime_requirements, - runtime_requirements, - runtime_requirements_mut| { - Box::pin({ - let plugin_driver = plugin_driver.clone(); - async move { - plugin_driver - .compilation_hooks - .runtime_requirement_in_tree - .call( - compilation, - &entry_ukey, - all_runtime_requirements, - runtime_requirements, - runtime_requirements_mut, - ) - .await - .map_err(|e| { - e.wrap_err("caused by plugins in Compilation.hooks.runtimeRequirementInTree") - })?; - Ok(()) - } - }) - } - }) - .await?; - - ChunkGraph::set_tree_runtime_requirements(self, entry_ukey, set); - } - - // NOTE: webpack runs hooks.runtime_module in compilation.add_runtime_module - // and overwrite the runtime_module.generate() to get new source in create_chunk_assets - // this needs full runtime requirements, so run hooks.runtime_module after runtime_requirements_in_tree - let mut runtime_modules = mem::take(&mut self.runtime_modules); - for entry_ukey in &entries { - let runtime_module_ids: Vec<_> = self - .chunk_graph - .get_chunk_runtime_modules_iterable(entry_ukey) - .copied() - .collect(); - for runtime_module_id in runtime_module_ids { - plugin_driver - .compilation_hooks - .runtime_module - .call(self, &runtime_module_id, entry_ukey, &mut runtime_modules) - .await - .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.runtimeModule"))?; - } - } - self.runtime_modules = runtime_modules; - - logger.time_end(start); - Ok(()) - } - - process_runtime_requirement_hook_macro!( - process_runtime_requirement_hook, - &Compilation, - &'a Compilation - ); - process_runtime_requirement_hook_macro!( - process_runtime_requirement_hook_mut, - &mut Compilation, - &'a mut Compilation - ); - - #[instrument(name = "Compilation:create_hash",target=TRACING_BENCH_TARGET, skip_all)] - pub async fn create_hash(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { - let logger = self.get_logger("rspack.Compilation"); - - // Check if there are any chunks that depend on full hash, usually only runtime chunks are - // possible to depend on full hash, but for library type commonjs/module, it's possible to - // have non-runtime chunks depend on full hash, the library format plugin is using - // dependent_full_hash hook to declare it. - let mut full_hash_chunks = UkeySet::default(); - for chunk_ukey in self.chunk_by_ukey.keys() { - let chunk_dependent_full_hash = plugin_driver - .compilation_hooks - .dependent_full_hash - .call(self, chunk_ukey) - .await? - .unwrap_or_default(); - if chunk_dependent_full_hash { - full_hash_chunks.insert(*chunk_ukey); - } - } - if !full_hash_chunks.is_empty() - && let Some(diagnostic) = self.incremental.disable_passes( - IncrementalPasses::CHUNKS_HASHES, - "Chunk content that dependent on full hash", - "it requires calculating the hashes of all the chunks, which is a global effect", - ) - && let Some(diagnostic) = diagnostic - { - self.push_diagnostic(diagnostic); - } - if !self - .incremental - .passes_enabled(IncrementalPasses::CHUNKS_HASHES) - { - self.chunk_hashes_artifact.clear(); - } - - let create_hash_chunks = if let Some(mutations) = self - .incremental - .mutations_read(IncrementalPasses::CHUNKS_HASHES) - && !self.chunk_hashes_artifact.is_empty() - { - let removed_chunks = mutations.iter().filter_map(|mutation| match mutation { - Mutation::ChunkRemove { chunk } => Some(*chunk), - _ => None, - }); - for removed_chunk in removed_chunks { - self.chunk_hashes_artifact.remove(&removed_chunk); - } - self - .chunk_hashes_artifact - .retain(|chunk, _| self.chunk_by_ukey.contains(chunk)); - let chunks = mutations.get_affected_chunks_with_chunk_graph(self); - tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::CHUNKS_HASHES, %mutations, ?chunks); - let logger = self.get_logger("rspack.incremental.chunksHashes"); - logger.log(format!( - "{} chunks are affected, {} in total", - chunks.len(), - self.chunk_by_ukey.len(), - )); - chunks - } else { - self.chunk_by_ukey.keys().copied().collect() - }; - - let mut compilation_hasher = RspackHash::from(&self.options.output); - - fn try_process_chunk_hash_results( - compilation: &mut Compilation, - chunk_hash_results: Vec>, - ) -> Result<()> { - for hash_result in chunk_hash_results { - let (chunk_ukey, chunk_hash_result) = hash_result?; - let chunk = compilation.chunk_by_ukey.expect_get(&chunk_ukey); - let chunk_hashes_changed = chunk.set_hashes( - &mut compilation.chunk_hashes_artifact, - chunk_hash_result.hash, - chunk_hash_result.content_hash, - ); - if chunk_hashes_changed - && let Some(mut mutations) = compilation.incremental.mutations_write() - { - mutations.add(Mutation::ChunkSetHashes { chunk: chunk_ukey }); - } - } - Ok(()) - } - - let unordered_runtime_chunks: UkeySet = self.get_chunk_graph_entries().collect(); - let start = logger.time("hashing: hash chunks"); - let other_chunks: Vec<_> = create_hash_chunks - .iter() - .filter(|key| !unordered_runtime_chunks.contains(key)) - .collect(); - - // create hash for runtime modules in other chunks - let other_chunk_runtime_module_hashes = rspack_futures::scope::<_, Result<_>>(|token| { - other_chunks - .iter() - .flat_map(|chunk| self.chunk_graph.get_chunk_runtime_modules_iterable(chunk)) - .for_each(|runtime_module_identifier| { - let s = unsafe { token.used((&self, runtime_module_identifier)) }; - s.spawn(|(compilation, runtime_module_identifier)| async { - let runtime_module = &compilation.runtime_modules[runtime_module_identifier]; - let digest = runtime_module.get_runtime_hash(compilation, None).await?; - Ok((*runtime_module_identifier, digest)) - }); - }) - }) - .await - .into_iter() - .map(|res| res.to_rspack_result()) - .collect::>>()?; - - for res in other_chunk_runtime_module_hashes { - let (runtime_module_identifier, digest) = res?; - self - .runtime_modules_hash - .insert(runtime_module_identifier, digest); - } - - // create hash for other chunks - let other_chunks_hash_results = rspack_futures::scope::<_, Result<_>>(|token| { - for chunk in other_chunks { - let s = unsafe { token.used((&self, chunk, &plugin_driver)) }; - s.spawn(|(compilation, chunk, plugin_driver)| async { - let hash_result = compilation - .process_chunk_hash(*chunk, plugin_driver) - .await?; - Ok((*chunk, hash_result)) - }); - } - }) - .await - .into_iter() - .map(|res| res.to_rspack_result()) - .collect::>>()?; - - try_process_chunk_hash_results(self, other_chunks_hash_results)?; - logger.time_end(start); - - // collect references for runtime chunks - let mut runtime_chunks_map: HashMap, u32)> = - unordered_runtime_chunks - .into_iter() - .map(|runtime_chunk| (runtime_chunk, (Vec::new(), 0))) - .collect(); - let mut remaining: u32 = 0; - for runtime_chunk_ukey in runtime_chunks_map.keys().copied().collect::>() { - let runtime_chunk = self.chunk_by_ukey.expect_get(&runtime_chunk_ukey); - let groups = runtime_chunk.get_all_referenced_async_entrypoints(&self.chunk_group_by_ukey); - for other in groups - .into_iter() - .map(|group| self.chunk_group_by_ukey.expect_get(&group)) - .map(|group| group.get_runtime_chunk(&self.chunk_group_by_ukey)) - { - let (other_referenced_by, _) = runtime_chunks_map - .get_mut(&other) - .expect("should in runtime_chunks_map"); - other_referenced_by.push(runtime_chunk_ukey); - let info = runtime_chunks_map - .get_mut(&runtime_chunk_ukey) - .expect("should in runtime_chunks_map"); - info.1 += 1; - remaining += 1; - } - } - // sort runtime chunks by its references - let mut runtime_chunks = Vec::with_capacity(runtime_chunks_map.len()); - for (runtime_chunk, (_, remaining)) in &runtime_chunks_map { - if *remaining == 0 { - runtime_chunks.push(*runtime_chunk); - } - } - let mut ready_chunks = Vec::new(); - - let mut i = 0; - while i < runtime_chunks.len() { - let chunk_ukey = runtime_chunks[i]; - let has_full_hash_modules = full_hash_chunks.contains(&chunk_ukey) - || self - .chunk_graph - .has_chunk_full_hash_modules(&chunk_ukey, &self.runtime_modules); - if has_full_hash_modules { - full_hash_chunks.insert(chunk_ukey); - } - let referenced_by = runtime_chunks_map - .get(&chunk_ukey) - .expect("should in runtime_chunks_map") - .0 - .clone(); - for other in referenced_by { - if has_full_hash_modules { - for runtime_module in self.chunk_graph.get_chunk_runtime_modules_iterable(&other) { - let runtime_module = self - .runtime_modules - .get(runtime_module) - .expect("should have runtime_module"); - if runtime_module.dependent_hash() { - full_hash_chunks.insert(other); - break; - } - } - } - remaining -= 1; - let (_, other_remaining) = runtime_chunks_map - .get_mut(&other) - .expect("should in runtime_chunks_map"); - *other_remaining -= 1; - if *other_remaining == 0 { - ready_chunks.push(other); - } - } - if !ready_chunks.is_empty() { - runtime_chunks.append(&mut ready_chunks); - } - i += 1; - } - // create warning for remaining circular references - if remaining > 0 { - let mut circular: Vec<_> = runtime_chunks_map - .iter() - .filter(|(_, (_, remaining))| *remaining != 0) - .map(|(chunk_ukey, _)| self.chunk_by_ukey.expect_get(chunk_ukey)) - .collect(); - circular.sort_unstable_by(|a, b| a.id().cmp(&b.id())); - runtime_chunks.extend(circular.iter().map(|chunk| chunk.ukey())); - let circular_names = circular - .iter() - .map(|chunk| { - chunk - .name() - .or(chunk.id().map(|id| id.as_str())) - .unwrap_or("no id chunk") - }) - .join(", "); - let error = rspack_error::Error::warning(format!( - "Circular dependency between chunks with runtime ({circular_names})\nThis prevents using hashes of each other and should be avoided." - )); - self.push_diagnostic(error.into()); - } - - // create hash for runtime chunks and the runtime modules within them - // The subsequent runtime chunks and runtime modules will depend on - // the hash results of the previous runtime chunks and runtime modules. - // Therefore, create hashes one by one in sequence. - let start = logger.time("hashing: hash runtime chunks"); - for runtime_chunk_ukey in runtime_chunks { - let runtime_module_hashes = rspack_futures::scope::<_, Result<_>>(|token| { - self - .chunk_graph - .get_chunk_runtime_modules_iterable(&runtime_chunk_ukey) - .for_each(|runtime_module_identifier| { - let s = unsafe { token.used((&self, runtime_module_identifier)) }; - s.spawn(|(compilation, runtime_module_identifier)| async { - let runtime_module = &compilation.runtime_modules[runtime_module_identifier]; - let digest = runtime_module.get_runtime_hash(compilation, None).await?; - Ok((*runtime_module_identifier, digest)) - }); - }) - }) - .await - .into_iter() - .map(|res| res.to_rspack_result()) - .collect::>>()?; - - for res in runtime_module_hashes { - let (mid, digest) = res?; - self.runtime_modules_hash.insert(mid, digest); - } - - let chunk_hash_result = self - .process_chunk_hash(runtime_chunk_ukey, &plugin_driver) - .await?; - let chunk = self.chunk_by_ukey.expect_get(&runtime_chunk_ukey); - let chunk_hashes_changed = chunk.set_hashes( - &mut self.chunk_hashes_artifact, - chunk_hash_result.hash, - chunk_hash_result.content_hash, - ); - if chunk_hashes_changed && let Some(mut mutations) = self.incremental.mutations_write() { - mutations.add(Mutation::ChunkSetHashes { - chunk: runtime_chunk_ukey, - }); - } - } - logger.time_end(start); - - // create full hash - self - .chunk_by_ukey - .values() - .sorted_unstable_by_key(|chunk| chunk.ukey()) - .filter_map(|chunk| chunk.hash(&self.chunk_hashes_artifact)) - .for_each(|hash| { - hash.hash(&mut compilation_hasher); - }); - self.hot_index.hash(&mut compilation_hasher); - self.hash = Some(compilation_hasher.digest(&self.options.output.hash_digest)); - - // re-create runtime chunk hash that depend on full hash - let start = logger.time("hashing: process full hash chunks"); - for chunk_ukey in full_hash_chunks { - for runtime_module_identifier in self - .chunk_graph - .get_chunk_runtime_modules_iterable(&chunk_ukey) - { - let runtime_module = &self.runtime_modules[runtime_module_identifier]; - if runtime_module.full_hash() || runtime_module.dependent_hash() { - let digest = runtime_module.get_runtime_hash(self, None).await?; - self - .runtime_modules_hash - .insert(*runtime_module_identifier, digest); - } - } - let chunk = self.chunk_by_ukey.expect_get(&chunk_ukey); - let new_chunk_hash = { - let chunk_hash = chunk - .hash(&self.chunk_hashes_artifact) - .expect("should have chunk hash"); - let mut hasher = RspackHash::from(&self.options.output); - chunk_hash.hash(&mut hasher); - self.hash.hash(&mut hasher); - hasher.digest(&self.options.output.hash_digest) - }; - let new_content_hash = { - let content_hash = chunk - .content_hash(&self.chunk_hashes_artifact) - .expect("should have content hash"); - content_hash - .iter() - .map(|(source_type, content_hash)| { - let mut hasher = RspackHash::from(&self.options.output); - content_hash.hash(&mut hasher); - self.hash.hash(&mut hasher); - ( - *source_type, - hasher.digest(&self.options.output.hash_digest), - ) - }) - .collect() - }; - let chunk_hashes_changed = chunk.set_hashes( - &mut self.chunk_hashes_artifact, - new_chunk_hash, - new_content_hash, - ); - if chunk_hashes_changed && let Some(mut mutations) = self.incremental.mutations_write() { - mutations.add(Mutation::ChunkSetHashes { chunk: chunk_ukey }); - } - } - logger.time_end(start); - Ok(()) - } - - #[instrument(skip_all)] - pub async fn runtime_modules_code_generation(&mut self) -> Result<()> { - let results = rspack_futures::scope::<_, Result<_>>(|token| { - self - .runtime_modules - .iter() - .for_each(|(runtime_module_identifier, runtime_module)| { - let s = unsafe { token.used((&self, runtime_module_identifier, runtime_module)) }; - s.spawn( - |(compilation, runtime_module_identifier, runtime_module)| async { - let result = runtime_module - .code_generation(compilation, None, None) - .await?; - let source = result - .get(&SourceType::Runtime) - .expect("should have source"); - Ok((*runtime_module_identifier, source.clone())) - }, - ) - }) - }) - .await - .into_iter() - .map(|res| res.to_rspack_result()) - .collect::>>()?; - - let mut runtime_module_sources = IdentifierMap::::default(); - for result in results { - let (runtime_module_identifier, source) = result?; - runtime_module_sources.insert(runtime_module_identifier, source); - } - - self.runtime_modules_code_generation_source = runtime_module_sources; - self - .code_generated_modules - .extend(self.runtime_modules.keys().copied()); - Ok(()) - } - - async fn process_chunk_hash( - &self, - chunk_ukey: ChunkUkey, - plugin_driver: &SharedPluginDriver, - ) -> Result { - let mut hasher = RspackHash::from(&self.options.output); - if let Some(chunk) = self.chunk_by_ukey.get(&chunk_ukey) { - chunk.update_hash(&mut hasher, self); - } - plugin_driver - .compilation_hooks - .chunk_hash - .call(self, &chunk_ukey, &mut hasher) - .await?; - let chunk_hash = hasher.digest(&self.options.output.hash_digest); - - let mut content_hashes: HashMap = HashMap::default(); - plugin_driver - .compilation_hooks - .content_hash - .call(self, &chunk_ukey, &mut content_hashes) - .await?; - - let content_hashes = content_hashes - .into_iter() - .map(|(t, mut hasher)| { - chunk_hash.hash(&mut hasher); - (t, hasher.digest(&self.options.output.hash_digest)) - }) - .collect(); - - Ok(ChunkHashResult { - hash: chunk_hash, - content_hash: content_hashes, - }) - } - - #[instrument("Compilation:create_module_hashes", skip_all)] - pub async fn create_module_hashes(&mut self, modules: IdentifierSet) -> Result<()> { - let mg = self.get_module_graph(); - let chunk_graph = &self.chunk_graph; - let chunk_by_ukey = &self.chunk_by_ukey; - - let results = rspack_futures::scope::<_, Result<_>>(|token| { - for module_identifier in modules { - let s = unsafe { token.used((&*self, &mg, chunk_graph, chunk_by_ukey)) }; - s.spawn( - move |(compilation, mg, chunk_graph, chunk_by_ukey)| async move { - let mut hashes = RuntimeSpecMap::new(); - let module = mg - .module_by_identifier(&module_identifier) - .expect("should have module"); - for runtime in chunk_graph.get_module_runtimes_iter(module_identifier, chunk_by_ukey) { - let hash = module.get_runtime_hash(compilation, Some(runtime)).await?; - hashes.set(runtime.clone(), hash); - } - Ok((module_identifier, hashes)) - }, - ); - } - }) - .await - .into_iter() - .map(|r| r.to_rspack_result()) - .collect::>>()?; - - for result in results { - let (module, hashes) = result?; - if ChunkGraph::set_module_hashes(self, module, hashes) - && let Some(mut mutations) = self.incremental.mutations_write() - { - mutations.add(Mutation::ModuleSetHashes { module }); - } - } - Ok(()) - } - pub fn add_runtime_module( &mut self, chunk_ukey: &ChunkUkey, @@ -3207,11 +1425,6 @@ pub struct RenderManifestEntry { pub auxiliary: bool, } -pub struct ChunkHashResult { - pub hash: RspackHashDigest, - pub content_hash: ChunkContentHash, -} - #[cacheable] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub enum ManifestAssetType { diff --git a/crates/rspack_core/src/compilation/module_ids/mod.rs b/crates/rspack_core/src/compilation/module_ids/mod.rs new file mode 100644 index 000000000000..ecdf22998120 --- /dev/null +++ b/crates/rspack_core/src/compilation/module_ids/mod.rs @@ -0,0 +1,31 @@ +use super::*; +use crate::logger::Logger; + +pub async fn module_ids_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("module ids"); + + // Check if MODULE_IDS pass is disabled, and clear artifact if needed + if !compilation + .incremental + .passes_enabled(IncrementalPasses::MODULE_IDS) + { + compilation.module_ids_artifact.clear(); + } + + let mut diagnostics = vec![]; + let mut module_ids_artifact = mem::take(&mut compilation.module_ids_artifact); + plugin_driver + .compilation_hooks + .module_ids + .call(compilation, &mut module_ids_artifact, &mut diagnostics) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.moduleIds"))?; + compilation.module_ids_artifact = module_ids_artifact; + compilation.extend_diagnostics(diagnostics); + logger.time_end(start); + Ok(()) +} diff --git a/crates/rspack_core/src/compilation/optimize_chunk_modules/mod.rs b/crates/rspack_core/src/compilation/optimize_chunk_modules/mod.rs new file mode 100644 index 000000000000..222ad5ba2003 --- /dev/null +++ b/crates/rspack_core/src/compilation/optimize_chunk_modules/mod.rs @@ -0,0 +1,21 @@ +use super::*; +use crate::logger::Logger; + +pub async fn optimize_chunk_modules_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("optimize chunk modules"); + + let result = plugin_driver + .compilation_hooks + .optimize_chunk_modules + .call(compilation) + .await + .map(|_| ()) + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeChunkModules")); + + logger.time_end(start); + result +} diff --git a/crates/rspack_core/src/compilation/optimize_chunks/mod.rs b/crates/rspack_core/src/compilation/optimize_chunks/mod.rs new file mode 100644 index 000000000000..fda4f268b7be --- /dev/null +++ b/crates/rspack_core/src/compilation/optimize_chunks/mod.rs @@ -0,0 +1,23 @@ +use super::*; +use crate::logger::Logger; + +pub async fn optimize_chunks_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("optimize chunks"); + + while matches!( + plugin_driver + .compilation_hooks + .optimize_chunks + .call(compilation) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeChunks"))?, + Some(true) + ) {} + + logger.time_end(start); + Ok(()) +} diff --git a/crates/rspack_core/src/compilation/optimize_code_generation/mod.rs b/crates/rspack_core/src/compilation/optimize_code_generation/mod.rs new file mode 100644 index 000000000000..280b91cbb6ae --- /dev/null +++ b/crates/rspack_core/src/compilation/optimize_code_generation/mod.rs @@ -0,0 +1,18 @@ +use super::*; +use crate::logger::Logger; + +pub async fn optimize_code_generation_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("optimize code generation"); + plugin_driver + .compilation_hooks + .optimize_code_generation + .call(compilation) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeCodeGeneration"))?; + logger.time_end(start); + Ok(()) +} diff --git a/crates/rspack_core/src/compilation/optimize_dependencies/mod.rs b/crates/rspack_core/src/compilation/optimize_dependencies/mod.rs new file mode 100644 index 000000000000..79f4ffc52bc5 --- /dev/null +++ b/crates/rspack_core/src/compilation/optimize_dependencies/mod.rs @@ -0,0 +1,39 @@ +use super::*; +use crate::logger::Logger; + +pub async fn optimize_dependencies_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("optimize dependencies"); + // https://github.com/webpack/webpack/blob/d15c73469fd71cf98734685225250148b68ddc79/lib/Compilation.js#L2812-L2814 + + let mut diagnostics: Vec = vec![]; + let mut side_effects_optimize_artifact = compilation.side_effects_optimize_artifact.take(); + let mut build_module_graph_artifact = compilation.build_module_graph_artifact.take(); + while matches!( + plugin_driver + .compilation_hooks + .optimize_dependencies + .call( + compilation, + &mut side_effects_optimize_artifact, + &mut build_module_graph_artifact, + &mut diagnostics + ) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeDependencies"))?, + Some(true) + ) {} + compilation + .side_effects_optimize_artifact + .replace(side_effects_optimize_artifact); + compilation + .build_module_graph_artifact + .replace(build_module_graph_artifact); + compilation.extend_diagnostics(diagnostics); + + logger.time_end(start); + Ok(()) +} diff --git a/crates/rspack_core/src/compilation/optimize_modules/mod.rs b/crates/rspack_core/src/compilation/optimize_modules/mod.rs new file mode 100644 index 000000000000..af48e3cb3604 --- /dev/null +++ b/crates/rspack_core/src/compilation/optimize_modules/mod.rs @@ -0,0 +1,32 @@ +use super::*; +use crate::logger::Logger; + +pub async fn optimize_modules_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("optimize modules"); + + let mut diagnostics = vec![]; + while matches!( + plugin_driver + .compilation_hooks + .optimize_modules + .call(compilation, &mut diagnostics) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeModules"))?, + Some(true) + ) {} + compilation.extend_diagnostics(diagnostics); + + let result = plugin_driver + .compilation_hooks + .after_optimize_modules + .call(compilation) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.afterOptimizeModules")); + + logger.time_end(start); + result +} diff --git a/crates/rspack_core/src/compilation/optimize_tree/mod.rs b/crates/rspack_core/src/compilation/optimize_tree/mod.rs new file mode 100644 index 000000000000..f1ba8821b710 --- /dev/null +++ b/crates/rspack_core/src/compilation/optimize_tree/mod.rs @@ -0,0 +1,20 @@ +use super::*; +use crate::logger::Logger; + +pub async fn optimize_tree_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("optimize tree"); + + let result = plugin_driver + .compilation_hooks + .optimize_tree + .call(compilation) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeTree")); + + logger.time_end(start); + result +} diff --git a/crates/rspack_core/src/compilation/process_assets/mod.rs b/crates/rspack_core/src/compilation/process_assets/mod.rs new file mode 100644 index 000000000000..3e3887944f9f --- /dev/null +++ b/crates/rspack_core/src/compilation/process_assets/mod.rs @@ -0,0 +1,43 @@ +use super::*; +use crate::logger::Logger; + +pub async fn process_assets_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("process assets"); + compilation.process_assets(plugin_driver.clone()).await?; + logger.time_end(start); + + let start = logger.time("after process assets"); + compilation.after_process_assets(plugin_driver).await?; + logger.time_end(start); + Ok(()) +} + +impl Compilation { + #[instrument("Compilation:process_assets",target=TRACING_BENCH_TARGET, skip_all)] + async fn process_assets(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { + plugin_driver + .compilation_hooks + .process_assets + .call(self) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.processAssets")) + } + + #[instrument("Compilation:after_process_assets", skip_all)] + async fn after_process_assets(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { + let mut diagnostics: Vec = vec![]; + + let res = plugin_driver + .compilation_hooks + .after_process_assets + .call(self, &mut diagnostics) + .await; + + self.extend_diagnostics(diagnostics); + res + } +} diff --git a/crates/rspack_core/src/compilation/rspack_passes.md b/crates/rspack_core/src/compilation/rspack_passes.md new file mode 100644 index 000000000000..b8583f8bc1dd --- /dev/null +++ b/crates/rspack_core/src/compilation/rspack_passes.md @@ -0,0 +1,70 @@ +# Compilation Passes Architecture + +This document describes the modular architecture of the compilation process in rspack. + +## Overview + +The compilation process is organized into independent modules, each responsible for a specific phase or pass. This modular design improves code maintainability, testability, and allows for better separation of concerns. + +## Module Structure + +``` +compilation/ +├── mod.rs # Main Compilation struct which exposes the public API +├── run_passes.rs # Pass driver invoked from Compiler that runs make + seal passes +├── make/ # make_hook_pass: make hook + cache.before_build_module_graph +├── build_module_graph/ # build_module_graph_pass: module graph construction +├── finish_make/ # finish_make_pass: finish_make hook +├── finish_module_graph/ # finish_module_graph_pass: finalize module graph + cache +├── finish_modules/ # finish_modules_pass: finish_modules hook, diagnostics, checkpoint +├── seal/ # seal_pass: seal hook +├── optimize_dependencies/ # optimizeDependencies hook + side effects artifact +├── build_chunk_graph/ # Chunk graph construction (code splitting cache + pass wrapper) +├── optimize_modules/ # optimizeModules + afterOptimizeModules hooks +├── optimize_chunks/ # optimizeChunks hook +├── optimize_tree/ # optimizeTree hook +├── optimize_chunk_modules/ # optimizeChunkModules hook +├── module_ids/ # Module ID assignment + diagnostics +├── chunk_ids/ # Chunk ID assignment + diagnostics +├── assign_runtime_ids/ # Runtime ID assignment for runtime chunks +├── optimize_code_generation/ # optimizeCodeGeneration hook +├── create_module_hashes/ # Module hash computation (incremental aware) +├── code_generation/ # Module codegen + afterCodeGeneration hook +├── runtime_requirements/ # Module/chunk/tree runtime requirements + runtime modules +├── create_hash/ # Chunk hashing, runtime module hashes, full hash + runtime module codegen +├── create_module_assets/ # Emit module-declared assets and mark chunk auxiliary files +├── create_chunk_assets/ # Render manifests and emit chunk assets +├── process_assets/ # processAssets + afterProcessAssets hooks +└── after_seal/ # afterSeal hook +``` + +## Pass Entry + +- `Compiler::compile` builds `CompilationParams`, fires `thisCompilation` then `compilation` compiler hooks (binding safety for JS), and delegates to `Compilation::run_passes`. +- `Compilation::run_passes` performs the make and seal stages using the order below. + +## Pass Order (Compilation::run_passes) + +`run_passes` orchestrates the full pipeline (make + seal) in this order: + +1. `make_hook_pass`: `make` hook + cache.before_build_module_graph +2. `build_module_graph_pass`: build module graph +3. `finish_make_pass`: `finish_make` hook +4. `finish_module_graph_pass`: `finish_build_module_graph` + cache.after_build_module_graph +5. `finish_modules_pass`: `finish_modules` hook, collect diagnostics, incremental checkpoint +6. Freeze module static cache in production +7. `seal_pass`: `seal` hook +8. `optimize_dependencies_pass` +9. `build_chunk_graph_pass` → `optimize_modules_pass` → `optimize_chunks_pass` +10. `optimize_tree_pass` → `optimize_chunk_modules_pass` +11. `module_ids_pass` → `chunk_ids_pass` → `assign_runtime_ids` +12. `optimize_code_generation_pass` +13. `create_module_hashes_pass` +14. `code_generation_pass` +15. `runtime_requirements_pass` +16. `create_hash_pass` (also runs runtime module code generation) +17. `create_module_assets_pass` +18. `create_chunk_assets_pass` +19. `process_assets_pass` +20. `after_seal_pass` +21. Unfreeze module static cache in production diff --git a/crates/rspack_core/src/compilation/run_passes.rs b/crates/rspack_core/src/compilation/run_passes.rs new file mode 100644 index 000000000000..9b0c4cca45a8 --- /dev/null +++ b/crates/rspack_core/src/compilation/run_passes.rs @@ -0,0 +1,68 @@ +use super::{ + after_seal::after_seal_pass, assign_runtime_ids::assign_runtime_ids, + build_chunk_graph::pass::build_chunk_graph_pass, build_module_graph::build_module_graph_pass, + chunk_ids::chunk_ids_pass, code_generation::code_generation_pass, + create_chunk_assets::create_chunk_assets_pass, create_hash::create_hash_pass, + create_module_assets::create_module_assets_pass, create_module_hashes::create_module_hashes_pass, + finish_make::finish_make_pass, finish_module_graph::finish_module_graph_pass, + finish_modules::finish_modules_pass, make::make_hook_pass, module_ids::module_ids_pass, + optimize_chunk_modules::optimize_chunk_modules_pass, optimize_chunks::optimize_chunks_pass, + optimize_code_generation::optimize_code_generation_pass, + optimize_dependencies::optimize_dependencies_pass, optimize_modules::optimize_modules_pass, + optimize_tree::optimize_tree_pass, process_assets::process_assets_pass, + runtime_requirements::runtime_requirements_pass, seal::seal_pass, *, +}; +use crate::{Compilation, SharedPluginDriver, cache::Cache}; + +impl Compilation { + pub async fn run_passes( + &mut self, + plugin_driver: SharedPluginDriver, + cache: &mut dyn Cache, + ) -> Result<()> { + make_hook_pass(self, plugin_driver.clone(), cache).await?; + build_module_graph_pass(self).await?; + finish_make_pass(self, plugin_driver.clone()).await?; + finish_module_graph_pass(self, cache).await?; + finish_modules_pass(self).await?; + // This is the end of first pass of build module graph which will be recovered for next compilation + // add a checkpoint here since we may modify module graph later in incremental compilation + // and we can recover to this checkpoint in the future + if self.incremental.passes_enabled(IncrementalPasses::MAKE) { + self.build_module_graph_artifact.module_graph.checkpoint(); + } + if !self.options.mode.is_development() { + self.module_static_cache_artifact.freeze(); + } + + seal_pass(self, plugin_driver.clone()).await?; + + optimize_dependencies_pass(self, plugin_driver.clone()).await?; + + build_chunk_graph_pass(self).await?; + optimize_modules_pass(self, plugin_driver.clone()).await?; + optimize_chunks_pass(self, plugin_driver.clone()).await?; + + optimize_tree_pass(self, plugin_driver.clone()).await?; + optimize_chunk_modules_pass(self, plugin_driver.clone()).await?; + + module_ids_pass(self, plugin_driver.clone()).await?; + chunk_ids_pass(self, plugin_driver.clone()).await?; + assign_runtime_ids(self); + + optimize_code_generation_pass(self, plugin_driver.clone()).await?; + create_module_hashes_pass(self).await?; + code_generation_pass(self, plugin_driver.clone()).await?; + runtime_requirements_pass(self, plugin_driver.clone()).await?; + create_hash_pass(self, plugin_driver.clone()).await?; + create_module_assets_pass(self, plugin_driver.clone()).await?; + create_chunk_assets_pass(self, plugin_driver.clone()).await?; + process_assets_pass(self, plugin_driver.clone()).await?; + after_seal_pass(self, plugin_driver).await?; + + if !self.options.mode.is_development() { + self.module_static_cache_artifact.unfreeze(); + } + Ok(()) + } +} diff --git a/crates/rspack_core/src/compilation/runtime_requirements/mod.rs b/crates/rspack_core/src/compilation/runtime_requirements/mod.rs new file mode 100644 index 000000000000..7892b242edb0 --- /dev/null +++ b/crates/rspack_core/src/compilation/runtime_requirements/mod.rs @@ -0,0 +1,404 @@ +use super::*; +use crate::logger::Logger; + +pub async fn runtime_requirements_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + let logger = compilation.get_logger("rspack.Compilation"); + let start = logger.time("runtime requirements"); + let process_runtime_requirements_modules = if let Some(mutations) = compilation + .incremental + .mutations_read(IncrementalPasses::MODULES_RUNTIME_REQUIREMENTS) + && !compilation.cgm_runtime_requirements_artifact.is_empty() + { + let revoked_modules = mutations.iter().filter_map(|mutation| match mutation { + Mutation::ModuleRemove { module } => Some(*module), + _ => None, + }); + for revoked_module in revoked_modules { + compilation + .cgm_runtime_requirements_artifact + .remove(&revoked_module); + } + let modules: IdentifierSet = mutations + .iter() + .filter_map(|mutation| match mutation { + Mutation::ModuleSetHashes { module } => Some(*module), + _ => None, + }) + .collect(); + let logger = compilation.get_logger("rspack.incremental.modulesRuntimeRequirements"); + logger.log(format!( + "{} modules are affected, {} in total", + modules.len(), + compilation.get_module_graph().modules().len() + )); + modules + } else { + compilation.cgm_runtime_requirements_artifact = Default::default(); + compilation + .get_module_graph() + .modules() + .keys() + .copied() + .collect() + }; + compilation + .process_modules_runtime_requirements( + process_runtime_requirements_modules, + plugin_driver.clone(), + ) + .await?; + let runtime_chunks = compilation.get_chunk_graph_entries().collect(); + + // Check if CHUNKS_RUNTIME_REQUIREMENTS pass is disabled, and clear artifact if needed + if !compilation + .incremental + .passes_enabled(IncrementalPasses::CHUNKS_RUNTIME_REQUIREMENTS) + { + compilation.cgc_runtime_requirements_artifact.clear(); + } + + let process_runtime_requirements_chunks = if let Some(mutations) = compilation + .incremental + .mutations_read(IncrementalPasses::CHUNKS_RUNTIME_REQUIREMENTS) + && !compilation.cgc_runtime_requirements_artifact.is_empty() + { + let removed_chunks = mutations.iter().filter_map(|mutation| match mutation { + Mutation::ChunkRemove { chunk } => Some(chunk), + _ => None, + }); + for removed_chunk in removed_chunks { + compilation + .cgc_runtime_requirements_artifact + .remove(removed_chunk); + } + let affected_chunks = mutations.get_affected_chunks_with_chunk_graph(compilation); + for affected_chunk in &affected_chunks { + compilation + .cgc_runtime_requirements_artifact + .remove(affected_chunk); + } + for runtime_chunk in &runtime_chunks { + compilation + .cgc_runtime_requirements_artifact + .remove(runtime_chunk); + } + compilation + .cgc_runtime_requirements_artifact + .retain(|chunk, _| compilation.chunk_by_ukey.contains(chunk)); + let logger = compilation.get_logger("rspack.incremental.chunksRuntimeRequirements"); + logger.log(format!( + "{} chunks are affected, {} in total", + affected_chunks.len(), + compilation.chunk_by_ukey.len() + )); + affected_chunks + } else { + compilation.chunk_by_ukey.keys().copied().collect() + }; + compilation + .process_chunks_runtime_requirements( + process_runtime_requirements_chunks, + runtime_chunks, + plugin_driver.clone(), + ) + .await?; + logger.time_end(start); + Ok(()) +} + +macro_rules! process_runtime_requirement_hook_macro { + ($name: ident, $s: ty, $c: ty) => { + async fn $name( + self: $s, + requirements: &mut RuntimeGlobals, + call_hook: impl for<'a> Fn( + $c, + &'a RuntimeGlobals, + &'a RuntimeGlobals, + &'a mut RuntimeGlobals, + ) -> BoxFuture<'a, Result<()>>, + ) -> Result<()> { + let mut runtime_requirements_mut = *requirements; + let mut runtime_requirements; + + loop { + runtime_requirements = runtime_requirements_mut; + runtime_requirements_mut = RuntimeGlobals::default(); + // runtime_requirements: rt_requirements of last time + // runtime_requirements_mut: changed rt_requirements + // requirements: all rt_requirements + call_hook( + self, + requirements, + &runtime_requirements, + &mut runtime_requirements_mut, + ) + .await?; + + // check if we have changes to runtime_requirements + runtime_requirements_mut = + runtime_requirements_mut.difference(requirements.intersection(runtime_requirements_mut)); + if runtime_requirements_mut.is_empty() { + break; + } else { + requirements.insert(runtime_requirements_mut); + } + } + Ok(()) + } + }; +} + +impl Compilation { + #[instrument("Compilation:process_modules_runtime_requirements", skip_all)] + pub async fn process_modules_runtime_requirements( + &mut self, + modules: IdentifierSet, + plugin_driver: SharedPluginDriver, + ) -> Result<()> { + let logger = self.get_logger("rspack.Compilation"); + let start = logger.time("runtime requirements.modules"); + + let module_results = rspack_futures::scope::<_, Result<_>>(|token| { + modules + .into_iter() + .filter(|module| self.chunk_graph.get_number_of_module_chunks(*module) > 0) + .for_each(|module| { + let s = unsafe { token.used((&self, &plugin_driver)) }; + s.spawn(move |(compilation, plugin_driver)| async move { + let mut map = RuntimeSpecMap::new(); + let runtimes = compilation + .chunk_graph + .get_module_runtimes_iter(module, &compilation.chunk_by_ukey); + for runtime in runtimes { + let runtime_requirements = compilation + .old_cache + .process_runtime_requirements_occasion + .use_cache(module, runtime, compilation, || async { + let mut runtime_requirements = compilation + .code_generation_results + .get_runtime_requirements(&module, Some(runtime)); + + plugin_driver + .compilation_hooks + .additional_module_runtime_requirements + .call(compilation, &module, &mut runtime_requirements) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.additionalModuleRuntimeRequirements"))?; + + compilation + .process_runtime_requirement_hook(&mut runtime_requirements, { + let plugin_driver = plugin_driver.clone(); + move |compilation, + all_runtime_requirements, + runtime_requirements, + runtime_requirements_mut| { + Box::pin({ + let plugin_driver = plugin_driver.clone(); + async move { + plugin_driver + .compilation_hooks + .runtime_requirement_in_module + .call( + compilation, + &module, + all_runtime_requirements, + runtime_requirements, + runtime_requirements_mut, + ) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.runtimeRequirementInModule"))?; + Ok(()) + }}) + } + }) + .await?; + Ok(runtime_requirements) + }) + .await?; + map.set(runtime.clone(), runtime_requirements); + } + Ok((module, map)) + }); + }); + }) + .await + .into_iter() + .map(|r| r.to_rspack_result()) + .collect::>>()?; + + for entry in module_results { + let (module, map) = entry?; + ChunkGraph::set_module_runtime_requirements(self, module, map); + } + logger.time_end(start); + Ok(()) + } + + #[instrument(name = "Compilation:process_chunks_runtime_requirements", target=TRACING_BENCH_TARGET skip_all)] + pub async fn process_chunks_runtime_requirements( + &mut self, + chunks: UkeySet, + entries: UkeySet, + plugin_driver: SharedPluginDriver, + ) -> Result<()> { + let logger = self.get_logger("rspack.Compilation"); + let start = logger.time("runtime requirements.chunks"); + let chunk_requirements = chunks + .iter() + .chain(entries.iter()) + .par_bridge() + .map(|chunk_ukey| { + let mut set = RuntimeGlobals::default(); + for mid in self.chunk_graph.get_chunk_modules_identifier(chunk_ukey) { + let chunk = self.chunk_by_ukey.expect_get(chunk_ukey); + if let Some(runtime_requirements) = + ChunkGraph::get_module_runtime_requirements(self, *mid, chunk.runtime()) + { + set.insert(*runtime_requirements); + } + } + + (*chunk_ukey, set) + }) + .collect::>(); + + for (chunk_ukey, mut set) in chunk_requirements { + plugin_driver + .compilation_hooks + .additional_chunk_runtime_requirements + .call(self, &chunk_ukey, &mut set) + .await + .map_err(|e| { + e.wrap_err("caused by plugins in Compilation.hooks.additionalChunkRuntimeRequirements") + })?; + + self + .process_runtime_requirement_hook_mut(&mut set, { + let plugin_driver = plugin_driver.clone(); + move |compilation, + all_runtime_requirements, + runtime_requirements, + runtime_requirements_mut| { + Box::pin({ + let plugin_driver = plugin_driver.clone(); + async move { + plugin_driver + .compilation_hooks + .runtime_requirement_in_chunk + .call( + compilation, + &chunk_ukey, + all_runtime_requirements, + runtime_requirements, + runtime_requirements_mut, + ) + .await + .map_err(|e| { + e.wrap_err("caused by plugins in Compilation.hooks.runtimeRequirementInChunk") + })?; + Ok(()) + } + }) + } + }) + .await?; + + ChunkGraph::set_chunk_runtime_requirements(self, chunk_ukey, set); + } + logger.time_end(start); + + let start = logger.time("runtime requirements.entries"); + for &entry_ukey in &entries { + let entry = self.chunk_by_ukey.expect_get(&entry_ukey); + let mut set = RuntimeGlobals::default(); + for chunk_ukey in entry + .get_all_referenced_chunks(&self.chunk_group_by_ukey) + .iter() + { + let runtime_requirements = ChunkGraph::get_chunk_runtime_requirements(self, chunk_ukey); + set.insert(*runtime_requirements); + } + + plugin_driver + .compilation_hooks + .additional_tree_runtime_requirements + .call(self, &entry_ukey, &mut set) + .await + .map_err(|e| { + e.wrap_err("caused by plugins in Compilation.hooks.additionalTreeRuntimeRequirements") + })?; + + self + .process_runtime_requirement_hook_mut(&mut set, { + let plugin_driver = plugin_driver.clone(); + move |compilation, + all_runtime_requirements, + runtime_requirements, + runtime_requirements_mut| { + Box::pin({ + let plugin_driver = plugin_driver.clone(); + async move { + plugin_driver + .compilation_hooks + .runtime_requirement_in_tree + .call( + compilation, + &entry_ukey, + all_runtime_requirements, + runtime_requirements, + runtime_requirements_mut, + ) + .await + .map_err(|e| { + e.wrap_err("caused by plugins in Compilation.hooks.runtimeRequirementInTree") + })?; + Ok(()) + } + }) + } + }) + .await?; + + ChunkGraph::set_tree_runtime_requirements(self, entry_ukey, set); + } + + // NOTE: webpack runs hooks.runtime_module in compilation.add_runtime_module + // and overwrite the runtime_module.generate() to get new source in create_chunk_assets + // this needs full runtime requirements, so run hooks.runtime_module after runtime_requirements_in_tree + let mut runtime_modules = mem::take(&mut self.runtime_modules); + for entry_ukey in &entries { + let runtime_module_ids: Vec<_> = self + .chunk_graph + .get_chunk_runtime_modules_iterable(entry_ukey) + .copied() + .collect(); + for runtime_module_id in runtime_module_ids { + plugin_driver + .compilation_hooks + .runtime_module + .call(self, &runtime_module_id, entry_ukey, &mut runtime_modules) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.runtimeModule"))?; + } + } + self.runtime_modules = runtime_modules; + + logger.time_end(start); + Ok(()) + } + + process_runtime_requirement_hook_macro!( + process_runtime_requirement_hook, + &Compilation, + &'a Compilation + ); + process_runtime_requirement_hook_macro!( + process_runtime_requirement_hook_mut, + &mut Compilation, + &'a mut Compilation + ); +} diff --git a/crates/rspack_core/src/compilation/seal/mod.rs b/crates/rspack_core/src/compilation/seal/mod.rs new file mode 100644 index 000000000000..94d12e17cd6b --- /dev/null +++ b/crates/rspack_core/src/compilation/seal/mod.rs @@ -0,0 +1,23 @@ +use rspack_error::Result; + +use crate::{Compilation, SharedPluginDriver}; + +pub async fn seal_pass( + compilation: &mut Compilation, + plugin_driver: SharedPluginDriver, +) -> Result<()> { + #[cfg(feature = "debug_tool")] + { + use rspack_util::debug_tool::wait_for_signal; + wait_for_signal("seal compilation"); + } + // https://github.com/webpack/webpack/blob/main/lib/Compilation.js#L2809 + plugin_driver + .compilation_hooks + .seal + .call(compilation) + .await + .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.seal"))?; + + Ok(()) +} diff --git a/crates/rspack_core/src/compiler/mod.rs b/crates/rspack_core/src/compiler/mod.rs index 9355e8d1e2b8..5ae6b5e7021e 100644 --- a/crates/rspack_core/src/compiler/mod.rs +++ b/crates/rspack_core/src/compiler/mod.rs @@ -15,12 +15,13 @@ use tracing::instrument; pub use self::rebuild::CompilationRecords; use crate::{ BoxPlugin, CleanOptions, Compilation, CompilationAsset, CompilerOptions, CompilerPlatform, - ContextModuleFactory, Filename, KeepPattern, Logger, NormalModuleFactory, PluginDriver, - ResolverFactory, SharedPluginDriver, + ContextModuleFactory, Filename, KeepPattern, NormalModuleFactory, PluginDriver, ResolverFactory, + SharedPluginDriver, cache::{Cache, new_cache}, compilation::build_module_graph::ModuleExecutor, fast_set, include_hash, incremental::{Incremental, IncrementalPasses}, + logger::Logger, old_cache::Cache as OldCache, trim_dir, }; @@ -257,13 +258,10 @@ impl Compiler { Ok(()) } - async fn build_module_graph(&mut self) -> Result<()> { + #[instrument("Compiler:compile", target=TRACING_BENCH_TARGET,skip_all)] + async fn compile(&mut self) -> Result<()> { let mut compilation_params = self.new_compilation_params(); - // FOR BINDING SAFETY: - // Make sure `thisCompilation` hook was called for each `JsCompilation` update before any access to it. - // `JsCompiler` tapped `thisCompilation` to update the `JsCompilation` on the JavaScript side. - // Otherwise, trying to access the old native `JsCompilation` would cause undefined behavior - // as the previous instance might get dropped. + // Make sure `thisCompilation` is emitted before any JS side access to `JsCompilation`. self .plugin_driver .compiler_hooks @@ -277,65 +275,12 @@ impl Compiler { .call(&mut self.compilation, &mut compilation_params) .await?; - let logger = self.compilation.get_logger("rspack.Compiler"); - let make_start = logger.time("make"); - let make_hook_start = logger.time("make hook"); - self - .cache - .before_build_module_graph(&mut self.compilation.build_module_graph_artifact) - .await; - - self - .plugin_driver - .compiler_hooks - .make - .call(&mut self.compilation) - .await?; - logger.time_end(make_hook_start); - self.compilation.build_module_graph().await?; - logger.time_end(make_start); - - let start = logger.time("finish make hook"); - self - .plugin_driver - .compiler_hooks - .finish_make - .call(&mut self.compilation) - .await?; - logger.time_end(start); - - let start = logger.time("finish compilation"); - self.compilation.finish_build_module_graph().await?; - self - .cache - .after_build_module_graph(&self.compilation.build_module_graph_artifact) - .await; - - logger.time_end(start); - Ok(()) - } - #[instrument("Compiler:compile", target=TRACING_BENCH_TARGET,skip_all)] - async fn compile(&mut self) -> Result<()> { let logger = self.compilation.get_logger("rspack.Compiler"); let start = logger.time("seal compilation"); - #[cfg(feature = "debug_tool")] - { - use rspack_util::debug_tool::wait_for_signal; - wait_for_signal("seal compilation"); - } - self.build_module_graph().await?; - let dependencies_diagnostics_artifact = - self.compilation.dependencies_diagnostics_artifact.clone(); - let async_modules_artifact = self.compilation.async_modules_artifact.clone(); - let diagnostics = self + self .compilation - .collect_build_module_graph_effects( - &mut dependencies_diagnostics_artifact.borrow_mut(), - &mut async_modules_artifact.borrow_mut(), - ) + .run_passes(self.plugin_driver.clone(), &mut *self.cache) .await?; - self.compilation.extend_diagnostics(diagnostics); - self.compilation.seal(self.plugin_driver.clone()).await?; logger.time_end(start); // Consume plugin driver diagnostic diff --git a/crates/rspack_loader_runner/src/content.rs b/crates/rspack_loader_runner/src/content.rs index 2c19e9c92ba3..9c770cb7db5d 100644 --- a/crates/rspack_loader_runner/src/content.rs +++ b/crates/rspack_loader_runner/src/content.rs @@ -8,7 +8,8 @@ use anymap::CloneAny; use once_cell::sync::OnceCell; use rspack_cacheable::{ cacheable, - with::{AsInner, AsOption, AsPreset, AsString}, + utils::PortablePath, + with::{As, AsInner, AsOption, AsPreset}, }; use rspack_error::{Error, Result, ToStringResultToRspackResultExt}; use rspack_paths::{Utf8Path, Utf8PathBuf}; @@ -307,7 +308,7 @@ impl ResourceData { #[derive(Debug, Clone)] pub struct DescriptionData { /// Path to package.json - #[cacheable(with=AsString)] + #[cacheable(with=As)] path: PathBuf, /// Raw package.json diff --git a/crates/rspack_paths/src/lib.rs b/crates/rspack_paths/src/lib.rs index b3d082b7de3f..0331fc6e47ae 100644 --- a/crates/rspack_paths/src/lib.rs +++ b/crates/rspack_paths/src/lib.rs @@ -11,8 +11,9 @@ pub use camino::{Utf8Component, Utf8Components, Utf8Path, Utf8PathBuf, Utf8Prefi use dashmap::{DashMap, DashSet}; use indexmap::IndexSet; use rspack_cacheable::{ - cacheable, - with::{AsRefStr, AsRefStrConverter}, + ContextGuard, Error as CacheableError, cacheable, + utils::PortablePath, + with::{Custom, CustomConverter}, }; use rustc_hash::FxHasher; use ustr::IdentityHasher; @@ -52,7 +53,7 @@ impl<'a> AssertUtf8 for &'a Path { } } -#[cacheable(with=AsRefStr, hashable)] +#[cacheable(with=Custom)] #[derive(Clone, PartialEq, Eq)] pub struct ArcPath { path: Arc, @@ -126,12 +127,15 @@ impl From<&str> for ArcPath { } } -impl AsRefStrConverter for ArcPath { - fn as_str(&self) -> &str { - self.path.to_str().expect("expect utf8 str") +impl CustomConverter for ArcPath { + type Target = PortablePath; + fn serialize(&self, guard: &ContextGuard) -> Result { + Ok(PortablePath::new(&self.path, guard.project_root())) } - fn from_str(s: &str) -> Self { - Self::from(Path::new(s)) + fn deserialize(data: Self::Target, guard: &ContextGuard) -> Result { + Ok(Self::from(PathBuf::from( + data.into_path_string(guard.project_root()), + ))) } } diff --git a/crates/rspack_tools/src/compare/occasion/make.rs b/crates/rspack_tools/src/compare/occasion/make.rs index 6d196c0fcab3..433c61786479 100644 --- a/crates/rspack_tools/src/compare/occasion/make.rs +++ b/crates/rspack_tools/src/compare/occasion/make.rs @@ -4,9 +4,10 @@ pub use rspack_core::cache::persistent::occasion::make::SCOPE; use rspack_core::{ DependencyId, build_module_graph::BuildModuleGraphArtifact, - cache::persistent::{CacheableContext, occasion::make::MakeOccasion, storage::Storage}, + cache::persistent::{codec::CacheCodec, occasion::make::MakeOccasion, storage::Storage}, }; use rspack_error::Result; +use rspack_paths::Utf8PathBuf; use rustc_hash::FxHashMap as HashMap; use crate::{debug_info::DebugInfo, utils::ensure_iter_equal}; @@ -29,9 +30,10 @@ pub async fn compare( ensure_iter_equal("Make module key", map1.keys(), map2.keys(), &debug_info)?; // Convert stored data to BuildModuleGraphArtifact using MakeOccasion's recovery ability - let context = Arc::new(CacheableContext); - let occasion1 = MakeOccasion::new(storage1.clone(), context.clone()); - let occasion2 = MakeOccasion::new(storage2.clone(), context.clone()); + // Use a dummy path for codec since we're only deserializing + let codec = Arc::new(CacheCodec::new(Some(Utf8PathBuf::from("/")))); + let occasion1 = MakeOccasion::new(storage1.clone(), codec.clone()); + let occasion2 = MakeOccasion::new(storage2.clone(), codec.clone()); let artifact1 = occasion1.recovery().await?; let artifact2 = occasion2.recovery().await?; diff --git a/crates/rspack_workspace/src/generated.rs b/crates/rspack_workspace/src/generated.rs index d7a2d70611df..4c33836d0089 100644 --- a/crates/rspack_workspace/src/generated.rs +++ b/crates/rspack_workspace/src/generated.rs @@ -1,7 +1,7 @@ //! This is a generated file. Don't modify it by hand! Run 'cargo codegen' to re-generate the file. /// The version of the `swc_core` package used in the current workspace. pub const fn rspack_swc_core_version() -> &'static str { - "54.0.0" + "55.0.0" } /// The version of the JavaScript `@rspack/core` package. diff --git a/examples/react/package.json b/examples/react/package.json index 136b1c245d66..885ba8be4b36 100644 --- a/examples/react/package.json +++ b/examples/react/package.json @@ -15,7 +15,7 @@ "@rspack/cli": "workspace:*", "@rspack/core": "workspace:*", "@rspack/plugin-react-refresh": "^1.6.0", - "@types/react": "^19.2.7", + "@types/react": "^19.2.8", "@types/react-dom": "^19.2.3", "react-refresh": "^0.18.0", "typescript": "^5.9.3" diff --git a/package.json b/package.json index 8132001260ce..036a764643b1 100644 --- a/package.json +++ b/package.json @@ -64,11 +64,11 @@ "@biomejs/biome": "^2.3.11", "@microsoft/api-extractor": "7.55.2", "@microsoft/api-extractor-model": "7.32.2", - "@rslint/core": "0.1.13", + "@rslint/core": "0.2.0", "@rspack/cli": "workspace:*", "@taplo/cli": "^0.7.0", "@types/is-ci": "^3.0.4", - "@types/node": "^20.19.27", + "@types/node": "^20.19.29", "check-dependency-version-consistency": "^5.0.1", "commander": "14.0.2", "cross-env": "^10.1.0", diff --git a/packages/create-rspack/package.json b/packages/create-rspack/package.json index 899338ede751..42a0e2011b58 100644 --- a/packages/create-rspack/package.json +++ b/packages/create-rspack/package.json @@ -27,7 +27,7 @@ "create-rstack": "1.7.20" }, "devDependencies": { - "@rslib/core": "0.19.1", + "@rslib/core": "0.19.2", "typescript": "^5.9.3" }, "publishConfig": { diff --git a/packages/create-rspack/template-react-js/package.json b/packages/create-rspack/template-react-js/package.json index 5490a95adaad..fea794822d92 100644 --- a/packages/create-rspack/template-react-js/package.json +++ b/packages/create-rspack/template-react-js/package.json @@ -15,7 +15,7 @@ "@rspack/cli": "workspace:*", "@rspack/core": "workspace:*", "@rspack/plugin-react-refresh": "^1.6.0", - "@types/react": "^19.2.7", + "@types/react": "^19.2.8", "@types/react-dom": "^19.2.3", "react-refresh": "^0.18.0" } diff --git a/packages/create-rspack/template-react-ts/package.json b/packages/create-rspack/template-react-ts/package.json index 7e0082ccd117..70ef071ed8cc 100644 --- a/packages/create-rspack/template-react-ts/package.json +++ b/packages/create-rspack/template-react-ts/package.json @@ -15,7 +15,7 @@ "@rspack/cli": "workspace:*", "@rspack/core": "workspace:*", "@rspack/plugin-react-refresh": "^1.6.0", - "@types/react": "^19.2.7", + "@types/react": "^19.2.8", "@types/react-dom": "^19.2.3", "react-refresh": "^0.18.0", "typescript": "^5.9.3" diff --git a/packages/rspack-cli/package.json b/packages/rspack-cli/package.json index 91f1a10fdc3f..e381cbc13067 100644 --- a/packages/rspack-cli/package.json +++ b/packages/rspack-cli/package.json @@ -36,7 +36,7 @@ "exit-hook": "^4.0.0" }, "devDependencies": { - "@rslib/core": "0.19.1", + "@rslib/core": "0.19.2", "@rspack/core": "workspace:*", "@rspack/dev-server": "~1.1.5", "@rspack/test-tools": "workspace:*", diff --git a/packages/rspack-cli/rstest.config.ts b/packages/rspack-cli/rstest.config.ts index 0873dde68eb3..b9cc8cce90d3 100644 --- a/packages/rspack-cli/rstest.config.ts +++ b/packages/rspack-cli/rstest.config.ts @@ -22,6 +22,7 @@ export default defineConfig({ }, output: { externals: [/@rspack\/core/, /api-wrapper/], + module: false, }, env: { RUST_BACKTRACE: 'full', diff --git a/packages/rspack-test-tools/etc/test-tools.api.md b/packages/rspack-test-tools/etc/test-tools.api.md index da6cd4b9138a..2b682c08fea1 100644 --- a/packages/rspack-test-tools/etc/test-tools.api.md +++ b/packages/rspack-test-tools/etc/test-tools.api.md @@ -138,7 +138,8 @@ export function createWatchCase(name: string, src: string, dist: string, temp: s export function createWatchIncrementalCase(name: string, src: string, dist: string, temp: string, options?: WatchIncrementalOptions): void; // @public (undocumented) -export function describeByWalk(testFile: string, createCase: (name: string, src: string, dist: string) => void, options?: { +export function describeByWalk( +testFile: string, createCase: (name: string, src: string, dist: string) => void, options?: { type?: 'file' | 'directory'; level?: number; source?: string; diff --git a/packages/rspack-test-tools/package.json b/packages/rspack-test-tools/package.json index 52bab598adb4..f92246f768fe 100644 --- a/packages/rspack-test-tools/package.json +++ b/packages/rspack-test-tools/package.json @@ -41,14 +41,14 @@ "directory": "packages/rspack-test-tools" }, "dependencies": { - "@babel/generator": "7.28.5", - "@babel/parser": "7.28.5", - "@babel/traverse": "7.28.5", - "@babel/types": "7.28.5", + "@babel/generator": "7.28.6", + "@babel/parser": "7.28.6", + "@babel/traverse": "7.28.6", + "@babel/types": "7.28.6", "cross-env": "^10.1.0", "filenamify": "4.3.0", "fs-extra": "^11.3.3", - "iconv-lite": "^0.7.1", + "iconv-lite": "^0.7.2", "javascript-stringify": "^2.1.0", "jest-diff": "^30.2.0", "jest-snapshot": "29.7.0", diff --git a/packages/rspack-test-tools/src/helper/directory.ts b/packages/rspack-test-tools/src/helper/directory.ts index b06349d5faf2..be56a34c49d3 100644 --- a/packages/rspack-test-tools/src/helper/directory.ts +++ b/packages/rspack-test-tools/src/helper/directory.ts @@ -9,6 +9,9 @@ export const isValidCaseDirectory = (name: string) => !name.startsWith('_') && !name.startsWith('.') && name !== 'node_modules'; export function describeByWalk( + /** + * The test file absolute path. + */ testFile: string, createCase: (name: string, src: string, dist: string) => void, options: { @@ -30,6 +33,7 @@ export function describeByWalk( options.source || path.join(path.dirname(testFile), `${testId}Cases`); const testSourceId = path.basename(sourceBase); + const absoluteTestDir = path.dirname(testFile); const distBase = options.dist || path.join(path.dirname(testFile), 'js', testId); @@ -75,8 +79,21 @@ export function describeByWalk( .split('.') .shift()!, ); + let suiteName = name; - describeFn(name, () => { + // support filter test by absolute path + if (process.env.testFilter?.includes(absoluteTestDir)) { + const fullCasePath = path.join( + absoluteTestDir, + testSourceId, + caseName, + ); + if (fullCasePath.includes(process.env.testFilter!)) { + suiteName = fullCasePath; + } + } + + describeFn(suiteName, () => { const source = path.join(sourceBase, caseName); let dist = ''; if (absoluteDist) { @@ -89,7 +106,7 @@ export function describeByWalk( dist = path.join(sourceBase, caseName, relativeDist); } } - createCase(name, source, dist); + createCase(suiteName, source, dist); }); } }); diff --git a/packages/rspack-test-tools/src/plugin/lazy-compilation-test-plugin.ts b/packages/rspack-test-tools/src/plugin/lazy-compilation-test-plugin.ts index be255bd0a34a..b70a09eadedf 100644 --- a/packages/rspack-test-tools/src/plugin/lazy-compilation-test-plugin.ts +++ b/packages/rspack-test-tools/src/plugin/lazy-compilation-test-plugin.ts @@ -38,6 +38,9 @@ export class LazyCompilationTestPlugin { resolve(null); }); server.on('request', (req, res) => { + // Set CORS headers for jsdom's XMLHttpRequest + res.setHeader('Access-Control-Allow-Origin', '*'); + middleware(req, res, () => {}); }); server.on('connection', (socket) => { diff --git a/packages/rspack-test-tools/src/runner/web/index.ts b/packages/rspack-test-tools/src/runner/web/index.ts index eff92d3d4426..aa1f1ecbb37c 100644 --- a/packages/rspack-test-tools/src/runner/web/index.ts +++ b/packages/rspack-test-tools/src/runner/web/index.ts @@ -175,7 +175,12 @@ export class WebRunner extends NodeRunner { protected createBaseModuleScope() { const moduleScope = super.createBaseModuleScope(); moduleScope.EventSource = EventSource; - moduleScope.fetch = async (url: string) => { + moduleScope.fetch = async (url: string, options: any) => { + // For Lazy Compilation Proxy the POST request to the real dev server. + if (options?.method === 'POST') { + return fetch(url, options as any); + } + try { const filePath = this.urlToPath(url); this.log(`fetch: ${url} -> ${filePath}`); diff --git a/packages/rspack/hot/lazy-compilation-node.js b/packages/rspack/hot/lazy-compilation-node.js index 41411bb287cd..a16c3b6adfba 100644 --- a/packages/rspack/hot/lazy-compilation-node.js +++ b/packages/rspack/hot/lazy-compilation-node.js @@ -2,51 +2,108 @@ import { createRequire } from 'node:module'; var urlBase = decodeURIComponent(__resourceQuery.slice(1)); var require = createRequire(import.meta.url); +var compiling = new Set(); +var errorHandlers = new Set(); -/** - * @param {{ data: string, onError: (err: Error) => void, active: boolean, module: module }} options options - * @returns {() => void} function to destroy response - */ -export const activate = function (options) { - var data = options.data; - var onError = options.onError; - var active = options.active; - /** @type {import("http").IncomingMessage} */ - var response; - var request = ( - urlBase.startsWith('https') ? require('https') : require('http') - ).request( - urlBase + encodeURIComponent(data), +/** @type {import("http").ClientRequest | undefined} */ +var pendingRequest; +/** @type {boolean} */ +var hasPendingUpdate = false; + +function sendRequest() { + if (compiling.size === 0) { + hasPendingUpdate = false; + return; + } + + var modules = Array.from(compiling); + var data = modules.join('\n'); + + var httpModule = urlBase.startsWith('https') + ? require('https') + : require('http'); + + var request = httpModule.request( + urlBase, { + method: 'POST', agent: false, - headers: { accept: 'text/event-stream' }, + headers: { + 'Content-Type': 'text/plain', + }, }, function (res) { - response = res; - response.on('error', errorHandler); - if (!active && !import.meta.webpackHot) { - console.log( - 'Hot Module Replacement is not enabled. Waiting for process restart...', + pendingRequest = undefined; + if (res.statusCode < 200 || res.statusCode >= 300) { + var error = new Error( + 'Problem communicating active modules to the server: HTTP ' + + res.statusCode, ); + errorHandlers.forEach(function (onError) { + onError(error); + }); + } + // Consume response data to free up memory + res.resume(); + if (hasPendingUpdate) { + hasPendingUpdate = false; + sendRequest(); } }, ); - /** - * @param {Error} err error - */ - function errorHandler(err) { - err.message = - 'Problem communicating active modules to the server' + - (err.message ? ': ' + err.message : '') + - '\nRequest: ' + - urlBase + - data; - onError(err); - } - request.on('error', errorHandler); + pendingRequest = request; + + request.on('error', function (err) { + pendingRequest = undefined; + var error = new Error( + 'Problem communicating active modules to the server: ' + err.message, + ); + errorHandlers.forEach(function (onError) { + onError(error); + }); + }); + + request.write(data); request.end(); +} + +function sendActiveRequest() { + hasPendingUpdate = true; + + // If no request is pending, start one + if (!pendingRequest) { + hasPendingUpdate = false; + sendRequest(); + } +} + +/** + * @param {{ data: string, onError: (err: Error) => void, active: boolean, module: module }} options options + * @returns {() => void} function to destroy response + */ +export const activate = function (options) { + var data = options.data; + var onError = options.onError; + var active = options.active; + var module = options.module; + + errorHandlers.add(onError); + + if (!compiling.has(data)) { + compiling.add(data); + sendActiveRequest(); + } + + if (!active && !module.hot) { + console.log( + 'Hot Module Replacement is not enabled. Waiting for process restart...', + ); + } + return function () { - response.destroy(); + errorHandlers.delete(onError); + compiling.delete(data); + sendActiveRequest(); }; }; diff --git a/packages/rspack/hot/lazy-compilation-web.js b/packages/rspack/hot/lazy-compilation-web.js index 4dbbc6b717c8..e7716a9336b9 100644 --- a/packages/rspack/hot/lazy-compilation-web.js +++ b/packages/rspack/hot/lazy-compilation-web.js @@ -1,47 +1,73 @@ -if (typeof EventSource !== 'function') { +if (typeof XMLHttpRequest === 'undefined') { throw new Error( - "Environment doesn't support lazy compilation (requires EventSource)", + "Environment doesn't support lazy compilation (requires XMLHttpRequest)", ); } var urlBase = decodeURIComponent(__resourceQuery.slice(1)); -/** @type {EventSource | undefined} */ -var activeEventSource; var compiling = new Set(); var errorHandlers = new Set(); -var updateEventSource = function updateEventSource() { - if (activeEventSource) activeEventSource.close(); - if (compiling.size) { - activeEventSource = new EventSource( - urlBase + - Array.from(compiling, function (module) { - return encodeURIComponent(module); - }).join('@'), - ); - /** - * @this {EventSource} - * @param {Event & { message?: string, filename?: string, lineno?: number, colno?: number, error?: Error }} event event - */ - activeEventSource.onerror = function (event) { - errorHandlers.forEach(function (onError) { - onError( - new Error( - 'Problem communicating active modules to the server' + - (event.message ? `: ${event.message} ` : '') + - (event.filename ? `: ${event.filename} ` : '') + - (event.lineno ? `: ${event.lineno} ` : '') + - (event.colno ? `: ${event.colno} ` : '') + - (event.error ? `: ${event.error}` : ''), - ), - ); - }); - }; - } else { - activeEventSource = undefined; +/** @type {XMLHttpRequest | undefined} */ +var pendingXhr; +/** @type {boolean} */ +var hasPendingUpdate = false; + +var sendRequest = function sendRequest() { + if (compiling.size === 0) { + hasPendingUpdate = false; + return; } + + var modules = Array.from(compiling); + var data = modules.join('\n'); + + var xhr = new XMLHttpRequest(); + pendingXhr = xhr; + xhr.open('POST', urlBase, true); + // text/plain Content-Type is simple request header + xhr.setRequestHeader('Content-Type', 'text/plain'); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + pendingXhr = undefined; + if (xhr.status < 200 || xhr.status >= 300) { + var error = new Error( + 'Problem communicating active modules to the server: HTTP ' + + xhr.status, + ); + errorHandlers.forEach(function (onError) { + onError(error); + }); + } + if (hasPendingUpdate) { + hasPendingUpdate = false; + sendRequest(); + } + } + }; + + xhr.onerror = function () { + pendingXhr = undefined; + var error = new Error('Problem communicating active modules to the server'); + errorHandlers.forEach(function (onError) { + onError(error); + }); + }; + + xhr.send(data); }; +function sendActiveRequest() { + hasPendingUpdate = true; + + // If no request is pending, start one + if (!pendingXhr) { + hasPendingUpdate = false; + sendRequest(); + } +} + /** * @param {{ data: string, onError: (err: Error) => void, active: boolean, module: module }} options options * @returns {() => void} function to destroy response @@ -54,7 +80,7 @@ export const activate = function (options) { if (!compiling.has(data)) { compiling.add(data); - updateEventSource(); + sendActiveRequest(); } if (!active && !import.meta.webpackHot) { @@ -66,6 +92,6 @@ export const activate = function (options) { return function () { errorHandlers.delete(onError); compiling.delete(data); - updateEventSource(); + sendActiveRequest(); }; }; diff --git a/packages/rspack/package.json b/packages/rspack/package.json index 1e546c987661..3ed9fa62a6cb 100644 --- a/packages/rspack/package.json +++ b/packages/rspack/package.json @@ -46,12 +46,12 @@ "directory": "packages/rspack" }, "devDependencies": { - "@ast-grep/napi": "^0.40.4", + "@ast-grep/napi": "^0.40.5", "@napi-rs/wasm-runtime": "1.0.7", "@rsbuild/plugin-node-polyfill": "^1.4.2", - "@rslib/core": "0.19.1", + "@rslib/core": "0.19.2", "@swc/types": "0.1.25", - "@types/node": "^20.19.27", + "@types/node": "^20.19.29", "@types/watchpack": "^2.4.5", "browserslist-load-config": "^1.0.1", "enhanced-resolve": "5.18.4", diff --git a/packages/rspack/src/MultiStats.ts b/packages/rspack/src/MultiStats.ts index f8cedbd84c07..a67718f2a70d 100644 --- a/packages/rspack/src/MultiStats.ts +++ b/packages/rspack/src/MultiStats.ts @@ -35,13 +35,13 @@ export default class MultiStats { } #createChildOptions( - options: boolean | StatsPresets | MultiStatsOptions = {}, + options: boolean | StatsPresets | MultiStatsOptions, context: (KnownCreateStatsOptionsContext & Record) | undefined, ) { const { children: childrenOptions = undefined, ...baseOptions } = typeof options === 'string' || typeof options === 'boolean' ? { preset: options } - : options; + : (options ?? {}); const children = this.stats.map((stat, idx) => { const childOptions = Array.isArray(childrenOptions) diff --git a/packages/rspack/src/builtin-plugin/lazy-compilation/middleware.ts b/packages/rspack/src/builtin-plugin/lazy-compilation/middleware.ts index 2a39aa3c07bf..0e72873feebc 100644 --- a/packages/rspack/src/builtin-plugin/lazy-compilation/middleware.ts +++ b/packages/rspack/src/builtin-plugin/lazy-compilation/middleware.ts @@ -132,7 +132,69 @@ function applyPlugin( plugin.apply(compiler); } -// used for reuse code, do not export this +function readModuleIdsFromBody( + req: IncomingMessage & { body?: unknown }, +): Promise { + // If body is already parsed by another middleware, use it directly + if (req.body !== undefined) { + if (Array.isArray(req.body)) { + return Promise.resolve(req.body); + } + if (typeof req.body === 'string') { + return Promise.resolve(req.body.split('\n').filter(Boolean)); + } + throw new Error('Invalid body type'); + } + + return new Promise((resolve, reject) => { + if ((req as any).aborted || req.destroyed) { + reject(new Error('Request was aborted before body could be read')); + return; + } + + const cleanup = () => { + req.removeListener('data', onData); + req.removeListener('end', onEnd); + req.removeListener('error', onError); + req.removeListener('close', onClose); + req.removeListener('aborted', onAborted); + }; + + const chunks: Buffer[] = []; + const onData = (chunk: Buffer) => { + chunks.push(chunk); + }; + + const onEnd = () => { + cleanup(); + // Concatenate all chunks and decode as UTF-8 to handle multibyte characters correctly + const body = Buffer.concat(chunks).toString('utf8'); + resolve(body.split('\n').filter(Boolean)); + }; + + const onError = (err: Error) => { + cleanup(); + reject(err); + }; + + const onClose = () => { + cleanup(); + reject(new Error('Request was closed before body could be read')); + }; + + const onAborted = () => { + cleanup(); + reject(new Error('Request was aborted before body could be read')); + }; + + req.on('data', onData); + req.on('end', onEnd); + req.on('error', onError); + req.on('close', onClose); + req.on('aborted', onAborted); + }); +} + const lazyCompilationMiddlewareInternal = ( compiler: Compiler | MultiCompiler, activeModules: Set, @@ -140,21 +202,24 @@ const lazyCompilationMiddlewareInternal = ( ): MiddlewareHandler => { const logger = compiler.getInfrastructureLogger('LazyCompilation'); - return (req: IncomingMessage, res: ServerResponse, next?: () => void) => { - if (!req.url?.startsWith(lazyCompilationPrefix)) { - // only handle requests that are come from lazyCompilation + return async ( + req: IncomingMessage, + res: ServerResponse, + next?: () => void, + ) => { + if (!req.url?.startsWith(lazyCompilationPrefix) || req.method !== 'POST') { return next?.(); } - const modules = req.url - .slice(lazyCompilationPrefix.length) - .split('@') - .map(decodeURIComponent); - req.socket.setNoDelay(true); - - res.setHeader('content-type', 'text/event-stream'); - res.writeHead(200); - res.write('\n'); + let modules: string[] = []; + try { + modules = await readModuleIdsFromBody(req); + } catch (err) { + logger.error('Failed to parse request body: ' + err); + res.writeHead(400); + res.end('Bad Request'); + return; + } const moduleActivated = []; for (const key of modules) { @@ -169,5 +234,9 @@ const lazyCompilationMiddlewareInternal = ( if (moduleActivated.length && compiler.watching) { compiler.watching.invalidate(); } + + res.writeHead(200); + res.write('\n'); + res.end(); }; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1374dc8609c2..9ce66a6557b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,13 +18,13 @@ importers: version: 2.3.11 '@microsoft/api-extractor': specifier: 7.55.2 - version: 7.55.2(@types/node@20.19.27) + version: 7.55.2(@types/node@20.19.29) '@microsoft/api-extractor-model': specifier: 7.32.2 - version: 7.32.2(@types/node@20.19.27) + version: 7.32.2(@types/node@20.19.29) '@rslint/core': - specifier: 0.1.13 - version: 0.1.13 + specifier: 0.2.0 + version: 0.2.0 '@rspack/cli': specifier: workspace:* version: link:packages/rspack-cli @@ -38,8 +38,8 @@ importers: specifier: ^3.0.4 version: 3.0.4 '@types/node': - specifier: ^20.19.27 - version: 20.19.27 + specifier: ^20.19.29 + version: 20.19.29 check-dependency-version-consistency: specifier: ^5.0.1 version: 5.0.1 @@ -81,7 +81,7 @@ importers: devDependencies: '@napi-rs/cli': specifier: 3.0.4 - version: 3.0.4(@emnapi/runtime@1.5.0)(@types/node@20.19.27)(emnapi@1.7.1(node-addon-api@7.1.1)) + version: 3.0.4(@emnapi/runtime@1.5.0)(@types/node@20.19.29)(emnapi@1.7.1(node-addon-api@7.1.1)) '@napi-rs/wasm-runtime': specifier: 1.0.7 version: 1.0.7 @@ -127,7 +127,7 @@ importers: devDependencies: '@napi-rs/cli': specifier: 3.0.4 - version: 3.0.4(@emnapi/runtime@1.5.0)(@types/node@20.19.27)(emnapi@1.7.1(node-addon-api@7.1.1)) + version: 3.0.4(@emnapi/runtime@1.5.0)(@types/node@20.19.29)(emnapi@1.7.1(node-addon-api@7.1.1)) '@napi-rs/wasm-runtime': specifier: 1.0.7 version: 1.0.7 @@ -166,11 +166,11 @@ importers: specifier: ^1.6.0 version: 1.6.0(react-refresh@0.18.0) '@types/react': - specifier: ^19.2.7 - version: 19.2.7 + specifier: ^19.2.8 + version: 19.2.8 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.7) + version: 19.2.3(@types/react@19.2.8) react-refresh: specifier: ^0.18.0 version: 0.18.0 @@ -221,8 +221,8 @@ importers: version: 1.7.20 devDependencies: '@rslib/core': - specifier: 0.19.1 - version: 0.19.1(@microsoft/api-extractor@7.55.2(@types/node@20.19.27))(typescript@5.9.3) + specifier: 0.19.2 + version: 0.19.2(@microsoft/api-extractor@7.55.2(@types/node@20.19.29))(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -246,11 +246,11 @@ importers: specifier: ^1.6.0 version: 1.6.0(react-refresh@0.18.0) '@types/react': - specifier: ^19.2.7 - version: 19.2.7 + specifier: ^19.2.8 + version: 19.2.8 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.7) + version: 19.2.3(@types/react@19.2.8) react-refresh: specifier: ^0.18.0 version: 0.18.0 @@ -274,11 +274,11 @@ importers: specifier: ^1.6.0 version: 1.6.0(react-refresh@0.18.0) '@types/react': - specifier: ^19.2.7 - version: 19.2.7 + specifier: ^19.2.8 + version: 19.2.8 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.7) + version: 19.2.3(@types/react@19.2.8) react-refresh: specifier: ^0.18.0 version: 0.18.0 @@ -358,8 +358,8 @@ importers: version: 0.5.17 devDependencies: '@ast-grep/napi': - specifier: ^0.40.4 - version: 0.40.4 + specifier: ^0.40.5 + version: 0.40.5 '@napi-rs/wasm-runtime': specifier: 1.0.7 version: 1.0.7 @@ -367,14 +367,14 @@ importers: specifier: ^1.4.2 version: 1.4.2(@rsbuild/core@1.7.2) '@rslib/core': - specifier: 0.19.1 - version: 0.19.1(@microsoft/api-extractor@7.55.2(@types/node@20.19.27))(typescript@5.9.3) + specifier: 0.19.2 + version: 0.19.2(@microsoft/api-extractor@7.55.2(@types/node@20.19.29))(typescript@5.9.3) '@swc/types': specifier: 0.1.25 version: 0.1.25 '@types/node': - specifier: ^20.19.27 - version: 20.19.27 + specifier: ^20.19.29 + version: 20.19.29 '@types/watchpack': specifier: ^2.4.5 version: 2.4.5 @@ -443,8 +443,8 @@ importers: version: 4.0.0 devDependencies: '@rslib/core': - specifier: 0.19.1 - version: 0.19.1(@microsoft/api-extractor@7.55.2(@types/node@20.19.27))(typescript@5.9.3) + specifier: 0.19.2 + version: 0.19.2(@microsoft/api-extractor@7.55.2(@types/node@20.19.29))(typescript@5.9.3) '@rspack/core': specifier: workspace:* version: link:../rspack @@ -474,7 +474,7 @@ importers: version: 4.0.7 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.19.27)(typescript@5.9.3) + version: 10.9.2(@types/node@20.19.29)(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -482,17 +482,17 @@ importers: packages/rspack-test-tools: dependencies: '@babel/generator': - specifier: 7.28.5 - version: 7.28.5 + specifier: 7.28.6 + version: 7.28.6 '@babel/parser': - specifier: 7.28.5 - version: 7.28.5 + specifier: 7.28.6 + version: 7.28.6 '@babel/traverse': - specifier: 7.28.5 - version: 7.28.5 + specifier: 7.28.6 + version: 7.28.6 '@babel/types': - specifier: 7.28.5 - version: 7.28.5 + specifier: 7.28.6 + version: 7.28.6 chalk: specifier: ^4.1.2 version: 4.1.2 @@ -506,8 +506,8 @@ importers: specifier: ^11.3.3 version: 11.3.3 iconv-lite: - specifier: ^0.7.1 - version: 0.7.1 + specifier: ^0.7.2 + version: 0.7.2 javascript-stringify: specifier: ^2.1.0 version: 2.1.0 @@ -611,7 +611,7 @@ importers: devDependencies: '@codspeed/vitest-plugin': specifier: ^4.0.1 - version: 4.0.1(vite@7.1.7(@types/node@20.19.27)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.19.27)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.0.1(vite@7.1.7(@types/node@20.19.29)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.19.29)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) '@rspack/cli': specifier: workspace:* version: link:../../packages/rspack-cli @@ -622,23 +622,23 @@ importers: specifier: ^1.6.0 version: 1.6.0(react-refresh@0.18.0) '@types/react': - specifier: ^19.2.7 - version: 19.2.7 + specifier: ^19.2.8 + version: 19.2.8 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.7) + version: 19.2.3(@types/react@19.2.8) vitest: specifier: ^3.2.4 - version: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.27)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.29)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) tests/e2e: devDependencies: '@babel/core': - specifier: ^7.28.5 - version: 7.28.5 + specifier: ^7.28.6 + version: 7.28.6 '@babel/preset-react': specifier: ^7.28.5 - version: 7.28.5(@babel/core@7.28.5) + version: 7.28.5(@babel/core@7.28.6) '@module-federation/runtime-tools': specifier: ^0.22.0 version: 0.22.0 @@ -662,7 +662,7 @@ importers: version: 11.0.4 babel-loader: specifier: ^10.0.0 - version: 10.0.0(@babel/core@7.28.5)(webpack@5.102.1) + version: 10.0.0(@babel/core@7.28.6)(webpack@5.102.1) core-js: specifier: 3.47.0 version: 3.47.0 @@ -692,7 +692,7 @@ importers: version: 17.4.5(vue@3.5.26(typescript@5.9.3))(webpack@5.102.1) tailwindcss: specifier: ^3.4.19 - version: 3.4.19(ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3)) + version: 3.4.19(ts-node@10.9.2(@types/node@20.19.29)(typescript@5.9.3)) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -706,11 +706,11 @@ importers: tests/rspack-test: devDependencies: '@babel/core': - specifier: ^7.28.5 - version: 7.28.5 + specifier: ^7.28.6 + version: 7.28.6 '@babel/preset-react': specifier: ^7.28.5 - version: 7.28.5(@babel/core@7.28.5) + version: 7.28.5(@babel/core@7.28.6) '@module-federation/runtime-tools': specifier: ^0.22.0 version: 0.22.0 @@ -754,11 +754,11 @@ importers: specifier: ^21.1.7 version: 21.1.7 '@types/react': - specifier: ^19.2.7 - version: 19.2.7 + specifier: ^19.2.8 + version: 19.2.8 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.7) + version: 19.2.3(@types/react@19.2.8) '@webdiscus/pug-loader': specifier: ^2.11.1 version: 2.11.1(enhanced-resolve@5.18.4)(pug@3.0.3)(webpack@5.102.1) @@ -767,7 +767,7 @@ importers: version: 8.15.0 babel-loader: specifier: ^10.0.0 - version: 10.0.0(@babel/core@7.28.5)(webpack@5.102.1) + version: 10.0.0(@babel/core@7.28.6)(webpack@5.102.1) babel-plugin-import: specifier: ^1.13.8 version: 1.13.8 @@ -931,14 +931,14 @@ importers: website: dependencies: '@rstack-dev/doc-ui': - specifier: 1.12.2 - version: 1.12.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: 1.12.3 + version: 1.12.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) axios: specifier: ^1.13.2 version: 1.13.2 markdown-to-jsx: - specifier: ^9.5.0 - version: 9.5.0(react@19.2.3)(vue@3.5.26(typescript@5.9.3)) + specifier: ^9.5.7 + version: 9.5.7(react@19.2.3)(vue@3.5.26(typescript@5.9.3)) mermaid: specifier: ^11.12.2 version: 11.12.2 @@ -953,38 +953,38 @@ importers: version: 7.7.3 tailwindcss: specifier: ^3.4.19 - version: 3.4.19(ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3)) + version: 3.4.19(ts-node@10.9.2(@types/node@20.19.29)(typescript@5.9.3)) devDependencies: '@rsbuild/plugin-sass': specifier: ^1.4.0 version: 1.4.0(@rsbuild/core@1.7.2) '@rspress/core': - specifier: 2.0.0-rc.4 - version: 2.0.0-rc.4(@types/react@19.2.7) + specifier: 2.0.0-rc.5 + version: 2.0.0-rc.5(@types/react@19.2.8) '@rspress/plugin-algolia': - specifier: 2.0.0-rc.4 - version: 2.0.0-rc.4(@algolia/client-search@5.39.0)(@rspress/core@2.0.0-rc.4(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(search-insights@2.17.3) + specifier: 2.0.0-rc.5 + version: 2.0.0-rc.5(@algolia/client-search@5.39.0)(@rspress/core@2.0.0-rc.5(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(search-insights@2.17.3) '@rspress/plugin-client-redirects': - specifier: 2.0.0-rc.4 - version: 2.0.0-rc.4(@rspress/core@2.0.0-rc.4(@types/react@19.2.7)) + specifier: 2.0.0-rc.5 + version: 2.0.0-rc.5(@rspress/core@2.0.0-rc.5(@types/react@19.2.8)) '@rspress/plugin-llms': - specifier: 2.0.0-rc.4 - version: 2.0.0-rc.4(@rspress/core@2.0.0-rc.4(@types/react@19.2.7)) + specifier: 2.0.0-rc.5 + version: 2.0.0-rc.5(@rspress/core@2.0.0-rc.5(@types/react@19.2.8)) '@rspress/plugin-rss': - specifier: 2.0.0-rc.4 - version: 2.0.0-rc.4(@rspress/core@2.0.0-rc.4(@types/react@19.2.7)) + specifier: 2.0.0-rc.5 + version: 2.0.0-rc.5(@rspress/core@2.0.0-rc.5(@types/react@19.2.8)) '@rspress/plugin-sitemap': - specifier: 2.0.0-rc.4 - version: 2.0.0-rc.4(@rspress/core@2.0.0-rc.4(@types/react@19.2.7)) + specifier: 2.0.0-rc.5 + version: 2.0.0-rc.5(@rspress/core@2.0.0-rc.5(@types/react@19.2.8)) '@shikijs/transformers': specifier: ^3.21.0 version: 3.21.0 '@types/node': - specifier: ^20.19.27 - version: 20.19.27 + specifier: ^20.19.29 + version: 20.19.29 '@types/react': - specifier: ^19.2.7 - version: 19.2.7 + specifier: ^19.2.8 + version: 19.2.8 '@types/semver': specifier: ^7.7.1 version: 7.7.1 @@ -1008,7 +1008,7 @@ importers: version: 1.1.0(@rsbuild/core@1.7.2) rspress-plugin-font-open-sans: specifier: 1.0.3 - version: 1.0.3(@rspress/core@2.0.0-rc.4(@types/react@19.2.7)) + version: 1.0.3(@rspress/core@2.0.0-rc.5(@types/react@19.2.8)) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -1142,8 +1142,8 @@ packages: cpu: [arm64] os: [darwin] - '@ast-grep/napi-darwin-arm64@0.40.4': - resolution: {integrity: sha512-UIkpoEExRghZe5wN6QXGkDzI65zKVoaBQowAzmEd3MCGP8VlAK3FoxDMdy0OLgQVTyRUdBUwG384WpxiWilYEw==} + '@ast-grep/napi-darwin-arm64@0.40.5': + resolution: {integrity: sha512-2F072fGN0WTq7KI3okuEnkGJVEHLbi56Bw1H6NAMf7j2mJJeQWsRyGOMcyNnUXZDeNdvoMH0OB2a5wwUegY/nQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -1154,8 +1154,8 @@ packages: cpu: [x64] os: [darwin] - '@ast-grep/napi-darwin-x64@0.40.4': - resolution: {integrity: sha512-derMkDWiMFjRlcN0SEHXNPeZ67OGR5So6b4r/+ETvZMZavLQv+ER0vevltFS4ci8m402g7l/wVOdSE1oHcN2Iw==} + '@ast-grep/napi-darwin-x64@0.40.5': + resolution: {integrity: sha512-dJMidHZhhxuLBYNi6/FKI812jQ7wcFPSKkVPwviez2D+KvYagapUMAV/4dJ7FCORfguVk8Y0jpPAlYmWRT5nvA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -1166,8 +1166,8 @@ packages: cpu: [arm64] os: [linux] - '@ast-grep/napi-linux-arm64-gnu@0.40.4': - resolution: {integrity: sha512-1CeDsK6WRMz169mTXLfXdn2GkQAsMkYbqGd7mHDa2VqutJwDYrqe6t4QiFAlr+LRT2bQuExpPh3AiC8BNd6UQQ==} + '@ast-grep/napi-linux-arm64-gnu@0.40.5': + resolution: {integrity: sha512-nBRCbyoS87uqkaw4Oyfe5VO+SRm2B+0g0T8ME69Qry9ShMf41a2bTdpcQx9e8scZPogq+CTwDHo3THyBV71l9w==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -1178,8 +1178,8 @@ packages: cpu: [arm64] os: [linux] - '@ast-grep/napi-linux-arm64-musl@0.40.4': - resolution: {integrity: sha512-VqjL9Xbq5NNXexY4rluaFtpuHHGbcNIwFXInbm8hdaZS3Rsr9tz+QCEhDNO4IJgPtcRUOpa7AIztZotwxrb/iw==} + '@ast-grep/napi-linux-arm64-musl@0.40.5': + resolution: {integrity: sha512-/qKsmds5FMoaEj6FdNzepbmLMtlFuBLdrAn9GIWCqOIcVcYvM1Nka8+mncfeXB/MFZKOrzQsQdPTWqrrQzXLrA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -1190,8 +1190,8 @@ packages: cpu: [x64] os: [linux] - '@ast-grep/napi-linux-x64-gnu@0.40.4': - resolution: {integrity: sha512-6BrPYjP+Gr+mkI3z3Xh/UHkOedJ25qKGdhRwbYteeK/QX3rWeuuo/tUjow4nh/8ewhk04wHGw/G96OiF47ICjA==} + '@ast-grep/napi-linux-x64-gnu@0.40.5': + resolution: {integrity: sha512-DP4oDbq7f/1A2hRTFLhJfDFR6aI5mRWdEfKfHzRItmlKsR9WlcEl1qDJs/zX9R2EEtIDsSKRzuJNfJllY3/W8Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -1202,8 +1202,8 @@ packages: cpu: [x64] os: [linux] - '@ast-grep/napi-linux-x64-musl@0.40.4': - resolution: {integrity: sha512-4LGq6xYmOsgEycx4Cu9CdyNYa99O/C1bOsZi09T0C/NifKR+55v8og6nZrGCwsKEs4jWresdal4p7WXw3k8+0g==} + '@ast-grep/napi-linux-x64-musl@0.40.5': + resolution: {integrity: sha512-BRZUvVBPUNpWPo6Ns8chXVzxHPY+k9gpsubGTHy92Q26ecZULd/dTkWWdnvfhRqttsSQ9Pe/XQdi5+hDQ6RYcg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -1214,8 +1214,8 @@ packages: cpu: [arm64] os: [win32] - '@ast-grep/napi-win32-arm64-msvc@0.40.4': - resolution: {integrity: sha512-QrI9m9wmFYTRnyTsYOX+9/D/B+h0eyFqZf2qh/U3kbRU/ZZJnxI75YjYVjtoa2QI4oEJlzYb0QhKAN8NKIDYRA==} + '@ast-grep/napi-win32-arm64-msvc@0.40.5': + resolution: {integrity: sha512-y95zSEwc7vhxmcrcH0GnK4ZHEBQrmrszRBNQovzaciF9GUqEcCACNLoBesn4V47IaOp4fYgD2/EhGRTIBFb2Ug==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -1226,8 +1226,8 @@ packages: cpu: [ia32] os: [win32] - '@ast-grep/napi-win32-ia32-msvc@0.40.4': - resolution: {integrity: sha512-JLhcgCUD7e74rZ39XLEfUOU26TQboJltzHir2oSpvjZPtq4aBHEJOynq7dGABZdMrzRUOPdbBdM+nFzPiU38iQ==} + '@ast-grep/napi-win32-ia32-msvc@0.40.5': + resolution: {integrity: sha512-K/u8De62iUnFCzVUs7FBdTZ2Jrgc5/DLHqjpup66KxZ7GIM9/HGME/O8aSoPkpcAeCD4TiTZ11C1i5p5H98hTg==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -1238,8 +1238,8 @@ packages: cpu: [x64] os: [win32] - '@ast-grep/napi-win32-x64-msvc@0.40.4': - resolution: {integrity: sha512-DdsqVs/kg4Iun8GU8GoOw9L9WkT2pVLAZ+1hV9t+PsdI6SR3HGRIUIJSzKz2uejr8tY8evTZmdjBWyjUav8qbQ==} + '@ast-grep/napi-win32-x64-msvc@0.40.5': + resolution: {integrity: sha512-dqm5zg/o4Nh4VOQPEpMS23ot8HVd22gG0eg01t4CFcZeuzyuSgBlOL3N7xLbz3iH2sVkk7keuBwAzOIpTqziNQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1248,28 +1248,36 @@ packages: resolution: {integrity: sha512-Hb4o6h1Pf6yRUAX07DR4JVY7dmQw+RVQMW5/m55GoiAT/VRoKCWBtIUPPOnqDVhbx1Cjfil9b6EDrgJsUAujEQ==} engines: {node: '>= 10'} - '@ast-grep/napi@0.40.4': - resolution: {integrity: sha512-unRhSrSn4X0tf7nCuj300rBrlrXqtGbKanFX75CNmn2NM+NyPrdvq1tdDk2F+XA8Z574MynpSeCESii3WBK+bw==} + '@ast-grep/napi@0.40.5': + resolution: {integrity: sha512-hJA62OeBKUQT68DD2gDyhOqJxZxycqg8wLxbqjgqSzYttCMSDL9tiAQ9abgekBYNHudbJosm9sWOEbmCDfpX2A==} engines: {node: '>= 10'} '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.28.6': + resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.4': resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.6': + resolution: {integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==} + engines: {node: '>=6.9.0'} + '@babel/core@7.28.4': resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.5': - resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + '@babel/core@7.28.6': + resolution: {integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.5': - resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + '@babel/generator@7.28.6': + resolution: {integrity: sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==} engines: {node: '>=6.9.0'} '@babel/helper-annotate-as-pure@7.27.3': @@ -1280,6 +1288,10 @@ packages: resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} @@ -1288,12 +1300,22 @@ packages: resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-transforms@7.28.3': resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-plugin-utils@7.27.1': resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} @@ -1302,10 +1324,6 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} @@ -1318,8 +1336,12 @@ packages: resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.5': - resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + '@babel/helpers@7.28.6': + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.6': + resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -1448,12 +1470,16 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.5': - resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.6': + resolution: {integrity: sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.5': - resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + '@babel/types@7.28.6': + resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} engines: {node: '>=6.9.0'} '@biomejs/biome@2.3.11': @@ -2436,39 +2462,21 @@ packages: '@microsoft/tsdoc@0.16.0': resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} - '@module-federation/error-codes@0.21.6': - resolution: {integrity: sha512-MLJUCQ05KnoVl8xd6xs9a5g2/8U+eWmVxg7xiBMeR0+7OjdWUbHwcwgVFatRIwSZvFgKHfWEiI7wsU1q1XbTRQ==} - '@module-federation/error-codes@0.22.0': resolution: {integrity: sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==} - '@module-federation/runtime-core@0.21.6': - resolution: {integrity: sha512-5Hd1Y5qp5lU/aTiK66lidMlM/4ji2gr3EXAtJdreJzkY+bKcI5+21GRcliZ4RAkICmvdxQU5PHPL71XmNc7Lsw==} - '@module-federation/runtime-core@0.22.0': resolution: {integrity: sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==} - '@module-federation/runtime-tools@0.21.6': - resolution: {integrity: sha512-fnP+ZOZTFeBGiTAnxve+axGmiYn2D60h86nUISXjXClK3LUY1krUfPgf6MaD4YDJ4i51OGXZWPekeMe16pkd8Q==} - '@module-federation/runtime-tools@0.22.0': resolution: {integrity: sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==} - '@module-federation/runtime@0.21.6': - resolution: {integrity: sha512-+caXwaQqwTNh+CQqyb4mZmXq7iEemRDrTZQGD+zyeH454JAYnJ3s/3oDFizdH6245pk+NiqDyOOkHzzFQorKhQ==} - '@module-federation/runtime@0.22.0': resolution: {integrity: sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==} - '@module-federation/sdk@0.21.6': - resolution: {integrity: sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==} - '@module-federation/sdk@0.22.0': resolution: {integrity: sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==} - '@module-federation/webpack-bundler-runtime@0.21.6': - resolution: {integrity: sha512-7zIp3LrcWbhGuFDTUMLJ2FJvcwjlddqhWGxi/MW3ur1a+HaO8v5tF2nl+vElKmbG1DFLU/52l3PElVcWf/YcsQ==} - '@module-federation/webpack-bundler-runtime@0.22.0': resolution: {integrity: sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==} @@ -3195,11 +3203,6 @@ packages: cpu: [x64] os: [win32] - '@rsbuild/core@1.6.15': - resolution: {integrity: sha512-LvoOF53PL6zXgdzEhgnnP51S4FseDFH1bHrobK4EK6zZX/tN8qgf5tdlmN7h4OkMv/Qs1oUfvj0QcLWSstnnvA==} - engines: {node: '>=18.12.0'} - hasBin: true - '@rsbuild/core@1.7.1': resolution: {integrity: sha512-ULIE/Qh+Ne80Pm/aUPbRHUvwvIzpap07jYNFB47azI8w5Q3sDEC4Gn574jsluT/42iNDsZTFADRBog9FEvtN9Q==} engines: {node: '>=18.12.0'} @@ -3228,8 +3231,8 @@ packages: peerDependencies: '@rsbuild/core': 1.x - '@rslib/core@0.19.1': - resolution: {integrity: sha512-Fz+uknjf9BRE6tNax7zcZOZ8FX3FLT5kKcR10vHdo6IgCaMZpJxQmM62NNfv21kdy5u1PdIRgKFVpqP2QTarfg==} + '@rslib/core@0.19.2': + resolution: {integrity: sha512-eoRR1tQqkEiq3ijBT170KpxnBM7nf3/sanAUoJhyCeyLKKkGpQW6nr1cKHfZmkEizqSvLyjvLFA7fdIfK7pCMw==} engines: {node: '>=18.12.0'} hasBin: true peerDependencies: @@ -3241,45 +3244,40 @@ packages: typescript: optional: true - '@rslint/core@0.1.13': - resolution: {integrity: sha512-4K/5/aZFS4sz4fZ2hdNFFLTwgpnxTkaDo1vC+fBltDMe9G0Jp0ELhesM1nbYPa3f+JsN8xE8BU8XseJjW6igYQ==} + '@rslint/core@0.2.0': + resolution: {integrity: sha512-g9EXVfr5Y5ZDAMSzRA8YOPGrhsUCF1YvVb8XrX9Ft6vX6PPi3RiL+zbDoaOYPx4V0tdHrXs68S8jsasJvld17Q==} hasBin: true - '@rslint/darwin-arm64@0.1.13': - resolution: {integrity: sha512-UpZWLD6B9pZ+uXjZ0i+nkXbZKHPatd/6FNroF7q6qRSnFdix+moxaqgzhHg+9Eq4rnVc7aL1VmduRTh40bH7xg==} + '@rslint/darwin-arm64@0.2.0': + resolution: {integrity: sha512-dn5zEWBpW6VUUi4KdrLEcbzQLxvZ2TsdMg42+e2QRcYvu66/65WXOyZQJ8fbQEEST0tkpa154zgPDG3fDW3okQ==} cpu: [arm64] os: [darwin] - '@rslint/darwin-x64@0.1.13': - resolution: {integrity: sha512-o9BocB82RglcoI0Cnz3Stx5SdBTzjZb3ZY+EFcsom4RTEna2QVlQhG4Y5NogicfzPJvudvnl2RREPuNuWiBTpw==} + '@rslint/darwin-x64@0.2.0': + resolution: {integrity: sha512-6zWm09teGePwAo8s3dVix4TwFDKl2gizfrHOraGs4fxAjm42tbK1m6d5GKKFOdpnSqEaV/sPYYej9lCJKpr3Rw==} cpu: [x64] os: [darwin] - '@rslint/linux-arm64@0.1.13': - resolution: {integrity: sha512-4vBIDkFS9e1239VNbwWe5c35D9pxM0RrxXidVV2jmv2/O0kOrcKdx0m+n6RiD/0EHfboe8dUd2AleRQpHiDZqQ==} + '@rslint/linux-arm64@0.2.0': + resolution: {integrity: sha512-WTvS2ULWFjrBssZIKApw0353DFlTcq9LqTDK6987YJcKlwn9Rgs07nLSMgstdWrB5yIrTKprFGPEvMtiJtrrBw==} cpu: [arm64] os: [linux] - '@rslint/linux-x64@0.1.13': - resolution: {integrity: sha512-HQG/GeN5YggyPUJIvdbVrIqSiiR/X+NSKSCpw719Ttbl9Vw6juk4tUskCjIDVgl5lhpfiqArn4gVT4EPNSd7XQ==} + '@rslint/linux-x64@0.2.0': + resolution: {integrity: sha512-woIFlScbGDzX8pCexdIKG7uwq9nP3DDmUHfVQ/Ri2wzM4gH6dEKagf4tmbRay7jiVSp28SK8KAmtabvw3kxLKw==} cpu: [x64] os: [linux] - '@rslint/win32-arm64@0.1.13': - resolution: {integrity: sha512-oxfgWQMePJ6QjhG1Q7M527D2neunbuUpYy/HFDS8kdhTYPuHRsMUG2I5eIkdoumBC36FuGWKRaKJBZuKfWwskg==} + '@rslint/win32-arm64@0.2.0': + resolution: {integrity: sha512-U+1UdG0RUFT8rv1lRqrf+tpXLhTc0MhVYBmSshEPlaain0PSdGQ6f3Cq8njtqBk7smI09+wayLBro8Fk/eOkYg==} cpu: [arm64] os: [win32] - '@rslint/win32-x64@0.1.13': - resolution: {integrity: sha512-fxUw0sYpuy+Op1096/zddo4fqNBOC//NOp3c85uiLie3SMyQ3HtPp4dnW7e9CLmc6EcXr24fMkQI0BEHHpN7Ow==} + '@rslint/win32-x64@0.2.0': + resolution: {integrity: sha512-TDaaghoT4i0VCfpQMN8STzQJThiLoUkgNJDPXvuscQ2FeiX5WE3NT/NES+sq9VwwAelrKjQlL+TDw4kzRR/wcg==} cpu: [x64] os: [win32] - '@rspack/binding-darwin-arm64@1.6.8': - resolution: {integrity: sha512-e8CTQtzaeGnf+BIzR7wRMUwKfIg0jd/sxMRc1Vd0bCMHBhSN9EsGoMuJJaKeRrSmy2nwMCNWHIG+TvT1CEKg+A==} - cpu: [arm64] - os: [darwin] - '@rspack/binding-darwin-arm64@1.7.0': resolution: {integrity: sha512-HMYrhvVh3sMRBXl6cSI2JqsvlHJKQ42qX+Sw4qbj7LeZBN6Gv4GjfL3cXRLUTdO37FOC0uLEUYgxVXetx/Y4sA==} cpu: [arm64] @@ -3290,11 +3288,6 @@ packages: cpu: [arm64] os: [darwin] - '@rspack/binding-darwin-x64@1.6.8': - resolution: {integrity: sha512-ku1XpTEPt6Za11zhpFWhfwrTQogcgi9RJrOUVC4FESiPO9aKyd4hJ+JiPgLY0MZOqsptK6vEAgOip+uDVXrCpg==} - cpu: [x64] - os: [darwin] - '@rspack/binding-darwin-x64@1.7.0': resolution: {integrity: sha512-R/SoR04ySmHPqoIBGC+SjP9zRGjL1fS908mdwBvQ1RfFinKu7a/o/5rxH/vxUUsVQrHCyX+o7YXpfWq9xpvyQA==} cpu: [x64] @@ -3305,11 +3298,6 @@ packages: cpu: [x64] os: [darwin] - '@rspack/binding-linux-arm64-gnu@1.6.8': - resolution: {integrity: sha512-fvZX6xZPvBT8qipSpvkKMX5M7yd2BSpZNCZXcefw6gA3uC7LI3gu+er0LrDXY1PtPzVuHTyDx+abwWpagV3PiQ==} - cpu: [arm64] - os: [linux] - '@rspack/binding-linux-arm64-gnu@1.7.0': resolution: {integrity: sha512-jDCcso++qshu58+Iuo6oiL0XKuX04lDugL0qwrWHW8SS/EjZ2rc1J3yQx+XDW0PCQsfI2c9ji0IOW56PzW1hXQ==} cpu: [arm64] @@ -3320,11 +3308,6 @@ packages: cpu: [arm64] os: [linux] - '@rspack/binding-linux-arm64-musl@1.6.8': - resolution: {integrity: sha512-++XMKcMNrt59HcFBLnRaJcn70k3X0GwkAegZBVpel8xYIAgvoXT5+L8P1ExId/yTFxqedaz8DbcxQnNmMozviw==} - cpu: [arm64] - os: [linux] - '@rspack/binding-linux-arm64-musl@1.7.0': resolution: {integrity: sha512-0W49s0SQQhr3hZ8Zd7Auyf2pv4OTBr6wQhgWUQ6XeeMEjB16KpAVypSK5Jpn1ON0v9jAPLdod+a255rz8/f3kg==} cpu: [arm64] @@ -3335,11 +3318,6 @@ packages: cpu: [arm64] os: [linux] - '@rspack/binding-linux-x64-gnu@1.6.8': - resolution: {integrity: sha512-tv3BWkTE1TndfX+DsE1rSTg8fBevCxujNZ3MlfZ22Wfy9x1FMXTJlWG8VIOXmaaJ1wUHzv8S7cE2YUUJ2LuiCg==} - cpu: [x64] - os: [linux] - '@rspack/binding-linux-x64-gnu@1.7.0': resolution: {integrity: sha512-oFjzjTD1MmG0ucAaP0Wyg9eobrsnFwZjEHa7LwyzWDRBeC3GWAF9T04Bqd6Ba6DgASGzU0BjEJcUpjvtXxO95Q==} cpu: [x64] @@ -3350,11 +3328,6 @@ packages: cpu: [x64] os: [linux] - '@rspack/binding-linux-x64-musl@1.6.8': - resolution: {integrity: sha512-DCGgZ5/in1O3FjHWqXnDsncRy+48cMhfuUAAUyl0yDj1NpsZu9pP+xfGLvGcQTiYrVl7IH9Aojf1eShP/77WGA==} - cpu: [x64] - os: [linux] - '@rspack/binding-linux-x64-musl@1.7.0': resolution: {integrity: sha512-MNGslPLOsurdwOcoo6r0u8mLpw1ADar3hkx67WzwwMqYnem/Ky0aANJC2JvQHPC22mu01gCOukHYyEaUFTxcuw==} cpu: [x64] @@ -3365,10 +3338,6 @@ packages: cpu: [x64] os: [linux] - '@rspack/binding-wasm32-wasi@1.6.8': - resolution: {integrity: sha512-VUwdhl/lI4m6o1OGCZ9JwtMjTV/yLY5VZTQdEPKb40JMTlmZ5MBlr5xk7ByaXXYHr6I+qnqEm73iMKQvg6iknw==} - cpu: [wasm32] - '@rspack/binding-wasm32-wasi@1.7.0': resolution: {integrity: sha512-eaZzkGpxzVESmaX/UALMiQO+eNppe/i1VWQksGRfdoUu0rILqr/YDjsWFTcpbI9Dt3fg2kshHawBHxfwtxHcZQ==} cpu: [wasm32] @@ -3377,11 +3346,6 @@ packages: resolution: {integrity: sha512-2r9M5iVchmsFkp3sz7A5YnMm2TfpkB71LK3AoaRWKMfvf5oFky0GSGISYd2TCBASO+X2Qskaq+B24Szo8zH5FA==} cpu: [wasm32] - '@rspack/binding-win32-arm64-msvc@1.6.8': - resolution: {integrity: sha512-23YX7zlOZlub+nPGDBUzktb4D5D6ETUAluKjXEeHIZ9m7fSlEYBnGL66YE+3t1DHXGd0OqsdwlvrNGcyo6EXDQ==} - cpu: [arm64] - os: [win32] - '@rspack/binding-win32-arm64-msvc@1.7.0': resolution: {integrity: sha512-XFg4l7sOhupnpG0soOfzYLeF2cgpSJMenmjmdzd9y06CotTyVId0hNoS7y+A7hEP8XGf3YPbdiUL5UDp6+DRBA==} cpu: [arm64] @@ -3392,11 +3356,6 @@ packages: cpu: [arm64] os: [win32] - '@rspack/binding-win32-ia32-msvc@1.6.8': - resolution: {integrity: sha512-cFgRE3APxrY4AEdooVk2LtipwNNT/9mrnjdC5lVbsIsz+SxvGbZR231bxDJEqP15+RJOaD07FO1sIjINFqXMEg==} - cpu: [ia32] - os: [win32] - '@rspack/binding-win32-ia32-msvc@1.7.0': resolution: {integrity: sha512-eWt2XV6la/c0IlU/18RlhQsqwHGShSypwA3kt4s/dpfOK0YB1h4f0fYeUZuvj2X0MIoJQGhMofMrgA35/IcAcw==} cpu: [ia32] @@ -3407,11 +3366,6 @@ packages: cpu: [ia32] os: [win32] - '@rspack/binding-win32-x64-msvc@1.6.8': - resolution: {integrity: sha512-cIuhVsZYd3o3Neo1JSAhJYw6BDvlxaBoqvgwRkG1rs0ExFmEmgYyG7ip9pFKnKNWph/tmW3rDYypmEfjs1is7g==} - cpu: [x64] - os: [win32] - '@rspack/binding-win32-x64-msvc@1.7.0': resolution: {integrity: sha512-LOL5G8rfbAwlmusx+t98r9QzuGRz+L9Bg+8s5s6K/Qe64iemcNIuxGr5QLVq1jLa0SGNTeog4N21pAzlkWh4jw==} cpu: [x64] @@ -3422,24 +3376,12 @@ packages: cpu: [x64] os: [win32] - '@rspack/binding@1.6.8': - resolution: {integrity: sha512-lUeL4mbwGo+nqRKqFDCm9vH2jv9FNMVt1X8jqayWRcOCPlj/2UVMEFgqjR7Pp2vlvnTKq//31KbDBJmDZq31RQ==} - '@rspack/binding@1.7.0': resolution: {integrity: sha512-xO+pZKG2dvU9CuRTTi+DcCc4p+CZhBJlvuYikBja/0a62cTntQV2PWV+/xU1a6Vbo89yNz158LR05nvjtKVwTw==} '@rspack/binding@1.7.1': resolution: {integrity: sha512-qVTV1/UWpMSZktvK5A8+HolgR1Qf0nYR3Gg4Vax5x3/BcHDpwGZ0fbdFRUirGVWH/XwxZ81zoI6F2SZq7xbX+w==} - '@rspack/core@1.6.8': - resolution: {integrity: sha512-FolcIAH5FW4J2FET+qwjd1kNeFbCkd0VLuIHO0thyolEjaPSxw5qxG67DA7BZGm6PVcoiSgPLks1DL6eZ8c+fA==} - engines: {node: '>=18.12.0'} - peerDependencies: - '@swc/helpers': '>=0.5.1' - peerDependenciesMeta: - '@swc/helpers': - optional: true - '@rspack/core@1.7.0': resolution: {integrity: sha512-uDxPQsPh/+2DnOISuKnUiXZ9M0y2G1BOsI0IesxPJGp42ME2QW7axbJfUqD3bwp4bi3RN2zqh56NgxU/XETQvA==} engines: {node: '>=18.12.0'} @@ -3482,8 +3424,8 @@ packages: webpack-hot-middleware: optional: true - '@rspress/core@2.0.0-rc.4': - resolution: {integrity: sha512-EHJjbc8yA/La6sJN3bjZus24KhSCdh5lEIVtjt7EBEnLteDN+wULzO1sFKj8agH3BXYE0XOuz2W1HbTTGfcGJQ==} + '@rspress/core@2.0.0-rc.5': + resolution: {integrity: sha512-T5WF7CxAAlt0HAeugbaQAk0BKm3NGrfaxYyrij7nLHRSBZPkY4sO8p3IHfLfmxKCvFogpskmd5F+hBXhow7rxg==} engines: {node: '>=20.9.0'} hasBin: true @@ -3539,45 +3481,41 @@ packages: resolution: {integrity: sha512-NpNhTKBIlV3O6ADhoZkgHvBFvXMW2TYlIWmIT1ysJESUBqDpaN9H3Teve5fugjU2pQ2ORBZO6SQGKliMw/8m/Q==} engines: {node: '>= 10'} - '@rspress/plugin-algolia@2.0.0-rc.4': - resolution: {integrity: sha512-rW/XRs/W7llAwdYusBqMLt8jo39+oYtdtMpyrRq/d3h0WE/xXMSSnAp24BrcjE6forWke4pxra1I20wUeGcRGw==} + '@rspress/plugin-algolia@2.0.0-rc.5': + resolution: {integrity: sha512-+SFsyqqN6ykeFPXng0ZcaBEERscvXnlihpmX87fwbwR56NFlHl+5mPaTFbsCQInlEViFXY9Fbcl6OqblF2ATRg==} engines: {node: '>=20.9.0'} peerDependencies: - '@rspress/core': ^2.0.0-rc.4 + '@rspress/core': ^2.0.0-rc.5 - '@rspress/plugin-client-redirects@2.0.0-rc.4': - resolution: {integrity: sha512-hBoXexZUk9HB2NR+QWH9E2oDZe887tq5wsqG0crZlLL1ehyShsVXhofUH9ROciScmWYfJvcbTfdvTnIWTrrk8g==} + '@rspress/plugin-client-redirects@2.0.0-rc.5': + resolution: {integrity: sha512-RRbZ2Hw1l5JpkFUDcghaBP8V6WP3p0Q2ka0A/NaB8Jx/5oDIl8SVO4e1Pqyyi7SYIq5Byq9wO2TBd/PJ5RKm9w==} engines: {node: '>=20.9.0'} peerDependencies: - '@rspress/core': ^2.0.0-rc.4 + '@rspress/core': ^2.0.0-rc.5 - '@rspress/plugin-llms@2.0.0-rc.4': - resolution: {integrity: sha512-PSG8JOO2EOqCeViif48puAeiP2pBhe//v0sC8dm9wGmQsrq8xbx2rH1aiQElZIwcoximqukzHavrU6O3qPW2NA==} + '@rspress/plugin-llms@2.0.0-rc.5': + resolution: {integrity: sha512-7FevOhOsPBMAln/vgxY0gE0oQIIUdATE91d1zof5LgUuocV26od362KTlvAzOK0VsFKTwuJGEOgx2uDU6LzejA==} engines: {node: '>=20.9.0'} peerDependencies: - '@rspress/core': ^2.0.0-rc.4 + '@rspress/core': ^2.0.0-rc.5 - '@rspress/plugin-rss@2.0.0-rc.4': - resolution: {integrity: sha512-eF6926NnQGV2SPhbz6RruiGnBgcjsDW+ow5sVePWdltHUQdo1wQCnLUnJdaCjek5Jx+wtJViNMT2dhXu/NUVYQ==} + '@rspress/plugin-rss@2.0.0-rc.5': + resolution: {integrity: sha512-TGIC/36+yyJofF2nkWUs3cNmjPASl6Wk+breT1RxBbXGV5NkDVipqxgGoB4JrABKZMi2wHP3CPFzqagWpjsPdw==} engines: {node: '>=20.9.0'} peerDependencies: - '@rspress/core': ^2.0.0-rc.4 + '@rspress/core': ^2.0.0-rc.5 - '@rspress/plugin-sitemap@2.0.0-rc.4': - resolution: {integrity: sha512-sr900krxs9ZVfbV+b/ig7t8mAPaMOonWz2Qj9zXg4TgEgnh+LnNtHQpHYeFbiTF5WAozVxdu5lJ1E36oh231pQ==} + '@rspress/plugin-sitemap@2.0.0-rc.5': + resolution: {integrity: sha512-jUPMiwoYMTozv3swMlo/n4vws9uoHpGJNz/T+C/EkVZpgFxO4h8qJDQrFczdxPKWnhKNnBPFoBHBfKTutb78Eg==} engines: {node: '>=20.9.0'} peerDependencies: - '@rspress/core': ^2.0.0-rc.4 + '@rspress/core': ^2.0.0-rc.5 - '@rspress/runtime@2.0.0-rc.4': - resolution: {integrity: sha512-BcNs6zpIXcWfhXGCDS525HDwSgLXO+S+q2Ty6sQPmyYJzOg+V9tHIlulKTOfuBCyEhTZeTAJofP4+rG4EZBUgw==} - engines: {node: '>=20.9.0'} + '@rspress/shared@2.0.0-rc.5': + resolution: {integrity: sha512-bPM+3hkh923HEq7UKbeY4CCokeh1ilXGvk+Kxm1LwT51CJH5Jt3aKhzzWLr//+OuaFcSUaO0sWiUQbJU7JaqWA==} - '@rspress/shared@2.0.0-rc.4': - resolution: {integrity: sha512-46jTwxV8SRLMbe3euCEMWQudmdYE2Tf+EN/5l6EP10i6QICOXMIzUFFWfr0LOTr4aZCSTSJu7Tp6Xxml6JbheA==} - - '@rstack-dev/doc-ui@1.12.2': - resolution: {integrity: sha512-4C+tfhODxCp81ohCik9baOdbhYGNFqdwcwQMAESncF0YX3EawdNCORI1E26DqkY/F3ggfKG4qOlEAu+oOxrPxg==} + '@rstack-dev/doc-ui@1.12.3': + resolution: {integrity: sha512-5W70pjRxxwyNT3R4kTYDE8cPaMjsJKXMeZQn7+Q54+RCJ1ahN4pADnpaY7WvSEBWkjXdI4IR4GGvBs7nSU/8MA==} '@rstest/core@0.7.9': resolution: {integrity: sha512-RHNPS1MDUxtf+1Z0YZi+vIQ13SdvCbcbpzDA7XHcPziTRy2mAPg8nfcms+XzbIp95KXH75ucAhgAKNFO0QgARA==} @@ -3909,8 +3847,8 @@ packages: '@types/node-forge@1.3.14': resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} - '@types/node@20.19.27': - resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} + '@types/node@20.19.29': + resolution: {integrity: sha512-YrT9ArrGaHForBaCNwFjoqJWmn8G1Pr7+BH/vwyLHciA9qT/wSiuOhxGCT50JA5xLvFBd6PIiGkE3afxcPE1nw==} '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -3923,8 +3861,8 @@ packages: peerDependencies: '@types/react': ^19.2.0 - '@types/react@19.2.7': - resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + '@types/react@19.2.8': + resolution: {integrity: sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==} '@types/retry@0.12.2': resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} @@ -3974,8 +3912,8 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - '@unhead/react@2.0.19': - resolution: {integrity: sha512-pW00tkOneGGTEJp5UeVkVWmti4VecLj0rIje5AqcBs0AoglSxc18LGGKi9Exd098++GzVouhkGo1Ch02YnZS7g==} + '@unhead/react@2.1.2': + resolution: {integrity: sha512-VNKa0JJZq5Jp28VuiOMfjAA7CTLHI0SdW/Hs1ZPq2PsNV/cgxGv8quFBGXWx4gfoHB52pejO929RKjIpYX5+iQ==} peerDependencies: react: '>=18.3.1' @@ -5804,8 +5742,8 @@ packages: hmac-drbg@1.0.1: resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} - hookable@5.5.3: - resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + hookable@6.0.1: + resolution: {integrity: sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==} hpack.js@2.1.6: resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} @@ -5917,8 +5855,8 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.1: - resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} icss-utils@5.1.0: @@ -6434,8 +6372,8 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - markdown-to-jsx@9.5.0: - resolution: {integrity: sha512-6PhWz9md8btrE52LDM4KHSzXf2HKCG+2oGNVw/eQhMP6h51dfZVHb9mt1zWYvQ7wM+xOK8WWaijzEhjB0qIpUg==} + markdown-to-jsx@9.5.7: + resolution: {integrity: sha512-0RO+9IgcURudRI6tmsKFt0S9fCHeUMFzfWmJ84XOKOQ+CEb8pARtWHAxpCPmlbDIs2HAlOXjJKJz4oOwAZBGRg==} engines: {node: '>= 18'} peerDependencies: react: '>= 16.0.0' @@ -7514,8 +7452,8 @@ packages: rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} - rsbuild-plugin-dts@0.19.1: - resolution: {integrity: sha512-LaK1RAvHPrb/ulfwvZckeui5qkQSKxf1RD7rzBTgK2UY8fMTQgsxsvun/XzfzAOKqdagzIohhnmNd5+j2Niqww==} + rsbuild-plugin-dts@0.19.2: + resolution: {integrity: sha512-neuTRt+H/isd2FDYMigF5TEoLUoR/hF0RKdVv1U7nDz0CfLRUfL+NnxePv9nS7dU2VvM3CYcLH7tZs43ol7g4Q==} engines: {node: '>=18.12.0'} peerDependencies: '@microsoft/api-extractor': ^7 @@ -8288,8 +8226,8 @@ packages: resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} engines: {node: '>=14.0'} - unhead@2.0.19: - resolution: {integrity: sha512-gEEjkV11Aj+rBnY6wnRfsFtF2RxKOLaPN4i+Gx3UhBxnszvV6ApSNZbGk7WKyy/lErQ6ekPN63qdFL7sa1leow==} + unhead@2.1.2: + resolution: {integrity: sha512-vSihrxyb+zsEUfEbraZBCjdE0p/WSoc2NGDrpwwSNAwuPxhYK1nH3eegf02IENLpn1sUhL8IoO84JWmRQ6tILA==} unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} @@ -8919,55 +8857,55 @@ snapshots: '@ast-grep/napi-darwin-arm64@0.37.0': optional: true - '@ast-grep/napi-darwin-arm64@0.40.4': + '@ast-grep/napi-darwin-arm64@0.40.5': optional: true '@ast-grep/napi-darwin-x64@0.37.0': optional: true - '@ast-grep/napi-darwin-x64@0.40.4': + '@ast-grep/napi-darwin-x64@0.40.5': optional: true '@ast-grep/napi-linux-arm64-gnu@0.37.0': optional: true - '@ast-grep/napi-linux-arm64-gnu@0.40.4': + '@ast-grep/napi-linux-arm64-gnu@0.40.5': optional: true '@ast-grep/napi-linux-arm64-musl@0.37.0': optional: true - '@ast-grep/napi-linux-arm64-musl@0.40.4': + '@ast-grep/napi-linux-arm64-musl@0.40.5': optional: true '@ast-grep/napi-linux-x64-gnu@0.37.0': optional: true - '@ast-grep/napi-linux-x64-gnu@0.40.4': + '@ast-grep/napi-linux-x64-gnu@0.40.5': optional: true '@ast-grep/napi-linux-x64-musl@0.37.0': optional: true - '@ast-grep/napi-linux-x64-musl@0.40.4': + '@ast-grep/napi-linux-x64-musl@0.40.5': optional: true '@ast-grep/napi-win32-arm64-msvc@0.37.0': optional: true - '@ast-grep/napi-win32-arm64-msvc@0.40.4': + '@ast-grep/napi-win32-arm64-msvc@0.40.5': optional: true '@ast-grep/napi-win32-ia32-msvc@0.37.0': optional: true - '@ast-grep/napi-win32-ia32-msvc@0.40.4': + '@ast-grep/napi-win32-ia32-msvc@0.40.5': optional: true '@ast-grep/napi-win32-x64-msvc@0.37.0': optional: true - '@ast-grep/napi-win32-x64-msvc@0.40.4': + '@ast-grep/napi-win32-x64-msvc@0.40.5': optional: true '@ast-grep/napi@0.37.0': @@ -8982,37 +8920,45 @@ snapshots: '@ast-grep/napi-win32-ia32-msvc': 0.37.0 '@ast-grep/napi-win32-x64-msvc': 0.37.0 - '@ast-grep/napi@0.40.4': + '@ast-grep/napi@0.40.5': optionalDependencies: - '@ast-grep/napi-darwin-arm64': 0.40.4 - '@ast-grep/napi-darwin-x64': 0.40.4 - '@ast-grep/napi-linux-arm64-gnu': 0.40.4 - '@ast-grep/napi-linux-arm64-musl': 0.40.4 - '@ast-grep/napi-linux-x64-gnu': 0.40.4 - '@ast-grep/napi-linux-x64-musl': 0.40.4 - '@ast-grep/napi-win32-arm64-msvc': 0.40.4 - '@ast-grep/napi-win32-ia32-msvc': 0.40.4 - '@ast-grep/napi-win32-x64-msvc': 0.40.4 + '@ast-grep/napi-darwin-arm64': 0.40.5 + '@ast-grep/napi-darwin-x64': 0.40.5 + '@ast-grep/napi-linux-arm64-gnu': 0.40.5 + '@ast-grep/napi-linux-arm64-musl': 0.40.5 + '@ast-grep/napi-linux-x64-gnu': 0.40.5 + '@ast-grep/napi-linux-x64-musl': 0.40.5 + '@ast-grep/napi-win32-arm64-msvc': 0.40.5 + '@ast-grep/napi-win32-ia32-msvc': 0.40.5 + '@ast-grep/napi-win32-x64-msvc': 0.40.5 '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/code-frame@7.28.6': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 '@babel/compat-data@7.28.4': {} + '@babel/compat-data@7.28.6': {} + '@babel/core@7.28.4': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 + '@babel/generator': 7.28.6 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.5 + '@babel/parser': 7.28.6 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/traverse': 7.28.6 + '@babel/types': 7.28.6 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -9022,17 +8968,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/core@7.28.5': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/core@7.28.6': + dependencies: + '@babel/code-frame': 7.28.6 + '@babel/generator': 7.28.6 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6) + '@babel/helpers': 7.28.6 + '@babel/parser': 7.28.6 + '@babel/template': 7.28.6 + '@babel/traverse': 7.28.6 + '@babel/types': 7.28.6 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -9042,17 +8988,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.28.5': + '@babel/generator@7.28.6': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 '@babel/helper-compilation-targets@7.27.2': dependencies: @@ -9062,12 +9008,27 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.26.3 + lru-cache: 5.1.1 + semver: 6.3.1 + '@babel/helper-globals@7.28.0': {} '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/traverse': 7.28.6 + '@babel/types': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.28.6 + '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color @@ -9075,17 +9036,17 @@ snapshots: dependencies: '@babel/core': 7.28.4 '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + '@babel/helper-module-transforms@7.28.6(@babel/core@7.28.6)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/core': 7.28.6 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -9093,8 +9054,6 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.27.1': {} - '@babel/helper-validator-identifier@7.28.5': {} '@babel/helper-validator-option@7.27.1': {} @@ -9102,11 +9061,16 @@ snapshots: '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 + + '@babel/helpers@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.28.6 - '@babel/parser@7.28.5': + '@babel/parser@7.28.6': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.4)': dependencies: @@ -9148,9 +9112,9 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.6)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.4)': @@ -9198,66 +9162,72 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.5)': + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.6)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.6)': dependencies: - '@babel/core': 7.28.5 - '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/core': 7.28.6 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.6) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.6)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.28.6 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) - '@babel/types': 7.28.5 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.6) + '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.28.6)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.28.6 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-plugin-utils': 7.27.1 - '@babel/preset-react@7.28.5(@babel/core@7.28.5)': + '@babel/preset-react@7.28.5(@babel/core@7.28.6)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.5) - '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.6) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.6) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.6) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.28.6) transitivePeerDependencies: - supports-color '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 - '@babel/traverse@7.28.5': + '@babel/template@7.28.6': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 + '@babel/code-frame': 7.28.6 + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 + + '@babel/traverse@7.28.6': + dependencies: + '@babel/code-frame': 7.28.6 + '@babel/generator': 7.28.6 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 + '@babel/parser': 7.28.6 + '@babel/template': 7.28.6 + '@babel/types': 7.28.6 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.28.5': + '@babel/types@7.28.6': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 @@ -9327,11 +9297,11 @@ snapshots: transitivePeerDependencies: - debug - '@codspeed/vitest-plugin@4.0.1(vite@7.1.7(@types/node@20.19.27)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.19.27)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + '@codspeed/vitest-plugin@4.0.1(vite@7.1.7(@types/node@20.19.29)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.19.29)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@codspeed/core': 4.0.1 - vite: 7.1.7(@types/node@20.19.27)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.27)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.1.7(@types/node@20.19.29)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.29)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - debug @@ -9575,26 +9545,26 @@ snapshots: '@discoveryjs/json-ext@0.5.7': {} - '@docsearch/core@4.4.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@docsearch/core@4.4.0(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': optionalDependencies: - '@types/react': 19.2.7 + '@types/react': 19.2.8 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) '@docsearch/css@4.4.0': {} - '@docsearch/react@4.4.0(@algolia/client-search@5.39.0)(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(search-insights@2.17.3)': + '@docsearch/react@4.4.0(@algolia/client-search@5.39.0)(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(search-insights@2.17.3)': dependencies: '@ai-sdk/react': 2.0.59(react@19.2.3)(zod@4.1.12) '@algolia/autocomplete-core': 1.19.2(@algolia/client-search@5.39.0)(algoliasearch@5.39.0)(search-insights@2.17.3) - '@docsearch/core': 4.4.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@docsearch/core': 4.4.0(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@docsearch/css': 4.4.0 ai: 5.0.59(zod@4.1.12) algoliasearch: 5.39.0 marked: 16.3.0 zod: 4.1.12 optionalDependencies: - '@types/react': 19.2.7 + '@types/react': 19.2.8 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) search-insights: 2.17.3 @@ -9793,128 +9763,128 @@ snapshots: '@inquirer/ansi@1.0.0': {} - '@inquirer/checkbox@4.2.4(@types/node@20.19.27)': + '@inquirer/checkbox@4.2.4(@types/node@20.19.29)': dependencies: '@inquirer/ansi': 1.0.0 - '@inquirer/core': 10.2.2(@types/node@20.19.27) + '@inquirer/core': 10.2.2(@types/node@20.19.29) '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@20.19.27) + '@inquirer/type': 3.0.8(@types/node@20.19.29) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@inquirer/confirm@5.1.18(@types/node@20.19.27)': + '@inquirer/confirm@5.1.18(@types/node@20.19.29)': dependencies: - '@inquirer/core': 10.2.2(@types/node@20.19.27) - '@inquirer/type': 3.0.8(@types/node@20.19.27) + '@inquirer/core': 10.2.2(@types/node@20.19.29) + '@inquirer/type': 3.0.8(@types/node@20.19.29) optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@inquirer/core@10.2.2(@types/node@20.19.27)': + '@inquirer/core@10.2.2(@types/node@20.19.29)': dependencies: '@inquirer/ansi': 1.0.0 '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@20.19.27) + '@inquirer/type': 3.0.8(@types/node@20.19.29) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@inquirer/editor@4.2.20(@types/node@20.19.27)': + '@inquirer/editor@4.2.20(@types/node@20.19.29)': dependencies: - '@inquirer/core': 10.2.2(@types/node@20.19.27) - '@inquirer/external-editor': 1.0.2(@types/node@20.19.27) - '@inquirer/type': 3.0.8(@types/node@20.19.27) + '@inquirer/core': 10.2.2(@types/node@20.19.29) + '@inquirer/external-editor': 1.0.2(@types/node@20.19.29) + '@inquirer/type': 3.0.8(@types/node@20.19.29) optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@inquirer/expand@4.0.20(@types/node@20.19.27)': + '@inquirer/expand@4.0.20(@types/node@20.19.29)': dependencies: - '@inquirer/core': 10.2.2(@types/node@20.19.27) - '@inquirer/type': 3.0.8(@types/node@20.19.27) + '@inquirer/core': 10.2.2(@types/node@20.19.29) + '@inquirer/type': 3.0.8(@types/node@20.19.29) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@inquirer/external-editor@1.0.2(@types/node@20.19.27)': + '@inquirer/external-editor@1.0.2(@types/node@20.19.29)': dependencies: chardet: 2.1.0 - iconv-lite: 0.7.1 + iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@inquirer/figures@1.0.13': {} - '@inquirer/input@4.2.4(@types/node@20.19.27)': + '@inquirer/input@4.2.4(@types/node@20.19.29)': dependencies: - '@inquirer/core': 10.2.2(@types/node@20.19.27) - '@inquirer/type': 3.0.8(@types/node@20.19.27) + '@inquirer/core': 10.2.2(@types/node@20.19.29) + '@inquirer/type': 3.0.8(@types/node@20.19.29) optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@inquirer/number@3.0.20(@types/node@20.19.27)': + '@inquirer/number@3.0.20(@types/node@20.19.29)': dependencies: - '@inquirer/core': 10.2.2(@types/node@20.19.27) - '@inquirer/type': 3.0.8(@types/node@20.19.27) + '@inquirer/core': 10.2.2(@types/node@20.19.29) + '@inquirer/type': 3.0.8(@types/node@20.19.29) optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@inquirer/password@4.0.20(@types/node@20.19.27)': + '@inquirer/password@4.0.20(@types/node@20.19.29)': dependencies: '@inquirer/ansi': 1.0.0 - '@inquirer/core': 10.2.2(@types/node@20.19.27) - '@inquirer/type': 3.0.8(@types/node@20.19.27) + '@inquirer/core': 10.2.2(@types/node@20.19.29) + '@inquirer/type': 3.0.8(@types/node@20.19.29) optionalDependencies: - '@types/node': 20.19.27 - - '@inquirer/prompts@7.8.6(@types/node@20.19.27)': - dependencies: - '@inquirer/checkbox': 4.2.4(@types/node@20.19.27) - '@inquirer/confirm': 5.1.18(@types/node@20.19.27) - '@inquirer/editor': 4.2.20(@types/node@20.19.27) - '@inquirer/expand': 4.0.20(@types/node@20.19.27) - '@inquirer/input': 4.2.4(@types/node@20.19.27) - '@inquirer/number': 3.0.20(@types/node@20.19.27) - '@inquirer/password': 4.0.20(@types/node@20.19.27) - '@inquirer/rawlist': 4.1.8(@types/node@20.19.27) - '@inquirer/search': 3.1.3(@types/node@20.19.27) - '@inquirer/select': 4.3.4(@types/node@20.19.27) + '@types/node': 20.19.29 + + '@inquirer/prompts@7.8.6(@types/node@20.19.29)': + dependencies: + '@inquirer/checkbox': 4.2.4(@types/node@20.19.29) + '@inquirer/confirm': 5.1.18(@types/node@20.19.29) + '@inquirer/editor': 4.2.20(@types/node@20.19.29) + '@inquirer/expand': 4.0.20(@types/node@20.19.29) + '@inquirer/input': 4.2.4(@types/node@20.19.29) + '@inquirer/number': 3.0.20(@types/node@20.19.29) + '@inquirer/password': 4.0.20(@types/node@20.19.29) + '@inquirer/rawlist': 4.1.8(@types/node@20.19.29) + '@inquirer/search': 3.1.3(@types/node@20.19.29) + '@inquirer/select': 4.3.4(@types/node@20.19.29) optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@inquirer/rawlist@4.1.8(@types/node@20.19.27)': + '@inquirer/rawlist@4.1.8(@types/node@20.19.29)': dependencies: - '@inquirer/core': 10.2.2(@types/node@20.19.27) - '@inquirer/type': 3.0.8(@types/node@20.19.27) + '@inquirer/core': 10.2.2(@types/node@20.19.29) + '@inquirer/type': 3.0.8(@types/node@20.19.29) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@inquirer/search@3.1.3(@types/node@20.19.27)': + '@inquirer/search@3.1.3(@types/node@20.19.29)': dependencies: - '@inquirer/core': 10.2.2(@types/node@20.19.27) + '@inquirer/core': 10.2.2(@types/node@20.19.29) '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@20.19.27) + '@inquirer/type': 3.0.8(@types/node@20.19.29) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@inquirer/select@4.3.4(@types/node@20.19.27)': + '@inquirer/select@4.3.4(@types/node@20.19.29)': dependencies: '@inquirer/ansi': 1.0.0 - '@inquirer/core': 10.2.2(@types/node@20.19.27) + '@inquirer/core': 10.2.2(@types/node@20.19.29) '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@20.19.27) + '@inquirer/type': 3.0.8(@types/node@20.19.29) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@inquirer/type@3.0.8(@types/node@20.19.27)': + '@inquirer/type@3.0.8(@types/node@20.19.29)': optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@isaacs/balanced-match@4.0.1': {} @@ -9959,7 +9929,7 @@ snapshots: '@jest/transform@29.7.0': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.28.6 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.31 babel-plugin-istanbul: 6.1.1 @@ -9982,7 +9952,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -10082,33 +10052,33 @@ snapshots: transitivePeerDependencies: - supports-color - '@mdx-js/react@3.1.1(@types/react@19.2.7)(react@19.2.3)': + '@mdx-js/react@3.1.1(@types/react@19.2.8)(react@19.2.3)': dependencies: '@types/mdx': 2.0.13 - '@types/react': 19.2.7 + '@types/react': 19.2.8 react: 19.2.3 '@mermaid-js/parser@0.6.3': dependencies: langium: 3.3.1 - '@microsoft/api-extractor-model@7.32.2(@types/node@20.19.27)': + '@microsoft/api-extractor-model@7.32.2(@types/node@20.19.29)': dependencies: '@microsoft/tsdoc': 0.16.0 '@microsoft/tsdoc-config': 0.18.0 - '@rushstack/node-core-library': 5.19.1(@types/node@20.19.27) + '@rushstack/node-core-library': 5.19.1(@types/node@20.19.29) transitivePeerDependencies: - '@types/node' - '@microsoft/api-extractor@7.55.2(@types/node@20.19.27)': + '@microsoft/api-extractor@7.55.2(@types/node@20.19.29)': dependencies: - '@microsoft/api-extractor-model': 7.32.2(@types/node@20.19.27) + '@microsoft/api-extractor-model': 7.32.2(@types/node@20.19.29) '@microsoft/tsdoc': 0.16.0 '@microsoft/tsdoc-config': 0.18.0 - '@rushstack/node-core-library': 5.19.1(@types/node@20.19.27) + '@rushstack/node-core-library': 5.19.1(@types/node@20.19.29) '@rushstack/rig-package': 0.6.0 - '@rushstack/terminal': 0.19.5(@types/node@20.19.27) - '@rushstack/ts-command-line': 5.1.5(@types/node@20.19.27) + '@rushstack/terminal': 0.19.5(@types/node@20.19.29) + '@rushstack/ts-command-line': 5.1.5(@types/node@20.19.29) diff: 8.0.2 lodash: 4.17.21 minimatch: 10.0.3 @@ -10128,59 +10098,34 @@ snapshots: '@microsoft/tsdoc@0.16.0': {} - '@module-federation/error-codes@0.21.6': {} - '@module-federation/error-codes@0.22.0': {} - '@module-federation/runtime-core@0.21.6': - dependencies: - '@module-federation/error-codes': 0.21.6 - '@module-federation/sdk': 0.21.6 - '@module-federation/runtime-core@0.22.0': dependencies: '@module-federation/error-codes': 0.22.0 '@module-federation/sdk': 0.22.0 - '@module-federation/runtime-tools@0.21.6': - dependencies: - '@module-federation/runtime': 0.21.6 - '@module-federation/webpack-bundler-runtime': 0.21.6 - '@module-federation/runtime-tools@0.22.0': dependencies: '@module-federation/runtime': 0.22.0 '@module-federation/webpack-bundler-runtime': 0.22.0 - '@module-federation/runtime@0.21.6': - dependencies: - '@module-federation/error-codes': 0.21.6 - '@module-federation/runtime-core': 0.21.6 - '@module-federation/sdk': 0.21.6 - '@module-federation/runtime@0.22.0': dependencies: '@module-federation/error-codes': 0.22.0 '@module-federation/runtime-core': 0.22.0 '@module-federation/sdk': 0.22.0 - '@module-federation/sdk@0.21.6': {} - '@module-federation/sdk@0.22.0': {} - '@module-federation/webpack-bundler-runtime@0.21.6': - dependencies: - '@module-federation/runtime': 0.21.6 - '@module-federation/sdk': 0.21.6 - '@module-federation/webpack-bundler-runtime@0.22.0': dependencies: '@module-federation/runtime': 0.22.0 '@module-federation/sdk': 0.22.0 - '@napi-rs/cli@3.0.4(@emnapi/runtime@1.5.0)(@types/node@20.19.27)(emnapi@1.7.1(node-addon-api@7.1.1))': + '@napi-rs/cli@3.0.4(@emnapi/runtime@1.5.0)(@types/node@20.19.29)(emnapi@1.7.1(node-addon-api@7.1.1))': dependencies: - '@inquirer/prompts': 7.8.6(@types/node@20.19.27) + '@inquirer/prompts': 7.8.6(@types/node@20.19.29) '@napi-rs/cross-toolchain': 1.0.3 '@napi-rs/wasm-tools': 1.0.1 '@octokit/rest': 22.0.0 @@ -10704,14 +10649,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.53.3': optional: true - '@rsbuild/core@1.6.15': - dependencies: - '@rspack/core': 1.6.8(@swc/helpers@0.5.18) - '@rspack/lite-tapable': 1.1.0 - '@swc/helpers': 0.5.18 - core-js: 3.47.0 - jiti: 2.6.1 - '@rsbuild/core@1.7.1': dependencies: '@rspack/core': 1.7.0(@swc/helpers@0.5.18) @@ -10756,9 +10693,9 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.2 - '@rsbuild/plugin-react@1.4.2(@rsbuild/core@1.6.15)': + '@rsbuild/plugin-react@1.4.2(@rsbuild/core@1.7.2)': dependencies: - '@rsbuild/core': 1.6.15 + '@rsbuild/core': 1.7.2 '@rspack/plugin-react-refresh': 1.6.0(react-refresh@0.18.0) react-refresh: 0.18.0 transitivePeerDependencies: @@ -10773,44 +10710,41 @@ snapshots: reduce-configs: 1.1.1 sass-embedded: 1.93.2 - '@rslib/core@0.19.1(@microsoft/api-extractor@7.55.2(@types/node@20.19.27))(typescript@5.9.3)': + '@rslib/core@0.19.2(@microsoft/api-extractor@7.55.2(@types/node@20.19.29))(typescript@5.9.3)': dependencies: - '@rsbuild/core': 1.7.2 - rsbuild-plugin-dts: 0.19.1(@microsoft/api-extractor@7.55.2(@types/node@20.19.27))(@rsbuild/core@1.7.2)(typescript@5.9.3) + '@rsbuild/core': 1.7.1 + rsbuild-plugin-dts: 0.19.2(@microsoft/api-extractor@7.55.2(@types/node@20.19.29))(@rsbuild/core@1.7.1)(typescript@5.9.3) optionalDependencies: - '@microsoft/api-extractor': 7.55.2(@types/node@20.19.27) + '@microsoft/api-extractor': 7.55.2(@types/node@20.19.29) typescript: 5.9.3 transitivePeerDependencies: - '@typescript/native-preview' - '@rslint/core@0.1.13': + '@rslint/core@0.2.0': optionalDependencies: - '@rslint/darwin-arm64': 0.1.13 - '@rslint/darwin-x64': 0.1.13 - '@rslint/linux-arm64': 0.1.13 - '@rslint/linux-x64': 0.1.13 - '@rslint/win32-arm64': 0.1.13 - '@rslint/win32-x64': 0.1.13 - - '@rslint/darwin-arm64@0.1.13': - optional: true + '@rslint/darwin-arm64': 0.2.0 + '@rslint/darwin-x64': 0.2.0 + '@rslint/linux-arm64': 0.2.0 + '@rslint/linux-x64': 0.2.0 + '@rslint/win32-arm64': 0.2.0 + '@rslint/win32-x64': 0.2.0 - '@rslint/darwin-x64@0.1.13': + '@rslint/darwin-arm64@0.2.0': optional: true - '@rslint/linux-arm64@0.1.13': + '@rslint/darwin-x64@0.2.0': optional: true - '@rslint/linux-x64@0.1.13': + '@rslint/linux-arm64@0.2.0': optional: true - '@rslint/win32-arm64@0.1.13': + '@rslint/linux-x64@0.2.0': optional: true - '@rslint/win32-x64@0.1.13': + '@rslint/win32-arm64@0.2.0': optional: true - '@rspack/binding-darwin-arm64@1.6.8': + '@rslint/win32-x64@0.2.0': optional: true '@rspack/binding-darwin-arm64@1.7.0': @@ -10819,56 +10753,36 @@ snapshots: '@rspack/binding-darwin-arm64@1.7.1': optional: true - '@rspack/binding-darwin-x64@1.6.8': - optional: true - '@rspack/binding-darwin-x64@1.7.0': optional: true '@rspack/binding-darwin-x64@1.7.1': optional: true - '@rspack/binding-linux-arm64-gnu@1.6.8': - optional: true - '@rspack/binding-linux-arm64-gnu@1.7.0': optional: true '@rspack/binding-linux-arm64-gnu@1.7.1': optional: true - '@rspack/binding-linux-arm64-musl@1.6.8': - optional: true - '@rspack/binding-linux-arm64-musl@1.7.0': optional: true '@rspack/binding-linux-arm64-musl@1.7.1': optional: true - '@rspack/binding-linux-x64-gnu@1.6.8': - optional: true - '@rspack/binding-linux-x64-gnu@1.7.0': optional: true '@rspack/binding-linux-x64-gnu@1.7.1': optional: true - '@rspack/binding-linux-x64-musl@1.6.8': - optional: true - '@rspack/binding-linux-x64-musl@1.7.0': optional: true '@rspack/binding-linux-x64-musl@1.7.1': optional: true - '@rspack/binding-wasm32-wasi@1.6.8': - dependencies: - '@napi-rs/wasm-runtime': 1.0.7 - optional: true - '@rspack/binding-wasm32-wasi@1.7.0': dependencies: '@napi-rs/wasm-runtime': 1.0.7 @@ -10879,46 +10793,24 @@ snapshots: '@napi-rs/wasm-runtime': 1.0.7 optional: true - '@rspack/binding-win32-arm64-msvc@1.6.8': - optional: true - '@rspack/binding-win32-arm64-msvc@1.7.0': optional: true '@rspack/binding-win32-arm64-msvc@1.7.1': optional: true - '@rspack/binding-win32-ia32-msvc@1.6.8': - optional: true - '@rspack/binding-win32-ia32-msvc@1.7.0': optional: true '@rspack/binding-win32-ia32-msvc@1.7.1': optional: true - '@rspack/binding-win32-x64-msvc@1.6.8': - optional: true - '@rspack/binding-win32-x64-msvc@1.7.0': optional: true '@rspack/binding-win32-x64-msvc@1.7.1': optional: true - '@rspack/binding@1.6.8': - optionalDependencies: - '@rspack/binding-darwin-arm64': 1.6.8 - '@rspack/binding-darwin-x64': 1.6.8 - '@rspack/binding-linux-arm64-gnu': 1.6.8 - '@rspack/binding-linux-arm64-musl': 1.6.8 - '@rspack/binding-linux-x64-gnu': 1.6.8 - '@rspack/binding-linux-x64-musl': 1.6.8 - '@rspack/binding-wasm32-wasi': 1.6.8 - '@rspack/binding-win32-arm64-msvc': 1.6.8 - '@rspack/binding-win32-ia32-msvc': 1.6.8 - '@rspack/binding-win32-x64-msvc': 1.6.8 - '@rspack/binding@1.7.0': optionalDependencies: '@rspack/binding-darwin-arm64': 1.7.0 @@ -10945,14 +10837,6 @@ snapshots: '@rspack/binding-win32-ia32-msvc': 1.7.1 '@rspack/binding-win32-x64-msvc': 1.7.1 - '@rspack/core@1.6.8(@swc/helpers@0.5.18)': - dependencies: - '@module-federation/runtime-tools': 0.21.6 - '@rspack/binding': 1.6.8 - '@rspack/lite-tapable': 1.1.0 - optionalDependencies: - '@swc/helpers': 0.5.18 - '@rspack/core@1.7.0(@swc/helpers@0.5.18)': dependencies: '@module-federation/runtime-tools': 0.22.0 @@ -10999,18 +10883,17 @@ snapshots: html-entities: 2.6.0 react-refresh: 0.18.0 - '@rspress/core@2.0.0-rc.4(@types/react@19.2.7)': + '@rspress/core@2.0.0-rc.5(@types/react@19.2.8)': dependencies: '@mdx-js/mdx': 3.1.1 - '@mdx-js/react': 3.1.1(@types/react@19.2.7)(react@19.2.3) - '@rsbuild/core': 1.6.15 - '@rsbuild/plugin-react': 1.4.2(@rsbuild/core@1.6.15) + '@mdx-js/react': 3.1.1(@types/react@19.2.8)(react@19.2.3) + '@rsbuild/core': 1.7.2 + '@rsbuild/plugin-react': 1.4.2(@rsbuild/core@1.7.2) '@rspress/mdx-rs': 0.6.6 - '@rspress/runtime': 2.0.0-rc.4 - '@rspress/shared': 2.0.0-rc.4 + '@rspress/shared': 2.0.0-rc.5 '@shikijs/rehype': 3.20.0 '@types/unist': 3.0.3 - '@unhead/react': 2.0.19(react@19.2.3) + '@unhead/react': 2.1.2(react@19.2.3) body-scroll-lock: 4.0.0-beta.0 cac: 6.7.14 chokidar: 3.6.0 @@ -11084,11 +10967,11 @@ snapshots: '@rspress/mdx-rs-win32-arm64-msvc': 0.6.6 '@rspress/mdx-rs-win32-x64-msvc': 0.6.6 - '@rspress/plugin-algolia@2.0.0-rc.4(@algolia/client-search@5.39.0)(@rspress/core@2.0.0-rc.4(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(search-insights@2.17.3)': + '@rspress/plugin-algolia@2.0.0-rc.5(@algolia/client-search@5.39.0)(@rspress/core@2.0.0-rc.5(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(search-insights@2.17.3)': dependencies: '@docsearch/css': 4.4.0 - '@docsearch/react': 4.4.0(@algolia/client-search@5.39.0)(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(search-insights@2.17.3) - '@rspress/core': 2.0.0-rc.4(@types/react@19.2.7) + '@docsearch/react': 4.4.0(@algolia/client-search@5.39.0)(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(search-insights@2.17.3) + '@rspress/core': 2.0.0-rc.5(@types/react@19.2.8) transitivePeerDependencies: - '@algolia/client-search' - '@types/react' @@ -11096,13 +10979,13 @@ snapshots: - react-dom - search-insights - '@rspress/plugin-client-redirects@2.0.0-rc.4(@rspress/core@2.0.0-rc.4(@types/react@19.2.7))': + '@rspress/plugin-client-redirects@2.0.0-rc.5(@rspress/core@2.0.0-rc.5(@types/react@19.2.8))': dependencies: - '@rspress/core': 2.0.0-rc.4(@types/react@19.2.7) + '@rspress/core': 2.0.0-rc.5(@types/react@19.2.8) - '@rspress/plugin-llms@2.0.0-rc.4(@rspress/core@2.0.0-rc.4(@types/react@19.2.7))': + '@rspress/plugin-llms@2.0.0-rc.5(@rspress/core@2.0.0-rc.5(@types/react@19.2.8))': dependencies: - '@rspress/core': 2.0.0-rc.4(@types/react@19.2.7) + '@rspress/core': 2.0.0-rc.5(@types/react@19.2.8) remark-mdx: 3.1.1 remark-parse: 11.0.0 remark-stringify: 11.0.0 @@ -11111,32 +10994,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@rspress/plugin-rss@2.0.0-rc.4(@rspress/core@2.0.0-rc.4(@types/react@19.2.7))': + '@rspress/plugin-rss@2.0.0-rc.5(@rspress/core@2.0.0-rc.5(@types/react@19.2.8))': dependencies: - '@rspress/core': 2.0.0-rc.4(@types/react@19.2.7) + '@rspress/core': 2.0.0-rc.5(@types/react@19.2.8) feed: 4.2.2 - '@rspress/plugin-sitemap@2.0.0-rc.4(@rspress/core@2.0.0-rc.4(@types/react@19.2.7))': + '@rspress/plugin-sitemap@2.0.0-rc.5(@rspress/core@2.0.0-rc.5(@types/react@19.2.8))': dependencies: - '@rspress/core': 2.0.0-rc.4(@types/react@19.2.7) + '@rspress/core': 2.0.0-rc.5(@types/react@19.2.8) - '@rspress/runtime@2.0.0-rc.4': + '@rspress/shared@2.0.0-rc.5': dependencies: - '@rspress/shared': 2.0.0-rc.4 - '@unhead/react': 2.0.19(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-router-dom: 7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - - '@rspress/shared@2.0.0-rc.4': - dependencies: - '@rsbuild/core': 1.6.15 + '@rsbuild/core': 1.7.2 '@shikijs/rehype': 3.20.0 gray-matter: 4.0.3 lodash-es: 4.17.22 unified: 11.0.5 - '@rstack-dev/doc-ui@1.12.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@rstack-dev/doc-ui@1.12.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: framer-motion: 12.23.26(react-dom@19.2.3(react@19.2.3))(react@19.2.3) transitivePeerDependencies: @@ -11152,7 +11027,7 @@ snapshots: optionalDependencies: jsdom: 26.1.0 - '@rushstack/node-core-library@5.19.1(@types/node@20.19.27)': + '@rushstack/node-core-library@5.19.1(@types/node@20.19.29)': dependencies: ajv: 8.13.0 ajv-draft-04: 1.0.0(ajv@8.13.0) @@ -11163,28 +11038,28 @@ snapshots: resolve: 1.22.11 semver: 7.5.4 optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@rushstack/problem-matcher@0.1.1(@types/node@20.19.27)': + '@rushstack/problem-matcher@0.1.1(@types/node@20.19.29)': optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@rushstack/rig-package@0.6.0': dependencies: resolve: 1.22.11 strip-json-comments: 3.1.1 - '@rushstack/terminal@0.19.5(@types/node@20.19.27)': + '@rushstack/terminal@0.19.5(@types/node@20.19.29)': dependencies: - '@rushstack/node-core-library': 5.19.1(@types/node@20.19.27) - '@rushstack/problem-matcher': 0.1.1(@types/node@20.19.27) + '@rushstack/node-core-library': 5.19.1(@types/node@20.19.29) + '@rushstack/problem-matcher': 0.1.1(@types/node@20.19.29) supports-color: 8.1.1 optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@rushstack/ts-command-line@5.1.5(@types/node@20.19.27)': + '@rushstack/ts-command-line@5.1.5(@types/node@20.19.29)': dependencies: - '@rushstack/terminal': 0.19.5(@types/node@20.19.27) + '@rushstack/terminal': 0.19.5(@types/node@20.19.29) '@types/argparse': 1.0.38 argparse: 1.0.10 string-argv: 0.3.2 @@ -11299,20 +11174,20 @@ snapshots: '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/bonjour@3.5.13': dependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/chai@5.2.2': dependencies: @@ -11326,11 +11201,11 @@ snapshots: '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.6 - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/connect@3.4.38': dependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/d3-array@3.2.2': {} @@ -11473,7 +11348,7 @@ snapshots: '@types/express-serve-static-core@4.19.6': dependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 @@ -11488,13 +11363,13 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/geojson@7946.0.16': {} '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/hast@3.0.4': dependencies: @@ -11506,7 +11381,7 @@ snapshots: '@types/http-proxy@1.17.16': dependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/is-ci@3.0.4': dependencies: @@ -11526,7 +11401,7 @@ snapshots: '@types/jsdom@21.1.7': dependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 @@ -11534,7 +11409,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/mdast@4.0.4': dependencies: @@ -11548,9 +11423,9 @@ snapshots: '@types/node-forge@1.3.14': dependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 - '@types/node@20.19.27': + '@types/node@20.19.29': dependencies: undici-types: 6.21.0 @@ -11558,11 +11433,11 @@ snapshots: '@types/range-parser@1.2.7': {} - '@types/react-dom@19.2.3(@types/react@19.2.7)': + '@types/react-dom@19.2.3(@types/react@19.2.8)': dependencies: - '@types/react': 19.2.7 + '@types/react': 19.2.8 - '@types/react@19.2.7': + '@types/react@19.2.8': dependencies: csstype: 3.2.3 @@ -11573,7 +11448,7 @@ snapshots: '@types/send@0.17.5': dependencies: '@types/mime': 1.3.5 - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/serve-index@1.9.4': dependencies: @@ -11582,12 +11457,12 @@ snapshots: '@types/serve-static@1.15.8': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/send': 0.17.5 '@types/sockjs@0.3.36': dependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/stack-utils@2.0.3': {} @@ -11603,11 +11478,11 @@ snapshots: '@types/watchpack@2.4.5': dependencies: '@types/graceful-fs': 4.1.9 - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/ws@8.18.1': dependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 '@types/yargs-parser@21.0.3': {} @@ -11617,10 +11492,10 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@unhead/react@2.0.19(react@19.2.3)': + '@unhead/react@2.1.2(react@19.2.3)': dependencies: react: 19.2.3 - unhead: 2.0.19 + unhead: 2.1.2 '@vercel/ncc@0.38.4': {} @@ -11632,13 +11507,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.7(@types/node@20.19.27)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@3.2.4(vite@7.1.7(@types/node@20.19.29)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.19 optionalDependencies: - vite: 7.1.7(@types/node@20.19.27)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.1.7(@types/node@20.19.29)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) '@vitest/pretty-format@3.2.4': dependencies: @@ -11668,7 +11543,7 @@ snapshots: '@vue/compiler-core@3.5.26': dependencies: - '@babel/parser': 7.28.5 + '@babel/parser': 7.28.6 '@vue/shared': 3.5.26 entities: 7.0.0 estree-walker: 2.0.2 @@ -11681,7 +11556,7 @@ snapshots: '@vue/compiler-sfc@3.5.26': dependencies: - '@babel/parser': 7.28.5 + '@babel/parser': 7.28.6 '@vue/compiler-core': 3.5.26 '@vue/compiler-dom': 3.5.26 '@vue/compiler-ssr': 3.5.26 @@ -11993,9 +11868,9 @@ snapshots: transitivePeerDependencies: - debug - babel-loader@10.0.0(@babel/core@7.28.5)(webpack@5.102.1): + babel-loader@10.0.0(@babel/core@7.28.6)(webpack@5.102.1): dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.28.6 find-up: 5.0.0 webpack: 5.102.1 @@ -12036,7 +11911,7 @@ snapshots: babel-walk@3.0.0-canary-5: dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 bail@2.0.2: {} @@ -12430,8 +12305,8 @@ snapshots: constantinople@4.0.1: dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 constants-browserify@1.0.0: {} @@ -13805,7 +13680,7 @@ snapshots: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - hookable@5.5.3: {} + hookable@6.0.1: {} hpack.js@2.1.6: dependencies: @@ -13950,7 +13825,7 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.1: + iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 @@ -14121,8 +13996,8 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: - '@babel/core': 7.28.5 - '@babel/parser': 7.28.5 + '@babel/core': 7.28.6 + '@babel/parser': 7.28.6 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -14163,7 +14038,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 20.19.27 + '@types/node': 20.19.29 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -14199,10 +14074,10 @@ snapshots: jest-snapshot@29.7.0: dependencies: '@babel/core': 7.28.4 - '@babel/generator': 7.28.5 + '@babel/generator': 7.28.6 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 @@ -14224,7 +14099,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.19.27 + '@types/node': 20.19.29 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -14232,13 +14107,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -14491,7 +14366,7 @@ snapshots: markdown-table@3.0.4: {} - markdown-to-jsx@9.5.0(react@19.2.3)(vue@3.5.26(typescript@5.9.3)): + markdown-to-jsx@9.5.7(react@19.2.3)(vue@3.5.26(typescript@5.9.3)): optionalDependencies: react: 19.2.3 vue: 3.5.26(typescript@5.9.3) @@ -15415,13 +15290,13 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.5.6 - postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3)): + postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@20.19.29)(typescript@5.9.3)): dependencies: lilconfig: 3.1.3 yaml: 2.8.2 optionalDependencies: postcss: 8.5.6 - ts-node: 10.9.2(@types/node@20.19.27)(typescript@5.9.3) + ts-node: 10.9.2(@types/node@20.19.29)(typescript@5.9.3) postcss-loader@8.2.0(@rspack/core@packages+rspack)(postcss@8.5.6)(typescript@5.9.3)(webpack@5.102.1): dependencies: @@ -15963,12 +15838,12 @@ snapshots: rrweb-cssom@0.8.0: {} - rsbuild-plugin-dts@0.19.1(@microsoft/api-extractor@7.55.2(@types/node@20.19.27))(@rsbuild/core@1.7.2)(typescript@5.9.3): + rsbuild-plugin-dts@0.19.2(@microsoft/api-extractor@7.55.2(@types/node@20.19.29))(@rsbuild/core@1.7.1)(typescript@5.9.3): dependencies: '@ast-grep/napi': 0.37.0 - '@rsbuild/core': 1.7.2 + '@rsbuild/core': 1.7.1 optionalDependencies: - '@microsoft/api-extractor': 7.55.2(@types/node@20.19.27) + '@microsoft/api-extractor': 7.55.2(@types/node@20.19.29) typescript: 5.9.3 rsbuild-plugin-google-analytics@1.0.4(@rsbuild/core@1.7.2): @@ -15987,9 +15862,9 @@ snapshots: optionalDependencies: vue: 3.5.26(typescript@5.9.3) - rspress-plugin-font-open-sans@1.0.3(@rspress/core@2.0.0-rc.4(@types/react@19.2.7)): + rspress-plugin-font-open-sans@1.0.3(@rspress/core@2.0.0-rc.5(@types/react@19.2.8)): dependencies: - '@rspress/core': 2.0.0-rc.4(@types/react@19.2.7) + '@rspress/core': 2.0.0-rc.5(@types/react@19.2.8) run-applescript@7.1.0: {} @@ -16512,7 +16387,7 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - tailwindcss@3.4.19(ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3)): + tailwindcss@3.4.19(ts-node@10.9.2(@types/node@20.19.29)(typescript@5.9.3)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -16531,7 +16406,7 @@ snapshots: postcss: 8.5.6 postcss-import: 15.1.0(postcss@8.5.6) postcss-js: 4.1.0(postcss@8.5.6) - postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3)) + postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@20.19.29)(typescript@5.9.3)) postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.11 @@ -16662,14 +16537,14 @@ snapshots: typescript: 5.9.3 webpack: 5.102.1 - ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3): + ts-node@10.9.2(@types/node@20.19.29)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.19.27 + '@types/node': 20.19.29 acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -16724,9 +16599,9 @@ snapshots: dependencies: '@fastify/busboy': 2.1.1 - unhead@2.0.19: + unhead@2.1.2: dependencies: - hookable: 5.5.3 + hookable: 6.0.1 unicorn-magic@0.1.0: {} @@ -16846,13 +16721,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-node@3.2.4(@types/node@20.19.27)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): + vite-node@3.2.4(@types/node@20.19.29)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.7(@types/node@20.19.27)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.1.7(@types/node@20.19.29)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -16867,7 +16742,7 @@ snapshots: - tsx - yaml - vite@7.1.7(@types/node@20.19.27)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): + vite@7.1.7(@types/node@20.19.29)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: esbuild: 0.25.10 fdir: 6.5.0(picomatch@4.0.3) @@ -16876,7 +16751,7 @@ snapshots: rollup: 4.52.3 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 20.19.27 + '@types/node': 20.19.29 fsevents: 2.3.3 jiti: 2.6.1 less: 4.4.2 @@ -16886,11 +16761,11 @@ snapshots: tsx: 4.21.0 yaml: 2.8.2 - vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.19.27)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.19.29)(jiti@2.6.1)(jsdom@26.1.0)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.7(@types/node@20.19.27)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 3.2.4(vite@7.1.7(@types/node@20.19.29)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -16908,12 +16783,12 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.7(@types/node@20.19.27)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@20.19.27)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.1.7(@types/node@20.19.29)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@20.19.29)(jiti@2.6.1)(less@4.4.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 20.19.27 + '@types/node': 20.19.29 jsdom: 26.1.0 transitivePeerDependencies: - jiti @@ -17124,8 +16999,8 @@ snapshots: with@7.0.2: dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 assert-never: 1.4.0 babel-walk: 3.0.0-canary-5 diff --git a/rslint.json b/rslint.json index 937303190a4c..a01673a9a955 100644 --- a/rslint.json +++ b/rslint.json @@ -37,7 +37,19 @@ "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/no-require-imports": "off", - "@typescript-eslint/no-empty-function": "off" + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/consistent-indexed-object-style": "off", + "@typescript-eslint/no-this-alias": "off", + "@typescript-eslint/consistent-type-definitions": "off", + "@typescript-eslint/consistent-return": "off", + "@typescript-eslint/consistent-type-exports": "off", + "@typescript-eslint/consistent-generic-constructors": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/no-extraneous-class": "off", + "@typescript-eslint/consistent-type-imports": "off", + "@typescript-eslint/no-invalid-void-type": "off", + "@typescript-eslint/default-param-last": "error" }, "plugins": ["@typescript-eslint"] } diff --git a/tests/bench/package.json b/tests/bench/package.json index a3417831cffa..84d089870b6c 100644 --- a/tests/bench/package.json +++ b/tests/bench/package.json @@ -11,7 +11,7 @@ "@rspack/cli": "workspace:*", "@rspack/core": "workspace:*", "@rspack/plugin-react-refresh": "^1.6.0", - "@types/react": "^19.2.7", + "@types/react": "^19.2.8", "@types/react-dom": "^19.2.3", "vitest": "^3.2.4" }, diff --git a/tests/e2e/cases/lazy-compilation/active-lots-modules/index.test.ts b/tests/e2e/cases/lazy-compilation/active-lots-modules/index.test.ts new file mode 100644 index 000000000000..b4df048e2626 --- /dev/null +++ b/tests/e2e/cases/lazy-compilation/active-lots-modules/index.test.ts @@ -0,0 +1,8 @@ +import { expect, test } from '@/fixtures'; + +test('should activate and compile lots of modules with long names', async ({ + page, +}) => { + const body = await page.locator('body'); + await expect(body).toContainText('All Modules Loaded'); +}); diff --git a/tests/e2e/cases/lazy-compilation/active-lots-modules/rspack.config.js b/tests/e2e/cases/lazy-compilation/active-lots-modules/rspack.config.js new file mode 100644 index 000000000000..a9ade0287662 --- /dev/null +++ b/tests/e2e/cases/lazy-compilation/active-lots-modules/rspack.config.js @@ -0,0 +1,43 @@ +const { rspack } = require('@rspack/core'); + +/* +Construct a project with lots of virtual files with very long file names +And `virtual_index.js` dynamically imports all the long file name files. +src/ +├── virtual_index.js +├── virtual_with_a_very_long_file_name_number_....._0.js +... +└── virtual_with_a_very_long_file_name_number_....._19.js +*/ +let lotsLongFileNameVirtualFiles = {}; +let longStr = new Array(1024).fill('a').join(''); +for (let i = 0; i < 20; i++) { + lotsLongFileNameVirtualFiles[ + `src/virtual_with_a_very_long_file_name_number_${longStr}_${i}.js` + ] = `"dynamic_imported"`; +} +let allFiles = Object.keys(lotsLongFileNameVirtualFiles); +lotsLongFileNameVirtualFiles['src/virtual_index.js'] = ` + Promise.all([ + ${allFiles.map((file) => `import('./${file.slice(3)}')\n`).join(',')} + ]).then(()=> document.body.innerHTML = 'All Modules Loaded'); +`; + +/** @type { import('@rspack/core').RspackOptions } */ +module.exports = { + context: __dirname, + entry: './src/virtual_index.js', + mode: 'development', + lazyCompilation: true, + devServer: { + hot: true, + port: 5678, + }, + plugins: [ + new rspack.HtmlRspackPlugin(), + new rspack.experiments.VirtualModulesPlugin(lotsLongFileNameVirtualFiles), + ], + experiments: { + useInputFileSystem: [/virtual/], + }, +}; diff --git a/tests/e2e/cases/lazy-compilation/cross-origin/README.md b/tests/e2e/cases/lazy-compilation/cross-origin/README.md new file mode 100644 index 000000000000..817ebaa50fb2 --- /dev/null +++ b/tests/e2e/cases/lazy-compilation/cross-origin/README.md @@ -0,0 +1,46 @@ +# Cross-Origin Lazy Compilation Test + +This test verifies that lazy compilation works correctly +when the lazy compilation server runs on a different origin (port) than the frontend dev server. + +## Architecture + +``` ++----------------------+ +----------------------+ +-----------------------------+ +| | | | | | +| Browser | | Dev Server | | Lazy Compilation Server | +| (Frontend) | | (Port: 8500) | | (Port: 8600) | +| | | | | | ++----------+-----------+ +----------+-----------+ +--------------+--------------+ + | | | + | 1. Load page | | + | GET http://localhost:8500 | + +-------------------------> | + | | | + | 2. Click button -> dynamic import() | + | | | + | 3. Cross-origin POST request | + | POST http://127.0.0.1:8600/lazy-... | + | Content-Type: text/plain | + | Body: "module-id-1\nmodule-id-2" | + +----------------------------------------------------------> + | | | + | 4. Response (SSE or empty for POST) | + <----------------------------------------------------------+ + | | | + | 5. Webpack invalidate & rebuild | + | | | + | 6. Load compiled chunk | + v | | ++----------------------+ | | +| Component | | | +| Rendered | | | ++----------------------+ | | +``` + +## Key Points + +1. **Two Separate Servers**: Frontend runs on port 8500, lazy compilation on port 8600 +2. **Cross-Origin Request**: Browser sends POST request to a different origin +3. **Simple Request**: Uses `Content-Type: text/plain` to avoid CORS preflight +4. **XMLHttpRequest**: Uses XHR instead of fetch for better browser compatibility diff --git a/tests/e2e/cases/lazy-compilation/cross-origin/index.test.ts b/tests/e2e/cases/lazy-compilation/cross-origin/index.test.ts new file mode 100644 index 000000000000..1962c8ef829c --- /dev/null +++ b/tests/e2e/cases/lazy-compilation/cross-origin/index.test.ts @@ -0,0 +1,174 @@ +import http from 'node:http'; +import path from 'node:path'; +import { test as base, expect } from '@playwright/test'; +import fs from 'fs-extra'; +import { type Compiler, type Configuration, rspack } from '@rspack/core'; +import { RspackDevServer } from '@rspack/dev-server'; +import { fileURLToPath } from 'node:url'; +import { createRequire } from 'node:module'; + +const require = createRequire(import.meta.url); +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const tempDir = path.resolve(__dirname, '../../temp'); + +// Create a separate lazy compilation server on a different port (cross-origin) +function createLazyCompilationServer( + compiler: Compiler, + port: number, +): Promise { + return new Promise((resolve, reject) => { + const middleware = rspack.lazyCompilationMiddleware(compiler); + const server = http.createServer((req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + middleware(req, res, () => { + res.writeHead(404); + res.end('Not Found'); + }); + }); + + server.listen(port, () => { + resolve(server); + }); + + server.on('error', reject); + }); +} + +const test = base.extend<{ + crossOriginSetup: { + frontendPort: number; + lazyCompilationPort: number; + }; +}>({ + crossOriginSetup: [ + async ({ page }, use, testInfo) => { + const workerId = String(testInfo.workerIndex); + const testProjectDir = path.dirname(testInfo.file); + const tempProjectDir = path.join(tempDir, `cross-origin-${workerId}`); + + // Copy test project to temp directory + if (await fs.exists(tempProjectDir)) { + await fs.remove(tempProjectDir); + } + await fs.copy(testProjectDir, tempProjectDir); + + // Clear require cache for temp directory + for (const modulePath of Object.keys(require.cache)) { + if (modulePath.startsWith(tempProjectDir)) { + delete require.cache[modulePath]; + } + } + + // Use different ports for frontend and lazy compilation server + const basePort = 8500; + const frontendPort = basePort + testInfo.workerIndex; + const lazyCompilationPort = frontendPort + 100; + + // Load and modify config + const configPath = path.resolve(tempProjectDir, 'rspack.config.js'); + const config: Configuration = require(configPath); + delete require.cache[configPath]; + + config.context = tempProjectDir; + config.output = { + ...config.output, + path: path.resolve(tempProjectDir, 'dist'), + }; + config.devServer = { + ...config.devServer, + port: frontendPort, + }; + // Set cross-origin serverUrl + config.lazyCompilation = { + ...(typeof config.lazyCompilation === 'object' + ? config.lazyCompilation + : {}), + entries: false, + imports: true, + serverUrl: `http://127.0.0.1:${lazyCompilationPort}`, + }; + + // Create compiler + const compiler = rspack(config); + + // Start lazy compilation server on a different port (cross-origin) + const lazyServer = await createLazyCompilationServer( + compiler, + lazyCompilationPort, + ); + + // Start dev server (frontend) - without lazy compilation middleware + // since we're running it on a separate server + const devServer = new RspackDevServer( + { + ...config.devServer, + port: frontendPort, + }, + compiler, + ); + await devServer.start(); + + // Wait for initial build + await new Promise((resolve) => { + compiler.hooks.done.tap('test', () => resolve()); + }); + + // Navigate to frontend + await page.goto(`http://localhost:${frontendPort}`); + + await use({ frontendPort, lazyCompilationPort }); + + // Cleanup + await new Promise((res, rej) => { + compiler.close((err) => (err ? rej(err) : res())); + }); + await devServer.stop(); + await new Promise((resolve) => { + lazyServer.close(() => resolve()); + }); + await fs.remove(tempProjectDir); + }, + { auto: true }, + ], +}); + +test('should work with cross-origin lazy compilation using simple POST request', async ({ + page, + crossOriginSetup, +}) => { + const { lazyCompilationPort } = crossOriginSetup; + + // Set up request interception to verify the request format + const requests: { method: string; contentType: string; body: string }[] = []; + + page.on('request', (request) => { + const url = request.url(); + if (url.includes(`${lazyCompilationPort}`)) { + requests.push({ + method: request.method(), + contentType: request.headers()['content-type'] || '', + body: request.postData() || '', + }); + } + }); + + await page.waitForSelector('button:has-text("Click me")'); + + // Click the button to trigger dynamic import (cross-origin lazy compilation request) + await page.getByText('Click me').click(); + + // Wait for the component to appear - this confirms the cross-origin request worked + await page.waitForSelector('div:has-text("CrossOriginComponent")', { + timeout: 10000, + }); + + const componentCount = await page.getByText('CrossOriginComponent').count(); + expect(componentCount).toBe(1); + + // Verify the request was a simple POST request with text/plain content type + const lazyRequest = requests.find((r) => r.method === 'POST'); + expect(lazyRequest).toBeDefined(); + expect(lazyRequest!.contentType).toBe('text/plain'); + // The body should be newline-separated module IDs, not JSON + expect(lazyRequest!.body).not.toMatch(/^\[/); // Not a JSON array +}); diff --git a/tests/e2e/cases/lazy-compilation/cross-origin/rspack.config.js b/tests/e2e/cases/lazy-compilation/cross-origin/rspack.config.js new file mode 100644 index 000000000000..ccdecd9fc0e7 --- /dev/null +++ b/tests/e2e/cases/lazy-compilation/cross-origin/rspack.config.js @@ -0,0 +1,24 @@ +const { rspack } = require('@rspack/core'); + +/** @type { import('@rspack/core').RspackOptions } */ +module.exports = { + context: __dirname, + entry: { + main: './src/index.js', + }, + stats: 'none', + mode: 'development', + plugins: [new rspack.HtmlRspackPlugin()], + lazyCompilation: { + entries: false, + imports: true, + // serverUrl will be set dynamically in the test to point to a different port + }, + devtool: false, + devServer: { + hot: true, + devMiddleware: { + writeToDisk: true, + }, + }, +}; diff --git a/tests/e2e/cases/lazy-compilation/cross-origin/src/component.js b/tests/e2e/cases/lazy-compilation/cross-origin/src/component.js new file mode 100644 index 000000000000..660827de9de6 --- /dev/null +++ b/tests/e2e/cases/lazy-compilation/cross-origin/src/component.js @@ -0,0 +1,3 @@ +const component = document.createElement('div'); +component.textContent = 'CrossOriginComponent'; +document.body.appendChild(component); diff --git a/tests/e2e/cases/lazy-compilation/cross-origin/src/index.js b/tests/e2e/cases/lazy-compilation/cross-origin/src/index.js new file mode 100644 index 000000000000..571d00ff49e7 --- /dev/null +++ b/tests/e2e/cases/lazy-compilation/cross-origin/src/index.js @@ -0,0 +1,9 @@ +const button = document.createElement('button'); +button.textContent = 'Click me'; + +button.addEventListener('click', () => { + import('./component.js').then(() => { + console.log('Component loaded via cross-origin lazy compilation'); + }); +}); +document.body.appendChild(button); diff --git a/tests/e2e/cases/lazy-compilation/custom-prefix/index.test.ts b/tests/e2e/cases/lazy-compilation/custom-prefix/index.test.ts index a7e1ca5e376f..4354ff62c592 100644 --- a/tests/e2e/cases/lazy-compilation/custom-prefix/index.test.ts +++ b/tests/e2e/cases/lazy-compilation/custom-prefix/index.test.ts @@ -3,9 +3,7 @@ import { expect, test } from '@/fixtures'; test('should use custom prefix for lazy compilation', async ({ page }) => { // Wait for a request with custom prefix const responsePromise = page.waitForResponse( - (response) => - response.url().includes('/custom-lazy-endpoint-') && - response.request().method() === 'GET', + (response) => response.url().includes('/custom-lazy-endpoint-'), { timeout: 5000 }, ); diff --git a/tests/e2e/package.json b/tests/e2e/package.json index 430070a2f521..08060e1ddc4c 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -8,7 +8,7 @@ "test:CI": "CI=1 pnpm run test" }, "devDependencies": { - "@babel/core": "^7.28.5", + "@babel/core": "^7.28.6", "@babel/preset-react": "^7.28.5", "@playwright/test": "1.57.0", "core-js": "3.47.0", diff --git a/tests/rspack-test/Cache.test.js b/tests/rspack-test/Cache.test.js index 8e891bd0f57d..6d70cf7193df 100644 --- a/tests/rspack-test/Cache.test.js +++ b/tests/rspack-test/Cache.test.js @@ -4,7 +4,7 @@ const tempDir = path.resolve(__dirname, `./js/temp`); // Run tests rspack-test/tests/cacheCases in target async-node describeByWalk( - "cache", + __filename, (name, src, dist) => { createCacheCase(name, src, dist, "async-node", path.join(tempDir, name)); }, diff --git a/tests/rspack-test/cacheCases/portable/basic/index.js b/tests/rspack-test/cacheCases/portable/basic/index.js index bbd66bf5780c..3363d4d4ead7 100644 --- a/tests/rspack-test/cacheCases/portable/basic/index.js +++ b/tests/rspack-test/cacheCases/portable/basic/index.js @@ -1,17 +1,17 @@ -import value from "./file"; +import value from './file'; -it("should basic test work", async () => { - if (COMPILER_INDEX == 0) { - expect(value).toBe(1); - await NEXT_START(); - } - if (COMPILER_INDEX == 1) { - expect(value).toBe(1); - await NEXT_MOVE_DIR_START(); - } - if (COMPILER_INDEX == 2) { - expect(value).toBe(3); - } +it('should basic test work', async () => { + if (COMPILER_INDEX == 0) { + expect(value).toBe(1); + await NEXT_START(); + } + if (COMPILER_INDEX == 1) { + expect(value).toBe(1); + await NEXT_MOVE_DIR_START(); + } + if (COMPILER_INDEX == 2) { + expect(value).toBe(3); + } }); -module.hot.accept("./file"); +module.hot.accept('./file'); diff --git a/tests/rspack-test/package.json b/tests/rspack-test/package.json index 9ce1e2178369..8f685ef32734 100644 --- a/tests/rspack-test/package.json +++ b/tests/rspack-test/package.json @@ -7,14 +7,12 @@ "author": "", "license": "ISC", "scripts": { - "test": "cross-env RUST_BACKTRACE=full pnpm run --stream /^test:.*/", - "testu": "pnpm run --stream /^test:.*/ -u", - "test:base": "rstest", - "test:hot": "cross-env RSPACK_HOT_TEST=true node --no-warnings --expose-gc --max-old-space-size=8192 --experimental-vm-modules ../../node_modules/@rstest/core/bin/rstest --config ./rstest.config.hot.ts --passWithNoTests" + "test": "rstest", + "testu": "rstest -u", + "test:base": "rstest --project base", + "test:hot": "rstest --project hottest" }, "devDependencies": { - "@babel/core": "^7.28.5", - "@babel/preset-react": "^7.28.5", "@module-federation/runtime-tools": "^0.22.0", "@rspack/binding-testing": "workspace:*", "@rspack/cli": "workspace:*", @@ -29,58 +27,60 @@ "@types/babel__traverse": "7.28.0", "@types/fs-extra": "11.0.4", "@types/jsdom": "^21.1.7", - "@types/react": "^19.2.7", + "@types/react": "^19.2.8", "@types/react-dom": "^19.2.3", "@webdiscus/pug-loader": "^2.11.1", "acorn": "^8.15.0", "babel-loader": "^10.0.0", "babel-plugin-import": "^1.13.8", - "bundle-loader": "^0.5.6", "chalk": "^4.1.2", - "coffee-loader": "^1.0.1", - "coffeescript": "^2.7.0", "core-js": "3.47.0", "css-loader": "^7.1.2", - "date-fns": "^4.1.0", - "es5-ext": "^0.10.64", - "exports-loader": "^5.0.0", "file-loader": "^6.2.0", - "fork-ts-checker-webpack-plugin": "^9.1.0", "graceful-fs": "^4.2.11", "html-loader": "^5.1.0", "html-webpack-plugin": "^5.6.5", - "is-ci": "4.1.0", "json-loader": "^0.5.7", - "json5": "^2.2.3", - "less": "4.4.2", "less-loader": "^12.3.0", "lodash": "^4.17.21", - "lodash-es": "^4.17.22", "memfs": "4.51.1", - "mime-types": "^3.0.2", "mini-svg-data-uri": "^1.4.4", "node-polyfill-webpack-plugin": "^4.1.0", "postcss-loader": "^8.2.0", "postcss-pxtorem": "^6.1.0", - "prop-types": "^15.8.1", "raw-loader": "^4.0.2", "react": "^19.2.3", "react-dom": "^19.2.3", "react-refresh": "^0.18.0", - "rimraf": "^5.0.10", "sass-loader": "^16.0.6", "source-map": "^0.7.6", "source-map-loader": "^5.0.0", "style-loader": "^4.0.0", "terser": "5.44.1", - "terser-webpack-plugin": "^5.3.16", "toml": "^3.0.0", - "ts-loader": "^9.5.4", "typescript": "^5.9.3", - "url-loader": "^4.1.1", "wast-loader": "^1.14.1", "webpack-sources": "3.3.3", "worker-rspack-loader": "^3.1.2", + "exports-loader": "^5.0.0", + "@babel/preset-react": "^7.28.5", + "coffee-loader": "^1.0.1", + "coffeescript": "^2.7.0", + "es5-ext": "^0.10.64", + "is-ci": "4.1.0", + "less": "4.4.2", + "lodash-es": "^4.17.22", + "mime-types": "^3.0.2", + "bundle-loader": "^0.5.6", + "rimraf": "^5.0.10", + "url-loader": "^4.1.1", + "terser-webpack-plugin": "^5.3.16", + "ts-loader": "^9.5.4", + "@babel/core": "^7.28.6", + "date-fns": "^4.1.0", + "fork-ts-checker-webpack-plugin": "^9.1.0", + "json5": "^2.2.3", + "prop-types": "^15.8.1", "xxhashjs": "^0.2.2", "yamljs": "^0.3.0" } diff --git a/tests/rspack-test/rstest.config.hot.ts b/tests/rspack-test/rstest.config.hot.ts deleted file mode 100644 index 158301288274..000000000000 --- a/tests/rspack-test/rstest.config.hot.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from '@rstest/core'; -import config from './rstest.config' - -export default defineConfig({ - ...config, - include: process.env.WASM ? [] : ["/*.hottest.js"] -}); diff --git a/tests/rspack-test/rstest.config.ts b/tests/rspack-test/rstest.config.ts index d2ec3209a5b3..9deb8c0beb0b 100644 --- a/tests/rspack-test/rstest.config.ts +++ b/tests/rspack-test/rstest.config.ts @@ -1,5 +1,6 @@ import path from 'node:path'; -import { defineConfig } from '@rstest/core'; +import { defineConfig, defineProject, type ProjectConfig } from '@rstest/core'; + const root = path.resolve(__dirname, "../../"); process.env.NO_COLOR = '1'; @@ -9,7 +10,7 @@ const setupFilesAfterEnv = [ "@rspack/test-tools/setup-expect", ]; -const wasmConfig = process.env.WASM && defineConfig({ +const wasmConfig = process.env.WASM && defineProject({ setupFiles: [...setupFilesAfterEnv, "@rspack/test-tools/setup-wasm"], exclude: [ // Skip because they rely on snapshots @@ -31,20 +32,24 @@ const wasmConfig = process.env.WASM && defineConfig({ "NativeWatcher*.test.js", ], maxConcurrency: 1, - pool: { - maxWorkers: 1, - execArgv: ['--no-warnings', '--expose-gc', '--max-old-space-size=6144', '--experimental-vm-modules'], - } }); -export default defineConfig({ +const testFilter = process.argv.includes("--test") || process.argv.includes("-t") + ? process.argv[ + (process.argv.includes("-t") + ? process.argv.indexOf("-t") + : process.argv.indexOf("--test")) + 1 + ] + : undefined; + +const sharedConfig = defineProject({ setupFiles: setupFilesAfterEnv, testTimeout: process.env.CI ? 60000 : 30000, include: [ "*.test.js", ], slowTestThreshold: 5000, - // Retry on CI to reduce flakes + // Retry on CI to reduce flakes retry: process.env.CI ? 3 : 0, resolve: { alias: { @@ -70,26 +75,15 @@ export default defineConfig({ chaiConfig: process.env.CI ? { // show all info on CI truncateThreshold: 5000, - } : undefined, - pool: { - maxWorkers: "80%", - execArgv: ['--no-warnings', '--expose-gc', '--max-old-space-size=8192', '--experimental-vm-modules'], - }, env: { + RUST_BACKTRACE: 'full', updateSnapshot: process.argv.includes("-u") || process.argv.includes("--updateSnapshot") ? 'true' : 'false', RSPACK_DEV: 'false', RSPACK_EXPERIMENTAL: 'true', RSPACK_CONFIG_VALIDATE: "strict", - testFilter: - process.argv.includes("--test") || process.argv.includes("-t") - ? process.argv[ - (process.argv.includes("-t") - ? process.argv.indexOf("-t") - : process.argv.indexOf("--test")) + 1 - ] - : undefined, + testFilter, printLogger: process.env.DEBUG === "test" ? 'true' : 'false', __TEST_PATH__: __dirname, __TEST_FIXTURES_PATH__: path.resolve(__dirname, "fixtures"), @@ -104,8 +98,26 @@ export default defineConfig({ __RSPACK_TEST_TOOLS_PATH__: path.resolve(root, "packages/rspack-test-tools"), __DEBUG__: process.env.DEBUG === "test" ? 'true' : 'false', }, - hideSkippedTests: true, - reporters: ['default'], ...(wasmConfig || {}), +}) as ProjectConfig; + +export default defineConfig({ + projects: [{ + extends: sharedConfig, + name: 'base', + }, { + extends: sharedConfig, + name: 'hottest', + include: process.env.WASM ? [] : ["/*.hottest.js"], + env: { + RSPACK_HOT_TEST: 'true', + }, + }], + reporters: testFilter ? ['verbose'] : ['default'], + hideSkippedTests: true, + pool: { + maxWorkers: process.env.WASM ? 1 : "80%", + execArgv: ['--no-warnings', '--expose-gc', '--max-old-space-size=8192', '--experimental-vm-modules'], + }, }); diff --git a/tests/rspack-test/statsAPICases/verbose-time.js b/tests/rspack-test/statsAPICases/verbose-time.js index 50e7aabb4177..e86b4920a5f6 100644 --- a/tests/rspack-test/statsAPICases/verbose-time.js +++ b/tests/rspack-test/statsAPICases/verbose-time.js @@ -13,76 +13,78 @@ module.exports = { ?.toString({ all: false, logging: "verbose" }) .replace(/\d+ ms/g, "X ms") ).toMatchInlineSnapshot(` - LOG from rspack.Compilation - finish modules: X ms - optimize dependencies: X ms - rebuild chunk graph: X ms - create chunks: X ms - optimize: X ms - module ids: X ms - chunk ids: X ms - optimize code generation: X ms - code generation: X ms - runtime requirements.modules: X ms - runtime requirements.chunks: X ms - runtime requirements.entries: X ms - runtime requirements: X ms - hashing: hash chunks: X ms - hashing: hash runtime chunks: X ms - hashing: process full hash chunks: X ms - hashing: X ms - create module assets: X ms - create chunk assets: X ms - process assets: X ms - after process assets: X ms - after seal: X ms + LOG from rspack.Compilation + finish modules: X ms + optimize dependencies: X ms + rebuild chunk graph: X ms + optimize modules: X ms + optimize chunks: X ms + optimize tree: X ms + optimize chunk modules: X ms + module ids: X ms + chunk ids: X ms + optimize code generation: X ms + code generation: X ms + runtime requirements.modules: X ms + runtime requirements.chunks: X ms + runtime requirements.entries: X ms + runtime requirements: X ms + hashing: hash chunks: X ms + hashing: hash runtime chunks: X ms + hashing: process full hash chunks: X ms + hashing: X ms + create module assets: X ms + create chunk assets: X ms + process assets: X ms + after process assets: X ms + after seal: X ms - LOG from rspack.Compiler - make hook: X ms - make: X ms - finish make hook: X ms - finish compilation: X ms - seal compilation: X ms - emitAssets: X ms + LOG from rspack.Compiler + make hook: X ms + build module graph: X ms + finish make hook: X ms + finish compilation: X ms + seal compilation: X ms + emitAssets: X ms - LOG from rspack.EnsureChunkConditionsPlugin - ensure chunk conditions: X ms + LOG from rspack.EnsureChunkConditionsPlugin + ensure chunk conditions: X ms - LOG from rspack.ModuleConcatenationPlugin - select relevant modules: X ms - 0 potential root modules, 0 potential inner modules - sort relevant modules: X ms - find modules to concatenate: X ms - 0 candidates were considered for adding (0 cached failure, 0 already in config, 0 invalid module, 0 incorrect chunks, 0 incorrect dependency, 0 incorrect chunks of importer, 0 incorrect module dependency, 0 incorrect runtime condition, 0 importer failed, 0 added) - sort concat configurations: X ms + LOG from rspack.ModuleConcatenationPlugin + select relevant modules: X ms + 0 potential root modules, 0 potential inner modules + sort relevant modules: X ms + find modules to concatenate: X ms + 0 candidates were considered for adding (0 cached failure, 0 already in config, 0 invalid module, 0 incorrect chunks, 0 incorrect dependency, 0 incorrect chunks of importer, 0 incorrect module dependency, 0 incorrect runtime condition, 0 importer failed, 0 added) + sort concat configurations: X ms - LOG from rspack.RealContentHashPlugin - hash to asset names: X ms + LOG from rspack.RealContentHashPlugin + hash to asset names: X ms - LOG from rspack.RemoveEmptyChunksPlugin - remove empty chunks: X ms + LOG from rspack.RemoveEmptyChunksPlugin + remove empty chunks: X ms - LOG from rspack.SideEffectsFlagPlugin - prepare connections: X ms - find optimizable connections: X ms - do optimize connections: X ms - update connections: X ms - optimized 0 connections + LOG from rspack.SideEffectsFlagPlugin + prepare connections: X ms + find optimizable connections: X ms + do optimize connections: X ms + update connections: X ms + optimized 0 connections - LOG from rspack.SplitChunksPlugin - prepare module data: X ms - prepare cache groups: X ms - process cache groups: X ms - ensure max size fit: X ms + LOG from rspack.SplitChunksPlugin + prepare module data: X ms + prepare cache groups: X ms + process cache groups: X ms + ensure max size fit: X ms - LOG from rspack.buildChunkGraph - prepare entrypoints: X ms - process queue: X ms - extend chunkGroup runtime: X ms - 8 queue items processed (4 blocks) - 0 chunk groups connected - 0 chunk groups processed for merging (0 module sets) - 0 chunk group info updated (0 already connected chunk groups reconnected) - `); + LOG from rspack.buildChunkGraph + prepare entrypoints: X ms + process queue: X ms + extend chunkGroup runtime: X ms + 8 queue items processed (4 blocks) + 0 chunk groups connected + 0 chunk groups processed for merging (0 module sets) + 0 chunk group info updated (0 already connected chunk groups reconnected) + `); } }; diff --git a/tests/rspack-test/statsOutputCases/logging/__snapshots__/stats.txt b/tests/rspack-test/statsOutputCases/logging/__snapshots__/stats.txt index a2a269ae8c2f..7ec369a4be74 100644 --- a/tests/rspack-test/statsOutputCases/logging/__snapshots__/stats.txt +++ b/tests/rspack-test/statsOutputCases/logging/__snapshots__/stats.txt @@ -76,8 +76,10 @@ LOG from rspack.Compilation finish modules: xx ms optimize dependencies: xx ms rebuild chunk graph: xx ms - create chunks: xx ms - optimize: xx ms + optimize modules: xx ms + optimize chunks: xx ms + optimize tree: xx ms + optimize chunk modules: xx ms module ids: xx ms chunk ids: xx ms optimize code generation: xx ms @@ -98,7 +100,7 @@ LOG from rspack.Compilation LOG from rspack.Compiler make hook: xx ms - make: xx ms + build module graph: xx ms finish make hook: xx ms finish compilation: xx ms seal compilation: xx ms diff --git a/tests/rspack-test/statsOutputCases/preset-verbose/__snapshots__/stats.txt b/tests/rspack-test/statsOutputCases/preset-verbose/__snapshots__/stats.txt index 07aa3b214773..7dfef7c468f3 100644 --- a/tests/rspack-test/statsOutputCases/preset-verbose/__snapshots__/stats.txt +++ b/tests/rspack-test/statsOutputCases/preset-verbose/__snapshots__/stats.txt @@ -43,8 +43,10 @@ LOG from rspack.Compilation finish modules: xx ms optimize dependencies: xx ms rebuild chunk graph: xx ms - create chunks: xx ms - optimize: xx ms + optimize modules: xx ms + optimize chunks: xx ms + optimize tree: xx ms + optimize chunk modules: xx ms module ids: xx ms chunk ids: xx ms optimize code generation: xx ms @@ -65,7 +67,7 @@ LOG from rspack.Compilation LOG from rspack.Compiler make hook: xx ms - make: xx ms + build module graph: xx ms finish make hook: xx ms finish compilation: xx ms seal compilation: xx ms diff --git a/website/docs/en/contribute/development/testing.mdx b/website/docs/en/contribute/development/testing.mdx index 9c4c2c838660..e94a9e1398d4 100644 --- a/website/docs/en/contribute/development/testing.mdx +++ b/website/docs/en/contribute/development/testing.mdx @@ -41,7 +41,7 @@ You can run Rspack tests by running `./x test unit` or `pnpm run test:unit` at t You can also go to the `tests/rspack-test` folder and run `npm run test` to run test cases and add some arguments: - **When refreshing test snapshots is needed**: Add `-u`, like `npm run test -- -u` -- **When filtering test cases is needed**: Add `-t`, like `npm run test -- -t configCases/asset` to only run test cases from the `tests/rspack-test/configCases/asset` folder. Pattern matching supports regex, see [rstest](https://rstest.rs/config/test/testNamePattern) for details. +- **When filtering test cases is needed**: Add `-t`. Pattern matching supports regex, see [rstest](https://rstest.rs/config/test/testNamePattern) for details. ### Running tests @@ -51,8 +51,11 @@ You can run these test cases in the following ways: - Or run `npm run test` from the `tests/rspack-test` directory. - To update snapshots, run `npm run test -- -u` from the `tests/rspack-test` directory. - To pass specific rstest cli arguments, run `npm run test -- {args}` from the `tests/rspack-test` directory. -- To filter specific test cases, run `npm run test -- -t path-of-spec` from the `tests/rspack-test` directory. - - Like `npm run test -- -t configCases/asset` to only run test cases from the `tests/rspack-test/configCases/asset` folder (config will be automatically mapped to configCases, and other folders will work in a similar way). +- To filter specific test cases, run `npm run test -- -t ${testPath}` where `testPath` can be either an absolute or relative path. + - For example: + - `npm run test -- -t hotCases/json/error-in-json` (or `npm run test -- -t /Users/rspack/tests/rspack-test/hotCases/json/error-in-json`) will run all tests in the `error-in-json` test case. + - `npm run test -- -t hotCases/json` (or `npm run test -- -t /Users/rspack/tests/rspack-test/hotCases/json`) will run all test cases in the `json` directory. + - `npm run test -- -t hotCases` (or `npm run test -- -t /Users/rspack/tests/rspack-test/hotCases`) will run all test cases in the `hotCases` directory. - To use Rspack Wasm for running test cases, you need to additionally configure the following environment variables: 1. `NAPI_RS_FORCE_WASI=1`: Forces the use of Rspack Wasm instead of native binding 2. `WASM=1`: Enables Wasm-specific test configurations diff --git a/website/docs/zh/contribute/development/testing.mdx b/website/docs/zh/contribute/development/testing.mdx index 2c20865796bd..cf5f85274736 100644 --- a/website/docs/zh/contribute/development/testing.mdx +++ b/website/docs/zh/contribute/development/testing.mdx @@ -41,7 +41,7 @@ Rspack 的测试用例包括如下: 也可以进入 `tests/rspack-test` 文件夹并运行 `npm run test` 来运行测试用例,并且对测试流程进行更精细的控制: - **需要刷新测试快照时**:添加 `-u` 参数,如 `npm run test -- -u` -- **需要过滤测试用例时**:添加 `-t` 参数,如 `npm run test -- -t configCases/asset` 即可仅运行 `tests/rspack-test/configCases/asset` 文件夹下的用例。匹配支持正则,详见 [rstest](https://rstest.rs/config/test/testNamePattern) +- **需要过滤测试用例时**:添加 `-t` 参数。匹配支持正则,详见 [rstest](https://rstest.rs/config/test/testNamePattern) ### 运行测试 @@ -51,8 +51,11 @@ Rspack 的测试用例包括如下: - 或在 `tests/rspack-test` 目录下运行 `npm run test`。 - 如需更新 snapshot,在 `tests/rspack-test` 目录下运行 `npm run test -- -u`。 - 如需传入特定 rstest cli 参数,在 `tests/rspack-test` 目录下运行 `npm run test -- {args}`。 -- 如需过滤特定测试用例,在 `tests/rspack-test` 目录下运行 `npm run test -- -t path-of-spec`。 - - 如 `npm run test -- -t configCases/asset` 即可仅运行 `tests/rspack-test/configCases/asset` 文件夹下的用例(config 会自动映射到 configCases,其他文件夹类似)。 +- 如需过滤特定测试用例,运行 `npm run test -- -t ${testPath}`,其中 `${testPath}` 可以是绝对路径或相对路径。 + - 例如: + - `npm run test -- -t hotCases/json/error-in-json` (或 `npm run test -- -t /Users/rspack/tests/rspack-test/hotCases/json/error-in-json`) 将运行 `error-in-json` 测试用例中的所有测试。 + - `npm run test -- -t hotCases/json` (或 `npm run test -- -t /Users/rspack/tests/rspack-test/hotCases/json`) 将运行 `json` 目录下的所有测试用例。 + - `npm run test -- -t hotCases` (或 `npm run test -- -t /Users/rspack/tests/rspack-test/hotCases`) 将运行 `hotCases` 目录下的所有测试用例。 - 如需使用 Rspack Wasm 运行测试用例,需要额外配置以下环境变量: 1. `NAPI_RS_FORCE_WASI=1`: 强制使用 Rspack Wasm 而不是原生绑定 2. `WASM=1`:启用 Wasm 专用的测试配置 diff --git a/website/package.json b/website/package.json index 210d6464b294..96e9a1d63ea8 100644 --- a/website/package.json +++ b/website/package.json @@ -16,9 +16,9 @@ "sort-projects-words": "node ./sortProjectWords.js" }, "dependencies": { - "@rstack-dev/doc-ui": "1.12.2", + "@rstack-dev/doc-ui": "1.12.3", "axios": "^1.13.2", - "markdown-to-jsx": "^9.5.0", + "markdown-to-jsx": "^9.5.7", "mermaid": "^11.12.2", "react": "^19.2.3", "react-dom": "^19.2.3", @@ -27,15 +27,15 @@ }, "devDependencies": { "@rsbuild/plugin-sass": "^1.4.0", - "@rspress/core": "2.0.0-rc.4", - "@rspress/plugin-algolia": "2.0.0-rc.4", - "@rspress/plugin-client-redirects": "2.0.0-rc.4", - "@rspress/plugin-llms": "2.0.0-rc.4", - "@rspress/plugin-rss": "2.0.0-rc.4", - "@rspress/plugin-sitemap": "2.0.0-rc.4", + "@rspress/core": "2.0.0-rc.5", + "@rspress/plugin-algolia": "2.0.0-rc.5", + "@rspress/plugin-client-redirects": "2.0.0-rc.5", + "@rspress/plugin-llms": "2.0.0-rc.5", + "@rspress/plugin-rss": "2.0.0-rc.5", + "@rspress/plugin-sitemap": "2.0.0-rc.5", "@shikijs/transformers": "^3.21.0", - "@types/node": "^20.19.27", - "@types/react": "^19.2.7", + "@types/node": "^20.19.29", + "@types/react": "^19.2.8", "@types/semver": "^7.7.1", "case-police": "~2.1.1", "cspell": "^9.4.0", diff --git a/xtask/benchmark/benches/groups/build_chunk_graph.rs b/xtask/benchmark/benches/groups/build_chunk_graph.rs index f41612a55531..6fa00b983ff1 100644 --- a/xtask/benchmark/benches/groups/build_chunk_graph.rs +++ b/xtask/benchmark/benches/groups/build_chunk_graph.rs @@ -5,7 +5,9 @@ use criterion::criterion_group; use rspack::builder::Builder as _; use rspack_benchmark::Criterion; use rspack_core::{ - Compilation, Compiler, Experiments, Optimization, build_chunk_graph, fast_set, + Compilation, Compiler, Experiments, Optimization, build_chunk_graph, + build_module_graph::build_module_graph_pass, + fast_set, incremental::{Incremental, IncrementalOptions}, }; use rspack_error::Diagnostic; @@ -175,7 +177,9 @@ pub fn build_chunk_graph_benchmark_inner(c: &mut Criterion) { .call(&mut compiler.compilation) .await .unwrap(); - compiler.compilation.build_module_graph().await.unwrap(); + build_module_graph_pass(&mut compiler.compilation) + .await + .unwrap(); let mut side_effects_optimize_artifact = compiler.compilation.side_effects_optimize_artifact.take();