diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8760e0f6437..ff77bf5564d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -17,3 +17,10 @@ ## Test Plan + +## Require Documentation? + + + +- [ ] No +- [ ] Yes, the corresponding rspack-website PR is \_\_ diff --git a/.github/workflows/need-doc.yml b/.github/workflows/need-doc.yml index 715ad59d531..9dded26f475 100644 --- a/.github/workflows/need-doc.yml +++ b/.github/workflows/need-doc.yml @@ -4,7 +4,7 @@ name: Need Documentation on: pull_request: - types: [closed, labeled] + types: [labeled] jobs: check_label: @@ -15,7 +15,7 @@ jobs: doc: name: Need Documentation runs-on: ubuntu-latest - if: ${{ (github.event.pull_request.merged == true || github.event.label.name == 'need documentation') && contains(github.event.pull_request.labels.*.name, 'need documentation') }} + if: ${{ (github.event.label.name == 'need documentation') && contains(github.event.pull_request.labels.*.name, 'need documentation') }} steps: - uses: actions/github-script@v6 with: diff --git a/Cargo.lock b/Cargo.lock index ab59a1214cc..bd3f79ace16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,7 +140,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -151,7 +151,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -174,7 +174,7 @@ checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -599,7 +599,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eed5fff0d93c7559121e9c72bf9c242295869396255071ff2cb1617147b608c5" dependencies = [ "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -651,9 +651,9 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.5.0" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", "hashbrown 0.14.0", @@ -771,7 +771,7 @@ checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -831,7 +831,7 @@ dependencies = [ "pmutil", "proc-macro2", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -896,7 +896,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -1149,6 +1149,7 @@ checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ "equivalent", "hashbrown 0.14.0", + "serde", ] [[package]] @@ -1197,7 +1198,7 @@ dependencies = [ "pmutil", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -1713,6 +1714,23 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "oxc_resolver" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0cba9c2dd86cc336814aa1b2fb2e3c1bdee5cbf411debb736ab582856f249db" +dependencies = [ + "dashmap", + "dunce", + "indexmap 2.0.0", + "once_cell", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tracing", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -1833,7 +1851,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -1920,7 +1938,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -1971,7 +1989,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -2401,11 +2419,11 @@ dependencies = [ "rspack_plugin_wasm", "rspack_plugin_worker", "rspack_regex", + "rspack_swc_visitors", "rustc-hash", "serde", "serde_json", "swc_core", - "swc_plugin_import", "tokio", "tracing", ] @@ -2431,6 +2449,7 @@ dependencies = [ "mime_guess", "nodejs-resolver", "once_cell", + "oxc_resolver", "paste", "petgraph", "rayon", @@ -2445,6 +2464,7 @@ dependencies = [ "rspack_loader_runner", "rspack_regex", "rspack_sources", + "rspack_swc_visitors", "rspack_util", "rustc-hash", "serde", @@ -2598,15 +2618,20 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "either", "indexmap 1.9.3", "rspack_core", "rspack_error", "rspack_loader_runner", + "rspack_swc_visitors", "rspack_testing", "serde", "serde_json", "swc_config", "swc_core", + "swc_emotion", + "swc_plugin_import", + "xxhash-rust", ] [[package]] @@ -2670,12 +2695,13 @@ version = "0.1.0" dependencies = [ "async-recursion", "async-trait", + "futures", + "once_cell", + "regex", "rspack_core", "rspack_error", "rspack_regex", - "rspack_testing", "rspack_util", - "testing_macros", ] [[package]] @@ -2843,6 +2869,7 @@ dependencies = [ "rspack_hash", "rspack_identifier", "rspack_regex", + "rspack_swc_visitors", "rspack_testing", "rustc-hash", "serde_json", @@ -3041,9 +3068,9 @@ dependencies = [ [[package]] name = "rspack_sources" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945955c953f0a80eba0c6b9566012249a349df595a6d3ae10f562a99e35b2837" +checksum = "25a741e3d5c1b73996bad4beeefb8aa0bbffad40ba4f0c4b09080265c11bd7bd" dependencies = [ "dashmap", "dyn-clone", @@ -3058,6 +3085,19 @@ dependencies = [ "substring", ] +[[package]] +name = "rspack_swc_visitors" +version = "0.1.0" +dependencies = [ + "indexmap 1.9.3", + "once_cell", + "regex", + "serde", + "swc_core", + "swc_emotion", + "swc_plugin_import", +] + [[package]] name = "rspack_testing" version = "0.1.0" @@ -3265,9 +3305,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.171" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] @@ -3295,13 +3335,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -3317,9 +3357,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.100" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "indexmap 2.0.0", "itoa", @@ -3484,7 +3524,7 @@ dependencies = [ "pmutil", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -3535,7 +3575,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -3592,9 +3632,9 @@ dependencies = [ [[package]] name = "swc" -version = "0.265.9" +version = "0.266.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b459cd0cdd42afc8347f3dbbda137e9e4010523034dd550d647c99902cea7800" +checksum = "ee8d051cbb46162e3c7a7c2ffb43b0b2d9f1994a68dd84ef422afd752acd398c" dependencies = [ "anyhow", "base64", @@ -3667,9 +3707,9 @@ dependencies = [ [[package]] name = "swc_common" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cb7fcd56655c8ae7dcf2344f0be6cbff4d9c7cb401fe3ec8e56e1de8dfe582" +checksum = "9c84742fc22df1c293da5354c1cc8a5b45a045e9dc941005c1fd9cb4e9bdabc1" dependencies = [ "ahash 0.8.3", "ast_node", @@ -3718,14 +3758,14 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] name = "swc_core" -version = "0.82.9" +version = "0.83.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb84a4863eec3f95ddc851ff0be49f8e53ccea3eab0f91e93baa4cb22d7ab83" +checksum = "b6b167dfadf8606ac1dc5ae13d22d057f195eed89bb0ecb42b468a317b1ed39e" dependencies = [ "swc", "swc_atoms", @@ -3759,9 +3799,9 @@ dependencies = [ [[package]] name = "swc_css_ast" -version = "0.139.0" +version = "0.139.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b183736d78583b5261e690833b0e8a1d5acee4ad99142d0e3aabdec10795fc5" +checksum = "fab824eff88884673de1d6b84cdb5d3d71c0b903fcef62a3ec1f44f40477433f" dependencies = [ "is-macro", "string_enum", @@ -3771,9 +3811,9 @@ dependencies = [ [[package]] name = "swc_css_codegen" -version = "0.149.0" +version = "0.149.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4a9af319764a33a6e7daf99a2d0f979d7f06e72e8a01947eec607cb46ddf7d" +checksum = "aef989abd4b9ccf3caf6a4ab0ceb9f9e7d6a27c08585a20a7fc7b9db6c73a341" dependencies = [ "auto_impl", "bitflags 2.3.3", @@ -3796,14 +3836,14 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] name = "swc_css_compat" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c506282116236260f189f8f1eddd2ed582e52aac57e184dd9812d92a80915dd9" +checksum = "a1ee1b2b77e7daaf389237ca2656df01cf8c1a6f2d9b158459921b202a661f8a" dependencies = [ "bitflags 2.3.3", "once_cell", @@ -3818,9 +3858,9 @@ dependencies = [ [[package]] name = "swc_css_minifier" -version = "0.114.0" +version = "0.114.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cafbe90da565be736118b2d67855cc028f0c284e74e5fd1979822d5d5a2eec" +checksum = "21db6b6ef607d47d09a7e2fd0b8fd5ec29d05d1182f8d3d5eebef0f1b94c3f4d" dependencies = [ "serde", "swc_atoms", @@ -3832,9 +3872,9 @@ dependencies = [ [[package]] name = "swc_css_modules" -version = "0.27.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1a4caa2e6e5166ffb3e1d5400f261edad555dfdc3380371fe578ab496499ed8" +checksum = "ec7cf86bd0da899f551089bcb3fb122e821b0b860359e27875c1676ea0f3b98d" dependencies = [ "rustc-hash", "serde", @@ -3848,9 +3888,9 @@ dependencies = [ [[package]] name = "swc_css_parser" -version = "0.148.0" +version = "0.148.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c09f1b12f73eb425a38b2ecace95069e024da7bdf6c0b35ac9f0999c7d36bf" +checksum = "b02a3c11508487249aa571a908e673c540191de97d5139bb78ab03188dd57e26" dependencies = [ "lexical", "serde", @@ -3861,9 +3901,9 @@ dependencies = [ [[package]] name = "swc_css_prefixer" -version = "0.151.0" +version = "0.151.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff100f504e11804c290114290c0d7622fa9d4cc88d2fa013cc39a9d29f246ff9" +checksum = "274da87a8f0117ef86382b132812aa6a1b700b31c37ef95ce3bbde7e05f8c098" dependencies = [ "once_cell", "preset_env_base", @@ -3878,9 +3918,9 @@ dependencies = [ [[package]] name = "swc_css_utils" -version = "0.136.0" +version = "0.136.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2bb1a683a9ca9106a2c93c7266221f4e9d3ab85eb3eaee4ed78ea3b4dc820b" +checksum = "3eead47672e3c832e2e3fc3e523490c4822d80a7fc8c50e87a66f9ab7003b517" dependencies = [ "once_cell", "serde", @@ -3893,9 +3933,9 @@ dependencies = [ [[package]] name = "swc_css_visit" -version = "0.138.0" +version = "0.138.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52bde5bd8d92d993a3c69c809b51fadec06bb1c3c325fb951b79b481a74fd30a" +checksum = "83f01449a09b8a87ab4bd2ea6cbaaf74e39f9bfba3842a2918e998c5f9b428a4" dependencies = [ "serde", "swc_atoms", @@ -3906,9 +3946,9 @@ dependencies = [ [[package]] name = "swc_ecma_ast" -version = "0.109.0" +version = "0.109.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc2286cedd688a68f214faa1c19bb5cceab7c9c54d0cbe3273e4c1704e38f69" +checksum = "e063a1614daed3ea8be56e5dd8edb17003409088d2fc9ce4aca3378879812607" dependencies = [ "bitflags 2.3.3", "is-macro", @@ -3923,9 +3963,9 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "0.144.1" +version = "0.145.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e62ba2c0ed1f119fc1a76542d007f1b2c12854d54dea15f5491363227debe11" +checksum = "3387d6ec9e636999b76af7d604e430f62ac16aee7cff1ff9aa466d7387b59143" dependencies = [ "memchr", "num-bigint", @@ -3950,14 +3990,14 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] name = "swc_ecma_ext_transforms" -version = "0.108.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57eb7bbfbd7d0b4c2d5abf6936efb16d5a228508246a19795d59c849dbff073e" +checksum = "d995f94740b4cde4919e6e03d982230f755f49dac9dac52f0218254a1fd69f2b" dependencies = [ "phf", "swc_atoms", @@ -3969,9 +4009,9 @@ dependencies = [ [[package]] name = "swc_ecma_lints" -version = "0.87.3" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5f56635e73df9d6f4fa1ecbc1c65e9175ad2593fada76b13b05c5d2895b8a1d" +checksum = "c79cd55fe7b36ba9399fa66609c97cc9220f09a8e7d12e86d721360df1f9c107" dependencies = [ "auto_impl", "dashmap", @@ -3989,9 +4029,9 @@ dependencies = [ [[package]] name = "swc_ecma_loader" -version = "0.44.2" +version = "0.44.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d7c322462657ae27ac090a2c89f7e456c94416284a2f5ecf66c43a6a3c19d1" +checksum = "d30d2ee2ea9207263f723ac0fe701c7c22200e5bec13d3a9b9a9189e97931081" dependencies = [ "anyhow", "dashmap", @@ -4010,9 +4050,9 @@ dependencies = [ [[package]] name = "swc_ecma_minifier" -version = "0.186.7" +version = "0.187.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c1ba3e299e769936b47bfcf68932305a32b5cdf9da2875a12fa1e8be6d523f" +checksum = "5d71b5b6d1f76782e3f91f0ded61364dce32ed70a2b1cf48edb02f7f753b2608" dependencies = [ "arrayvec", "indexmap 1.9.3", @@ -4045,9 +4085,9 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "0.139.0" +version = "0.140.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eab46cb863bc5cd61535464e07e5b74d5f792fa26a27b9f6fd4c8daca9903b7" +checksum = "3c968599841fcecfdc2e490188ad93251897a1bb912882547e6889e14a368399" dependencies = [ "either", "num-bigint", @@ -4065,9 +4105,9 @@ dependencies = [ [[package]] name = "swc_ecma_preset_env" -version = "0.200.6" +version = "0.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be8c666c0a51cf3210ac208dc5ec76f7e2c680e346e8fc2a0cea9547d66c362" +checksum = "17148615f8e209fa42f22a4cb893e91b7097c3b65bb5b6202657ba3583b65170" dependencies = [ "anyhow", "dashmap", @@ -4090,9 +4130,9 @@ dependencies = [ [[package]] name = "swc_ecma_quote_macros" -version = "0.50.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf676875aea532ce2c4cd479f89de52e0590048d71ec1790c8fe99f33788216" +checksum = "b028b0675ad45b79b163c70e192f25b59d72366a2864c5d369dce707a38a1597" dependencies = [ "anyhow", "pmutil", @@ -4103,14 +4143,14 @@ dependencies = [ "swc_ecma_ast", "swc_ecma_parser", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] name = "swc_ecma_transforms" -version = "0.223.5" +version = "0.224.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "950df5006708476d43ffa815f7c00bb24460187395fed3dfbb42850676aebcb5" +checksum = "7bbb521b9cddc76c62d9f0243708dbb4797e957922a4953d9722aeef7a53d174" dependencies = [ "swc_atoms", "swc_common", @@ -4128,9 +4168,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "0.132.3" +version = "0.133.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e082ff07a2d31fd8e480dc590dd81efb75106e79bec24483dbece03630ca67b3" +checksum = "d7787d3607d628ad0cc2e7173770f6a43229ce46e55136e81e5fdeb0951dd6c9" dependencies = [ "better_scoped_tls", "bitflags 2.3.3", @@ -4152,9 +4192,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_classes" -version = "0.121.3" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71f54c8ce83b25e89dc60d09686ebc8c4d0376bc82c21ac1a14a1b0a339c714f" +checksum = "ad3878381c7a115528f90bd1df4a97ac82711b7013f60b1cbf43982ad31ec645" dependencies = [ "swc_atoms", "swc_common", @@ -4166,9 +4206,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_compat" -version = "0.158.4" +version = "0.159.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4da9073b7bfa9b2fd0a46f98fae9c1a205d1c7e8b91d8cd43c4077ce598c75b7" +checksum = "64d4fbea0d55e51492c6bedb37f42b57968df0b1469ef58a9d2532f978131aeb" dependencies = [ "arrayvec", "indexmap 1.9.3", @@ -4199,14 +4239,14 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] name = "swc_ecma_transforms_module" -version = "0.175.5" +version = "0.176.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c02853fd5e0a2301762acf29ce14789691e0794b085ccce0f04e6a40550c03" +checksum = "b040349682a36a0fb036351fdac4e99a6f1190186c1aed81db29ddb2ba1d0355" dependencies = [ "Inflector", "anyhow", @@ -4231,9 +4271,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_optimization" -version = "0.192.5" +version = "0.193.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89871886565a084262242caec0407222ff140c6ecaae6c8e97319c55920677e1" +checksum = "0128684058b111f809aa97af9c327523ad232cd4fea14c8f57ce1a3235a1ccf9" dependencies = [ "dashmap", "indexmap 1.9.3", @@ -4256,9 +4296,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_proposal" -version = "0.166.4" +version = "0.167.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2e61b71de4a1b403e6451d0c0fbeb66e27ef369c341a52b3827c9cf6df052c" +checksum = "00df76004234c6ee80a70a1d869cefe9b223d878b18b7bfd799b574112cd29bc" dependencies = [ "either", "rustc-hash", @@ -4276,9 +4316,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_react" -version = "0.178.5" +version = "0.179.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15bcdf31da1c05fa364b694cef09ec3daa128aa30a0322ae16c1d0e769883810" +checksum = "59b9c4a068b55238df8bd70730651d45b39d5c924fc4bf4ccbb912e48b406eac" dependencies = [ "base64", "dashmap", @@ -4300,9 +4340,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_typescript" -version = "0.182.5" +version = "0.183.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55df168be05f2ccdac3492168aa61bb8a18aef28059be33872593402781b6d01" +checksum = "8d7d39b89a675bc0a8e7b96135899faa2e5f1d29d3c628c16099bf3875ecf077" dependencies = [ "serde", "swc_atoms", @@ -4316,9 +4356,9 @@ dependencies = [ [[package]] name = "swc_ecma_usage_analyzer" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1aabf52dfd20abdbe5087107273d250d134f5b0fa2251cbb42bbfbec88404af" +checksum = "d71dc9b35f1f137c72badbadb705a2325d161ff603224ab0e07e6834774ea281" dependencies = [ "indexmap 1.9.3", "rustc-hash", @@ -4333,9 +4373,9 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11006a3398ffd4693c4d3b0a1b1a5030edbdc04228159f5301120a6178144708" +checksum = "5b6d6b59ebd31b25fe2692ff705c806961e7856de8b7e91fd0942328886cd315" dependencies = [ "indexmap 1.9.3", "num_cpus", @@ -4352,9 +4392,9 @@ dependencies = [ [[package]] name = "swc_ecma_visit" -version = "0.95.0" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f628ec196e76e67892441e14eef2e423a738543d32bffdabfeec20c29582117" +checksum = "2774848b306e17fa280c598ecb192cc2c72a1163942b02d48606514336e9e7c5" dependencies = [ "num-bigint", "swc_atoms", @@ -4366,9 +4406,9 @@ dependencies = [ [[package]] name = "swc_emotion" -version = "0.40.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7a80eb046e114d6af3e78c28ab870d1de411d91ba4ba2e9caca07627d3f4580" +checksum = "13abda43ddbc94149aca923fb331f13b90e70f7cc2fb54662ee4a2c51cb59b22" dependencies = [ "base64", "byteorder", @@ -4391,14 +4431,14 @@ dependencies = [ "pmutil", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] name = "swc_error_reporters" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6530d0def50c33d14064a43837b7e3c1fe8716ee6c3495a478835793caae2c97" +checksum = "c76b479ad1a69bec65b261354b8e2dec8ed0f9ed43c7b54ab053dc4923e1c90e" dependencies = [ "anyhow", "miette", @@ -4409,9 +4449,9 @@ dependencies = [ [[package]] name = "swc_fast_graph" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a407fff2eb5ce3bee7513bdd9531a7be0285bc1213500b6d98ad235428d94cce" +checksum = "e2f7297cdefdb54d8d09e0294c1aec3826825b1feefd0c25978365aa7f447a1c" dependencies = [ "indexmap 1.9.3", "petgraph", @@ -4421,9 +4461,9 @@ dependencies = [ [[package]] name = "swc_html" -version = "0.130.7" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60835eea257c70751214952a559d695691668b12bdb5cd32f3b1b9dcc3bbb96b" +checksum = "5b727c5f63469fa67caf3ef4d051084f0ecec5ee9e083aa0e2ab380a8d26d941" dependencies = [ "swc_html_ast", "swc_html_codegen", @@ -4433,9 +4473,9 @@ dependencies = [ [[package]] name = "swc_html_ast" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ae5b940a04624f50f74d5f0364374e6d471b8bea9ca41ca5e2027265ea7a9a1" +checksum = "d38e530672c482fd2f05f3366edcd3779034e0b1cc90b24e5d6ce4090493075d" dependencies = [ "is-macro", "string_enum", @@ -4445,9 +4485,9 @@ dependencies = [ [[package]] name = "swc_html_codegen" -version = "0.41.0" +version = "0.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a25cf73cbad0bbfe4fc93dd15a28f0d56f37319ad2573b87b2690f39fa3dd2" +checksum = "7806bef46cc4889b8f5a4c2f860858d305ed3d080253929f3b8efd27e491c2ca" dependencies = [ "auto_impl", "bitflags 2.3.3", @@ -4469,14 +4509,14 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] name = "swc_html_minifier" -version = "0.127.7" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4534419711311a306c38731245c62f5335784160f2e4ba8dca28dde63fbd9578" +checksum = "6241d0f1ad02b02b4c6171002edf80b57cad7c4acb88f10df1df71ae3849d430" dependencies = [ "once_cell", "serde", @@ -4503,9 +4543,9 @@ dependencies = [ [[package]] name = "swc_html_parser" -version = "0.38.0" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6457f6dcfb80749738cf57d068cf1c444a1746dd0f2be7da3398f93d4aee0d93" +checksum = "cdcd884e646add21950050313455abf910147b6a533f7a3c4386d9f74f9d5089" dependencies = [ "swc_atoms", "swc_common", @@ -4515,9 +4555,9 @@ dependencies = [ [[package]] name = "swc_html_utils" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21c0959bcc662d8199808e47b26c7f1314b1bdfa7c696de53ad53d84e667a761" +checksum = "15ce5b446cfa7ba0f5be9bc08535e8aa5bbcaf520249a62864b93350a4742b06" dependencies = [ "once_cell", "serde", @@ -4528,9 +4568,9 @@ dependencies = [ [[package]] name = "swc_html_visit" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f993bd3e860f2e6bf9f5d188be3ba1af3f40b95ef1562b515797700c560c97" +checksum = "6e783d8d07ea597d006ad91ce890e6406544209d6d89359bd6dd6935de986a4a" dependencies = [ "serde", "swc_atoms", @@ -4548,14 +4588,14 @@ dependencies = [ "pmutil", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] name = "swc_node_comments" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73cee5dededc1e0d19e53dd0a41a343a43e21ed9b62c3df0fdd5801c11533bc9" +checksum = "b2b9597573f1ab8bae72329eef550d214ced0955c7a4f1b6b4ae5e216219e710" dependencies = [ "dashmap", "swc_atoms", @@ -4574,9 +4614,9 @@ dependencies = [ [[package]] name = "swc_timer" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95c548665c811d1c5def583d3d6ca0291e5397122c0de6362bb201cc2de8beff" +checksum = "b740ce6b402ed04176bd28dc4f4f92c764fe0defe8437c2f3b6e1b5818b4e10c" dependencies = [ "tracing", ] @@ -4589,7 +4629,7 @@ checksum = "ff9719b6085dd2824fd61938a881937be14b08f95e2d27c64c825a9f65e052ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4613,7 +4653,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4629,9 +4669,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.25" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -4677,7 +4717,7 @@ dependencies = [ "quote", "regex", "relative-path", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4702,22 +4742,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4835,7 +4875,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -4858,7 +4898,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", ] [[package]] @@ -5116,7 +5156,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", "wasm-bindgen-shared", ] @@ -5138,7 +5178,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.29", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index 38b2b877519..94ad5d0d255 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ preset_env_base = { version = "0.4.5" } rayon = { version = "1.7.0" } regex = { version = "1.9.1" } rkyv = { version = "0.7.42" } -rspack_sources = { version = "0.2.6" } +rspack_sources = { version = "0.2.7" } rustc-hash = { version = "1.1.0" } schemars = { version = "0.8.12" } serde = { version = "1.0.171" } @@ -47,6 +47,7 @@ tracing-subscriber = { version = "0.3.17" } url = { version = "2.4.0" } urlencoding = { version = "2.1.2" } ustr = { version = "0.9.0" } +xxhash-rust = { version = "0.8.6" } # Pinned napi = { version = "=2.12.5" } @@ -54,14 +55,14 @@ napi-build = { version = "=2.0.1" } napi-derive = { version = "=2.12.3" } napi-sys = { version = "=2.2.3" } swc_config = { version = "=0.1.7" } -swc_core = { version = "=0.82.9", default-features = false } -swc_css = { version = "=0.155.0" } -swc_ecma_minifier = { version = "=0.186.7", default-features = false } -swc_emotion = { version = "=0.40.0" } -swc_error_reporters = { version = "=0.16.0" } -swc_html = { version = "=0.130.7" } -swc_html_minifier = { version = "=0.127.7" } -swc_node_comments = { version = "=0.19.0" } +swc_core = { version = "=0.83.1", default-features = false } +swc_css = { version = "=0.155.2" } +swc_ecma_minifier = { version = "=0.187.0", default-features = false } +swc_emotion = { version = "=0.42.0" } +swc_error_reporters = { version = "=0.16.1" } +swc_html = { version = "=0.131.0" } +swc_html_minifier = { version = "=0.128.0" } +swc_node_comments = { version = "=0.19.1" } tikv-jemallocator = { version = "=0.5.4", features = ["disable_initial_exec_tls"] } [profile.dev] diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 8a4a6e1ee1b..12b701a0f3b 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -133,7 +133,7 @@ export const enum BuiltinPluginKind { export function cleanupGlobalTrace(): void export interface FactoryMeta { - sideEffects?: boolean + sideEffectFree?: boolean } export interface JsAsset { @@ -188,6 +188,7 @@ export interface JsAssetInfoRelated { } export interface JsChunk { + name?: string files: Array } @@ -400,6 +401,7 @@ export interface JsStatsModule { issuerName?: string issuerId?: string issuerPath: Array + nameForCondition?: string reasons?: Array assets?: Array source?: string | Buffer @@ -504,7 +506,7 @@ export interface RawBannerConditions { } export interface RawBannerConfig { - banner: string + banner: RawBannerContent entryOnly?: boolean footer?: boolean raw?: boolean @@ -513,6 +515,18 @@ export interface RawBannerConfig { exclude?: RawBannerConditions } +export interface RawBannerContent { + type: "string" | "function" + stringPayload?: string + fnPayload?: (...args: any[]) => any +} + +export interface RawBannerContentFnCtx { + hash: string + chunk: JsChunk + filename: string +} + export interface RawBuiltins { css?: RawCssPluginConfig presetEnv?: RawPresetEnv @@ -733,6 +747,8 @@ export interface RawLibraryOptions { export interface RawMinification { passes: number dropConsole: boolean + keepClassNames: boolean + keepFnNames: boolean comments: "all" | "some" | "false" asciiOnly: boolean pureFuncs: Array @@ -763,9 +779,10 @@ export interface RawModuleOptions { export interface RawModuleRule { /** - * A conditional match matching an absolute path + query + fragment - * This one is reserved as our escape hatch for those who - * relies on some single-thread runtime behaviors. + * A conditional match matching an absolute path + query + fragment. + * Note: + * This is a custom matching rule not initially designed by webpack. + * Only for single-threaded environment interoperation purpose. */ rspackResource?: RawRuleSetCondition /** A condition matcher matching an absolute path. */ @@ -795,9 +812,7 @@ export interface RawModuleRule { } /** - * `loader` is for js side loader, `builtin_loader` is for rust side loader, - * which is mapped to real rust side loader by [get_builtin_loader]. - * + * `loader` is for both JS and Rust loaders. * `options` is * - a `None` on rust side and handled by js side `getOptions` when * using with `loader`. @@ -965,7 +980,7 @@ export interface RawResolveOptions { } export interface RawRspackFuture { - + newResolver: boolean } export interface RawRuleSetCondition { diff --git a/crates/node_binding/package.json b/crates/node_binding/package.json index b4f2ed4d9f9..21cff575349 100644 --- a/crates/node_binding/package.json +++ b/crates/node_binding/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/binding", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Node binding for rspack", "main": "binding.js", diff --git a/crates/node_binding/src/js_values/mod.rs b/crates/node_binding/src/js_values/mod.rs index 197cc5d9df9..c2959a499c8 100644 --- a/crates/node_binding/src/js_values/mod.rs +++ b/crates/node_binding/src/js_values/mod.rs @@ -1,5 +1,9 @@ +mod chunk { + // TODO: should we merge rspack_binding_options and node_binding? + pub use rspack_binding_options::chunk::*; +} + mod asset; -mod chunk; mod chunk_group; mod compilation; mod hooks; diff --git a/crates/node_binding/src/js_values/normal_module_factory.rs b/crates/node_binding/src/js_values/normal_module_factory.rs index a33afbaae08..feb0296a2eb 100644 --- a/crates/node_binding/src/js_values/normal_module_factory.rs +++ b/crates/node_binding/src/js_values/normal_module_factory.rs @@ -30,7 +30,7 @@ pub struct AfterResolveData { #[napi(object)] pub struct FactoryMeta { - pub side_effects: Option, + pub side_effect_free: Option, } #[napi(object)] @@ -98,7 +98,7 @@ impl From> for AfterResolveData { .map(|item| item.to_string_lossy().to_string()) .collect::>(), factory_meta: FactoryMeta { - side_effects: value.factory_meta.side_effects, + side_effect_free: value.factory_meta.side_effect_free, }, } } diff --git a/crates/node_binding/src/js_values/stats.rs b/crates/node_binding/src/js_values/stats.rs index 550e9b24682..26d97f18288 100644 --- a/crates/node_binding/src/js_values/stats.rs +++ b/crates/node_binding/src/js_values/stats.rs @@ -143,6 +143,22 @@ impl From<(String, rspack_core::LogType)> for JsStatsLogging { args: Some(vec![message]), trace: None, }, + rspack_core::LogType::Cache { label, hit, total } => Self { + name: value.0, + r#type: "cache".to_string(), + args: Some(vec![format!( + "{}: {:.1}% ({}/{})", + label, + if total == 0 { + 0 as f32 + } else { + hit as f32 / total as f32 * 100_f32 + }, + hit, + total, + )]), + trace: None, + }, } } } @@ -201,6 +217,7 @@ pub struct JsStatsModule { pub issuer_name: Option, pub issuer_id: Option, pub issuer_path: Vec, + pub name_for_condition: Option, pub reasons: Option>, pub assets: Option>, pub source: Option>, @@ -236,6 +253,7 @@ impl TryFrom> for JsStatsModule { issuer: stats.issuer, issuer_name: stats.issuer_name, issuer_id: stats.issuer_id, + name_for_condition: stats.name_for_condition, issuer_path: stats.issuer_path.into_iter().map(Into::into).collect(), reasons: stats .reasons diff --git a/crates/rspack_binding_options/Cargo.toml b/crates/rspack_binding_options/Cargo.toml index cfd04e8cb0d..9c9bd76b1a1 100644 --- a/crates/rspack_binding_options/Cargo.toml +++ b/crates/rspack_binding_options/Cargo.toml @@ -41,6 +41,7 @@ rspack_plugin_swc_js_minimizer = { path = "../rspack_plugin_swc_js_mini rspack_plugin_wasm = { path = "../rspack_plugin_wasm" } rspack_plugin_worker = { path = "../rspack_plugin_worker" } rspack_regex = { path = "../rspack_regex" } +rspack_swc_visitors = { path = "../rspack_swc_visitors" } anyhow = { workspace = true, features = ["backtrace"] } async-trait = { workspace = true } @@ -53,6 +54,5 @@ rustc-hash = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } swc_core = { workspace = true, default-features = false, features = ["ecma_transforms_react"] } -swc_plugin_import = { path = "../swc_plugin_import" } tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "test-util", "parking_lot"] } tracing = { workspace = true } diff --git a/crates/node_binding/src/js_values/chunk.rs b/crates/rspack_binding_options/src/chunk.rs similarity index 83% rename from crates/node_binding/src/js_values/chunk.rs rename to crates/rspack_binding_options/src/chunk.rs index e5872c86d95..eaefe924633 100644 --- a/crates/node_binding/src/js_values/chunk.rs +++ b/crates/rspack_binding_options/src/chunk.rs @@ -1,15 +1,18 @@ +use napi_derive::napi; use rspack_core::ChunkAssetArgs; #[napi(object)] pub struct JsChunk { + pub name: Option, pub files: Vec, } impl JsChunk { pub fn from(chunk: &rspack_core::Chunk) -> Self { + let name = chunk.name.clone(); let mut files = Vec::from_iter(chunk.files.iter().cloned()); files.sort_unstable(); - Self { files } + Self { name, files } } } diff --git a/crates/rspack_binding_options/src/lib.rs b/crates/rspack_binding_options/src/lib.rs index 4e0c9dc1bb1..960c3f89206 100644 --- a/crates/rspack_binding_options/src/lib.rs +++ b/crates/rspack_binding_options/src/lib.rs @@ -1,2 +1,3 @@ +pub mod chunk; mod options; pub use options::*; diff --git a/crates/rspack_binding_options/src/options/raw_builtins/raw_banner.rs b/crates/rspack_binding_options/src/options/raw_builtins/raw_banner.rs index cc39698b295..88bdfd82a2a 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/raw_banner.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/raw_banner.rs @@ -1,10 +1,20 @@ -use std::fmt::Debug; +use std::{fmt::Debug, sync::Arc}; +use derivative::Derivative; +use napi::{Env, JsFunction}; use napi_derive::napi; use rspack_error::internal_error; -use rspack_plugin_banner::{BannerCondition, BannerConditions, BannerConfig}; +use rspack_napi_shared::{ + threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}, + NapiResultExt, NAPI_ENV, +}; +use rspack_plugin_banner::{ + BannerCondition, BannerConditions, BannerConfig, BannerContent, BannerContentFnCtx, +}; use serde::Deserialize; +use crate::chunk::JsChunk; + #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] #[napi(object)] @@ -26,11 +36,81 @@ pub struct RawBannerConditions { pub array_matcher: Option>, } +#[napi(object)] +pub struct RawBannerContentFnCtx { + pub hash: String, + pub chunk: JsChunk, + pub filename: String, +} + +impl<'a> From> for RawBannerContentFnCtx { + fn from(value: BannerContentFnCtx) -> Self { + Self { + hash: value.hash.to_string(), + chunk: JsChunk::from(value.chunk), + filename: value.filename.to_string(), + } + } +} + +#[derive(Derivative, Deserialize)] +#[derivative(Debug)] +#[serde(rename_all = "camelCase")] +#[napi(object)] +pub struct RawBannerContent { + #[napi(ts_type = r#""string" | "function""#)] + pub r#type: String, + pub string_payload: Option, + #[derivative(Debug = "ignore")] + #[serde(skip_deserializing)] + pub fn_payload: Option, +} + +impl TryFrom for BannerContent { + type Error = rspack_error::Error; + + fn try_from(value: RawBannerContent) -> Result { + match value.r#type.as_str() { + "string" => { + let s = value.string_payload.ok_or_else(|| { + internal_error!("should have a string_payload when RawBannerContent.type is \"string\"") + })?; + Ok(BannerContent::String(s)) + } + "function" => { + let func = value.fn_payload.ok_or_else(|| { + internal_error!("should have a fn_payload when RawBannerContent.type is \"function\"") + })?; + let func: ThreadsafeFunction = + NAPI_ENV.with(|env| -> anyhow::Result<_> { + let env = env.borrow().expect("Failed to get env with external"); + let func_use = rspack_binding_macros::js_fn_into_threadsafe_fn!(func, &Env::from(env)); + Ok(func_use) + })?; + let func = Arc::new(func); + Ok(BannerContent::Fn(Box::new( + move |ctx: BannerContentFnCtx| { + let func = func.clone(); + Box::pin(async move { + func + .call(ctx.into(), ThreadsafeFunctionCallMode::NonBlocking) + .into_rspack_result()? + .await + .map_err(|err| internal_error!("Failed to call rule.use function: {err}"))? + }) + }, + ))) + } + _ => unreachable!(), + } + } +} + #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] #[napi(object)] pub struct RawBannerConfig { - pub banner: String, + pub banner: RawBannerContent, pub entry_only: Option, pub footer: Option, pub raw: Option, @@ -117,7 +197,7 @@ impl TryFrom for BannerConfig { } Ok(BannerConfig { - banner: value.banner, + banner: value.banner.try_into()?, entry_only: value.entry_only, footer: value.footer, raw: value.raw, diff --git a/crates/rspack_binding_options/src/options/raw_builtins/raw_swc_js_minimizer.rs b/crates/rspack_binding_options/src/options/raw_builtins/raw_swc_js_minimizer.rs index 4be726006fd..f834e6aeb80 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/raw_swc_js_minimizer.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/raw_swc_js_minimizer.rs @@ -30,6 +30,8 @@ pub struct RawMinificationConditions { pub struct RawMinification { pub passes: u32, pub drop_console: bool, + pub keep_class_names: bool, + pub keep_fn_names: bool, #[napi(ts_type = r#""all" | "some" | "false""#)] pub comments: String, pub ascii_only: bool, @@ -59,6 +61,8 @@ impl TryFrom for Minification { Ok(Self { passes: value.passes as usize, drop_console: value.drop_console, + keep_class_names: value.keep_class_names, + keep_fn_names: value.keep_fn_names, pure_funcs: value.pure_funcs, ascii_only: value.ascii_only, comments: value.comments, diff --git a/crates/rspack_binding_options/src/options/raw_builtins/raw_to_be_deprecated.rs b/crates/rspack_binding_options/src/options/raw_builtins/raw_to_be_deprecated.rs index cb5e1fbca4e..33fd5673145 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/raw_to_be_deprecated.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/raw_to_be_deprecated.rs @@ -1,17 +1,17 @@ use std::{path::PathBuf, str::FromStr}; use napi_derive::napi; -use rspack_core::{ - Builtins, DecoratorOptions, PluginExt, PresetEnv, ReactOptions, RelayConfig, RelayLanguageConfig, -}; +use rspack_core::{Builtins, DecoratorOptions, PluginExt, PresetEnv}; use rspack_error::internal_error; use rspack_plugin_css::{ plugin::{CssConfig, LocalIdentName, LocalsConvention, ModulesConfig}, CssPlugin, }; use rspack_plugin_dev_friendly_split_chunks::DevFriendlySplitChunksPlugin; +use rspack_swc_visitors::{ + CustomTransform, ImportOptions, ReactOptions, RelayLanguageConfig, RelayOptions, StyleConfig, +}; use serde::{Deserialize, Serialize}; -use swc_plugin_import::{CustomTransform, PluginImportConfig, StyleConfig}; #[derive(Deserialize, Debug, Serialize, Default, Clone)] #[serde(rename_all = "camelCase")] @@ -70,7 +70,7 @@ pub struct RawPluginImportConfig { pub ignore_style_component: Option>, } -impl From for PluginImportConfig { +impl From for ImportOptions { fn from(plugin_import: RawPluginImportConfig) -> Self { let RawPluginImportConfig { library_name, @@ -154,7 +154,7 @@ impl From for ReactOptions { Some(Runtime::Automatic) }; - ReactOptions { + Self { runtime, import_source: value.import_source, pragma: value.pragma, @@ -177,7 +177,7 @@ pub struct RawRelayConfig { pub language: String, } -impl From for RelayConfig { +impl From for RelayOptions { fn from(raw_config: RawRelayConfig) -> Self { Self { artifact_directory: raw_config.artifact_directory.map(PathBuf::from), diff --git a/crates/rspack_binding_options/src/options/raw_experiments.rs b/crates/rspack_binding_options/src/options/raw_experiments.rs index 94da9f858d8..aab912c8761 100644 --- a/crates/rspack_binding_options/src/options/raw_experiments.rs +++ b/crates/rspack_binding_options/src/options/raw_experiments.rs @@ -13,8 +13,9 @@ pub struct RawIncrementalRebuild { #[derive(Deserialize, Debug, Default)] #[serde(rename_all = "camelCase")] #[napi(object)] -#[allow(clippy::empty_structs_with_brackets)] -pub struct RawRspackFuture {} +pub struct RawRspackFuture { + pub new_resolver: bool, +} #[derive(Deserialize, Debug, Default)] #[serde(rename_all = "camelCase")] @@ -29,7 +30,9 @@ pub struct RawExperiments { } impl From for RspackFuture { - fn from(_value: RawRspackFuture) -> Self { - Self {} + fn from(value: RawRspackFuture) -> Self { + Self { + new_resolver: value.new_resolver, + } } } diff --git a/crates/rspack_binding_options/src/options/raw_module/js_loader.rs b/crates/rspack_binding_options/src/options/raw_module/js_loader.rs index a204d64201a..5ec0b7faa16 100644 --- a/crates/rspack_binding_options/src/options/raw_module/js_loader.rs +++ b/crates/rspack_binding_options/src/options/raw_module/js_loader.rs @@ -298,7 +298,7 @@ impl TryFrom<&rspack_core::LoaderContext<'_, rspack_core::LoaderRunnerContext>> current_loader: cx.current_loader().to_string(), is_pitching: true, context: External::new(cx.context.clone()), - diagnostics: External::new(cx.diagnostics.clone()), + diagnostics: External::new(cx.__diagnostics.clone()), }) } } @@ -358,8 +358,7 @@ pub async fn run_builtin_loader( ), asset_filenames: HashSet::from_iter(loader_context.asset_filenames.into_iter()), // Initialize with no diagnostic - diagnostics: vec![], - + __diagnostics: vec![], __resource_data: &ResourceData::new(Default::default(), Default::default()), __loader_items: LoaderItemList(list), __loader_index: 0, diff --git a/crates/rspack_binding_options/src/options/raw_module/mod.rs b/crates/rspack_binding_options/src/options/raw_module/mod.rs index 52c4a121b0f..60024b410ad 100644 --- a/crates/rspack_binding_options/src/options/raw_module/mod.rs +++ b/crates/rspack_binding_options/src/options/raw_module/mod.rs @@ -35,20 +35,20 @@ pub fn get_builtin_loader(builtin: &str, options: Option<&str>) -> BoxLoader { } if builtin.starts_with(SWC_LOADER_IDENTIFIER) { - return Arc::new(rspack_loader_swc::SwcLoader::new( - serde_json::from_str(options.unwrap_or("{}")).unwrap_or_else(|e| { - panic!("Could not parse builtin:swc-loader options:{options:?},error: {e:?}") - }), - Some(builtin.into()), - )); + return Arc::new( + rspack_loader_swc::SwcLoader::new( + serde_json::from_str(options.unwrap_or("{}")).unwrap_or_else(|e| { + panic!("Could not parse builtin:swc-loader options:{options:?},error: {e:?}") + }), + ) + .with_identifier(builtin.into()), + ); } unreachable!("Unexpected builtin loader: {builtin}") } -/// `loader` is for js side loader, `builtin_loader` is for rust side loader, -/// which is mapped to real rust side loader by [get_builtin_loader]. -/// +/// `loader` is for both JS and Rust loaders. /// `options` is /// - a `None` on rust side and handled by js side `getOptions` when /// using with `loader`. @@ -59,7 +59,6 @@ pub fn get_builtin_loader(builtin: &str, options: Option<&str>) -> BoxLoader { #[serde(rename_all = "camelCase")] #[napi(object)] pub struct RawModuleRuleUse { - #[serde(skip_deserializing)] pub loader: String, pub options: Option, } diff --git a/crates/rspack_core/Cargo.toml b/crates/rspack_core/Cargo.toml index 66d45ab0844..4ca90bfb2a2 100644 --- a/crates/rspack_core/Cargo.toml +++ b/crates/rspack_core/Cargo.toml @@ -23,6 +23,7 @@ itertools = { workspace = true } mime_guess = { workspace = true } nodejs-resolver = { version = "0.1.0" } once_cell = { workspace = true } +oxc_resolver = { version = "0.1.0" } paste = { workspace = true } petgraph = { version = "0.6.3", features = ["serde-1"] } rayon = { workspace = true } @@ -37,6 +38,7 @@ rspack_identifier = { path = "../rspack_identifier" } rspack_loader_runner = { path = "../rspack_loader_runner" } rspack_regex = { path = "../rspack_regex" } rspack_sources = { workspace = true } +rspack_swc_visitors = { path = "../rspack_swc_visitors" } rspack_util = { path = "../rspack_util" } rustc-hash = { workspace = true } serde = { workspace = true } diff --git a/crates/rspack_core/src/cache/occasion/code_generate.rs b/crates/rspack_core/src/cache/occasion/code_generate.rs index 6a1efc5bbb7..6ba1d103cde 100644 --- a/crates/rspack_core/src/cache/occasion/code_generate.rs +++ b/crates/rspack_core/src/cache/occasion/code_generate.rs @@ -21,14 +21,14 @@ impl CodeGenerateOccasion { module: &'a BoxModule, compilation: &Compilation, generator: G, - ) -> Result + ) -> Result<(CodeGenerationResult, bool)> where G: Fn(&'a BoxModule) -> Result, { let storage = match &self.storage { Some(s) => s, // no cache return directly - None => return generator(module), + None => return Ok((generator(module)?, false)), }; let mut cache_id = None; @@ -44,7 +44,7 @@ impl CodeGenerateOccasion { // currently no need to separate module hash by runtime if let Some(data) = storage.get(&id) { - return Ok(data); + return Ok((data, true)); } if matches!(normal_module.source(), NormalModuleSource::Unbuild) { @@ -59,6 +59,6 @@ impl CodeGenerateOccasion { if let Some(id) = cache_id { storage.set(id, data.clone()); } - Ok(data) + Ok((data, false)) } } diff --git a/crates/rspack_core/src/cache/occasion/resolve_module.rs b/crates/rspack_core/src/cache/occasion/resolve_module.rs index 57e13390f94..24cd981e2ea 100644 --- a/crates/rspack_core/src/cache/occasion/resolve_module.rs +++ b/crates/rspack_core/src/cache/occasion/resolve_module.rs @@ -29,7 +29,7 @@ impl ResolveModuleOccasion { &self, args: ResolveArgs<'a>, generator: G, - ) -> Result + ) -> Result<(Result, bool), ResolveError> where G: Fn(ResolveArgs<'a>) -> F, F: Future>, @@ -37,7 +37,7 @@ impl ResolveModuleOccasion { let storage = match &self.storage { Some(s) => s, // no cache return directly - None => return generator(args).await, + None => return Ok((generator(args).await, false)), }; let id = ModuleIdentifier::from(format!( @@ -60,7 +60,7 @@ impl ResolveModuleOccasion { .unwrap_or(false); if valid { - return Ok(data); + return Ok((Ok(data), true)); } }; } @@ -78,6 +78,6 @@ impl ResolveModuleOccasion { .await .map_err(|err| ResolveError(err.to_string(), err))?; storage.set(id, (snapshot, data.clone())); - Ok(data) + Ok((Ok(data), false)) } } diff --git a/crates/rspack_core/src/compiler/compilation.rs b/crates/rspack_core/src/compiler/compilation.rs index 5776e297f6e..8476e0f0bf2 100644 --- a/crates/rspack_core/src/compiler/compilation.rs +++ b/crates/rspack_core/src/compiler/compilation.rs @@ -35,15 +35,15 @@ use crate::{ is_source_equal, tree_shaking::{optimizer, visitor::SymbolRef, BailoutFlag, OptimizeDependencyResult}, AddQueue, AddTask, AddTaskResult, AdditionalChunkRuntimeRequirementsArgs, BoxDependency, - BoxModule, BuildQueue, BuildTask, BuildTaskResult, Chunk, ChunkByUkey, ChunkContentHash, - ChunkGraph, ChunkGroup, ChunkGroupUkey, ChunkHashArgs, ChunkKind, ChunkUkey, CleanQueue, - CleanTask, CleanTaskResult, CodeGenerationResult, CodeGenerationResults, CompilationLogger, - CompilationLogging, CompilerOptions, ContentHashArgs, DependencyId, Entry, EntryData, - EntryOptions, Entrypoint, FactorizeQueue, FactorizeTask, FactorizeTaskResult, Filename, Logger, - Module, ModuleGraph, ModuleIdentifier, ModuleProfile, ModuleType, PathData, ProcessAssetsArgs, - ProcessDependenciesQueue, ProcessDependenciesResult, ProcessDependenciesTask, RenderManifestArgs, - Resolve, ResolverFactory, RuntimeGlobals, RuntimeModule, RuntimeSpec, SharedPluginDriver, - SourceType, Stats, TaskResult, WorkerTask, + BoxModule, BuildQueue, BuildTask, BuildTaskResult, CacheCount, CacheOptions, Chunk, ChunkByUkey, + ChunkContentHash, ChunkGraph, ChunkGroup, ChunkGroupUkey, ChunkHashArgs, ChunkKind, ChunkUkey, + CleanQueue, CleanTask, CleanTaskResult, CodeGenerationResult, CodeGenerationResults, + CompilationLogger, CompilationLogging, CompilerOptions, ContentHashArgs, DependencyId, Entry, + EntryData, EntryOptions, Entrypoint, FactorizeQueue, FactorizeTask, FactorizeTaskResult, + Filename, Logger, Module, ModuleGraph, ModuleIdentifier, ModuleProfile, ModuleType, PathData, + ProcessAssetsArgs, ProcessDependenciesQueue, ProcessDependenciesResult, ProcessDependenciesTask, + RenderManifestArgs, Resolve, ResolverFactory, RuntimeGlobals, RuntimeModule, RuntimeSpec, + SharedPluginDriver, SourceType, Stats, TaskResult, WorkerTask, }; use crate::{tree_shaking::visitor::OptimizeAnalyzeResult, Context}; @@ -343,7 +343,7 @@ impl Compilation { #[instrument(name = "compilation:make", skip_all)] pub async fn make(&mut self, mut param: MakeParam) -> Result<()> { - let logger = self.get_logger("rspack.Compiler"); + let logger = self.get_logger("rspack.Compilation"); let start = logger.time("make hook"); if let Some(e) = self .plugin_driver @@ -387,7 +387,7 @@ impl Compilation { } async fn update_module_graph(&mut self, params: Vec) -> Result<()> { - let logger = self.get_logger("rspack.Compiler"); + let logger = self.get_logger("rspack.Compilation"); let deps_builder = RebuildDepsBuilder::new(params, &self.module_graph); let mut origin_module_deps = HashMap::default(); @@ -442,9 +442,8 @@ impl Compilation { .expect("dependency not found"); let parent_module = parent_module_identifier.and_then(|id| self.module_graph.module_by_identifier(&id)); - if parent_module_identifier.is_some() - && parent_module.is_none() - && dependency.as_module_dependency().is_none() + if (parent_module_identifier.is_some() && parent_module.is_none()) + || dependency.as_module_dependency().is_none() { return; } @@ -469,6 +468,15 @@ impl Compilation { let mut process_deps_time = logger.time_aggregate("module process dependencies task"); let mut factorize_time = logger.time_aggregate("module factorize task"); let mut build_time = logger.time_aggregate("module build task"); + + let mut build_cache_counter = None; + let mut factorize_cache_counter = None; + + if !(matches!(self.options.cache, CacheOptions::Disabled)) { + build_cache_counter = Some(logger.cache("module build cache")); + factorize_cache_counter = Some(logger.cache("module factorize cache")); + } + tokio::task::block_in_place(|| loop { let start = factorize_time.start(); while let Some(task) = factorize_queue.get_task() { @@ -617,7 +625,17 @@ impl Compilation { dependencies, current_profile, exports_info_related, + from_cache, } = task_result; + + if let Some(counter) = &mut factorize_cache_counter { + if from_cache { + counter.hit(); + } else { + counter.miss(); + } + } + let module_identifier = factory_result.module.identifier(); tracing::trace!("Module created: {}", &module_identifier); @@ -686,7 +704,17 @@ impl Compilation { build_result, diagnostics, current_profile, + from_cache, } = task_result; + + if let Some(counter) = &mut build_cache_counter { + if from_cache { + counter.hit(); + } else { + counter.miss(); + } + } + if self.options.builtins.tree_shaking.enable() { self .optimize_analyze_result_map @@ -778,6 +806,13 @@ impl Compilation { logger.time_aggregate_end(factorize_time); logger.time_aggregate_end(build_time); + if let Some(counter) = build_cache_counter { + logger.cache_end(counter); + } + if let Some(counter) = factorize_cache_counter { + logger.cache_end(counter); + } + // TODO @jerrykingxyz make update_module_graph a pure function self .make_failed_dependencies @@ -913,8 +948,15 @@ impl Compilation { #[instrument(name = "compilation:code_generation", skip(self))] async fn code_generation(&mut self) -> 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")), + }; + fn run_iteration( compilation: &mut Compilation, + codegen_cache_counter: &mut Option, filter_op: impl Fn(&(&ModuleIdentifier, &Box)) -> bool + Sync + Send, ) -> Result<()> { let results = compilation @@ -935,40 +977,53 @@ impl Compilation { .use_cache(module, compilation, |module| { module.code_generation(compilation) }) - .map(|result| (*module_identifier, result)) + .map(|(result, from_cache)| (*module_identifier, result, from_cache)) }) - .collect::>>()?; + .collect::>>()?; - results.into_iter().for_each(|(module_identifier, result)| { - compilation.code_generated_modules.insert(module_identifier); + results + .into_iter() + .for_each(|(module_identifier, result, from_cache)| { + if let Some(counter) = codegen_cache_counter { + if from_cache { + counter.hit(); + } else { + counter.miss(); + } + } + compilation.code_generated_modules.insert(module_identifier); - let runtimes = compilation - .chunk_graph - .get_module_runtimes(module_identifier, &compilation.chunk_by_ukey); - - compilation - .code_generation_results - .module_generation_result_map - .insert(module_identifier, result); - for runtime in runtimes.values() { - compilation.code_generation_results.add( - module_identifier, - runtime.clone(), - module_identifier, - ); - } - }); + let runtimes = compilation + .chunk_graph + .get_module_runtimes(module_identifier, &compilation.chunk_by_ukey); + + compilation + .code_generation_results + .module_generation_result_map + .insert(module_identifier, result); + for runtime in runtimes.values() { + compilation.code_generation_results.add( + module_identifier, + runtime.clone(), + module_identifier, + ); + } + }); Ok(()) } - run_iteration(self, |(_, module)| { + run_iteration(self, &mut codegen_cache_counter, |(_, module)| { module.get_code_generation_dependencies().is_none() })?; - run_iteration(self, |(_, module)| { + run_iteration(self, &mut codegen_cache_counter, |(_, module)| { module.get_code_generation_dependencies().is_some() })?; + if let Some(counter) = codegen_cache_counter { + logger.cache_end(counter); + } + Ok(()) } diff --git a/crates/rspack_core/src/compiler/mod.rs b/crates/rspack_core/src/compiler/mod.rs index 58014bceccc..60a8b2b8c02 100644 --- a/crates/rspack_core/src/compiler/mod.rs +++ b/crates/rspack_core/src/compiler/mod.rs @@ -53,8 +53,12 @@ where plugins: Vec>, output_filesystem: T, ) -> Self { - let resolver_factory = Arc::new(ResolverFactory::new(options.resolve.clone())); - let loader_resolver_factory = Arc::new(ResolverFactory::new(options.resolve_loader.clone())); + let new_resolver = options.experiments.rspack_future.new_resolver; + let resolver_factory = Arc::new(ResolverFactory::new(new_resolver, options.resolve.clone())); + let loader_resolver_factory = Arc::new(ResolverFactory::new( + new_resolver, + options.resolve_loader.clone(), + )); let (plugin_driver, options) = PluginDriver::new(options, plugins, resolver_factory.clone()); let cache = Arc::new(Cache::new(options.clone())); diff --git a/crates/rspack_core/src/compiler/queue.rs b/crates/rspack_core/src/compiler/queue.rs index 7d7fab5b9b0..bcdd48157c0 100644 --- a/crates/rspack_core/src/compiler/queue.rs +++ b/crates/rspack_core/src/compiler/queue.rs @@ -59,6 +59,7 @@ pub struct FactorizeTaskResult { pub is_entry: bool, pub current_profile: Option>, pub exports_info_related: ExportsInfoRelated, + pub from_cache: bool, } #[async_trait::async_trait] @@ -134,6 +135,7 @@ impl WorkerTask for FactorizeTask { Ok(TaskResult::Factorize(Box::new(FactorizeTaskResult { is_entry: self.is_entry, original_module_identifier: self.original_module_identifier, + from_cache: result.from_cache, factory_result: result, module_graph_module: Box::new(mgm), dependencies: self.dependencies, @@ -257,6 +259,7 @@ pub struct BuildTaskResult { pub build_result: Box, pub diagnostics: Vec, pub current_profile: Option>, + pub from_cache: bool, } #[async_trait::async_trait] @@ -320,6 +323,7 @@ impl WorkerTask for BuildTask { build_result: Box::new(build_result), diagnostics, current_profile: self.current_profile, + from_cache: is_cache_valid, })) }) } diff --git a/crates/rspack_core/src/context_module_factory.rs b/crates/rspack_core/src/context_module_factory.rs index 05df0b6223b..d5798ecbc15 100644 --- a/crates/rspack_core/src/context_module_factory.rs +++ b/crates/rspack_core/src/context_module_factory.rs @@ -102,11 +102,16 @@ impl ContextModuleFactory { }; let plugin_driver = &self.plugin_driver; - let resource_data = self + let (resource_data, from_cache) = match self .cache .resolve_module_occasion .use_cache(resolve_args, |args| resolve(args, plugin_driver)) - .await; + .await + { + Ok(result) => result, + Err(err) => (Err(err), false), + }; + let module = match resource_data { Ok(ResolveResult::Resource(resource)) => Box::new(ContextModule::new( ContextModuleOptions { @@ -154,6 +159,7 @@ impl ContextModuleFactory { missing_dependencies, context_dependencies, factory_meta, + from_cache, } .with_empty_diagnostic(), ) diff --git a/crates/rspack_core/src/dependency/context_element_dependency.rs b/crates/rspack_core/src/dependency/context_element_dependency.rs index f583cac4339..5c05084d12d 100644 --- a/crates/rspack_core/src/dependency/context_element_dependency.rs +++ b/crates/rspack_core/src/dependency/context_element_dependency.rs @@ -2,7 +2,7 @@ use swc_core::ecma::atoms::JsWord; use crate::{ AsDependencyTemplate, Context, ContextMode, ContextOptions, Dependency, DependencyCategory, - DependencyId, DependencyType, ExportsReferencedType, ModuleDependency, ModuleGraph, + DependencyId, DependencyType, ExtendedReferencedExport, ModuleDependency, ModuleGraph, ReferencedExport, RuntimeSpec, }; @@ -72,12 +72,12 @@ impl ModuleDependency for ContextElementDependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { + _runtime: Option<&RuntimeSpec>, + ) -> Vec { if let Some(referenced_exports) = &self.referenced_exports { - vec![ReferencedExport::new(referenced_exports.clone(), false)].into() + vec![ReferencedExport::new(referenced_exports.clone(), false).into()] } else { - ExportsReferencedType::Object + vec![ExtendedReferencedExport::Array(vec![])] } } } diff --git a/crates/rspack_core/src/dependency/mod.rs b/crates/rspack_core/src/dependency/mod.rs index 29db6047011..eee0e88aefc 100644 --- a/crates/rspack_core/src/dependency/mod.rs +++ b/crates/rspack_core/src/dependency/mod.rs @@ -31,7 +31,8 @@ use dyn_clone::{clone_trait_object, DynClone}; use crate::{ ChunkGroupOptionsKindRef, ConnectionState, Context, ContextMode, ContextOptions, ErrorSpan, - ModuleGraph, ModuleGraphConnection, ModuleIdentifier, ReferencedExport, RuntimeSpec, + ExtendedReferencedExport, ModuleGraph, ModuleGraphConnection, ModuleIdentifier, ReferencedExport, + RuntimeSpec, }; // Used to describe dependencies' types, see webpack's `type` getter in `Dependency` @@ -300,7 +301,7 @@ impl AsModuleDependency for T { pub type DependencyConditionFn = Box; pub trait Function: - Fn(&ModuleGraphConnection, &RuntimeSpec, &ModuleGraph) -> ConnectionState + Send + Sync + Fn(&ModuleGraphConnection, Option<&RuntimeSpec>, &ModuleGraph) -> ConnectionState + Send + Sync { fn clone_boxed(&self) -> Box; } @@ -309,7 +310,7 @@ pub trait Function: impl Function for T where T: 'static - + Fn(&ModuleGraphConnection, &RuntimeSpec, &ModuleGraph) -> ConnectionState + + Fn(&ModuleGraphConnection, Option<&RuntimeSpec>, &ModuleGraph) -> ConnectionState + Send + Sync + Clone, @@ -378,9 +379,9 @@ pub trait ModuleDependency: Dependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { - ExportsReferencedType::Object + _runtime: Option<&RuntimeSpec>, + ) -> Vec { + vec![ExtendedReferencedExport::Array(vec![])] } // an identifier to merge equal requests diff --git a/crates/rspack_core/src/exports_info.rs b/crates/rspack_core/src/exports_info.rs index 18853cc6fcc..8630eb0f6a6 100644 --- a/crates/rspack_core/src/exports_info.rs +++ b/crates/rspack_core/src/exports_info.rs @@ -107,7 +107,7 @@ impl ExportsInfoId { if let Some(ref exclude_exports) = exclude_exports { for name in exclude_exports { - self.export_info_mut(name, mg); + self.get_export_info(name, mg); } } @@ -222,7 +222,7 @@ impl ExportsInfoId { unreachable!() } - pub fn export_info_mut(&self, name: &JsWord, mg: &mut ModuleGraph) -> ExportsInfoId { + pub fn get_export_info(&self, name: &JsWord, mg: &mut ModuleGraph) -> ExportInfoId { let exports_info = mg.get_exports_info_by_id(self); let mut cur = exports_info; // get redirect chain, because you can't pass a mut ref into a recursive call @@ -239,8 +239,8 @@ impl ExportsInfoId { let is_last = i == len - 1; let exports_info = mg.get_exports_info_by_id(&id); let other_exports_info = exports_info.other_exports_info; - if exports_info.exports.contains_key(name) { - return id; + if let Some(export_info_id) = exports_info.exports.get(name) { + return *export_info_id; } if is_last { let other_export_info = mg @@ -254,7 +254,7 @@ impl ExportsInfoId { let exports_info = mg.get_exports_info_mut_by_id(&id); exports_info._exports_are_ordered = false; exports_info.exports.insert(name.clone(), new_info_id); - return id; + return new_info_id; } } unreachable!() @@ -273,6 +273,77 @@ impl ExportsInfoId { } Some(*self) } + + pub fn set_has_use_info(&self, mg: &mut ModuleGraph) { + let exports_info = mg.get_exports_info_by_id(self); + let side_effects_only_info_id = exports_info._side_effects_only_info; + let redirect_to_id = exports_info.redirect_to; + let other_exports_info_id = exports_info.other_exports_info; + // this clone aiming to avoid use the mutable ref and immutable ref at the same time. + let export_id_list = exports_info.exports.values().cloned().collect::>(); + for export_info in export_id_list { + export_info.set_has_use_info(mg); + } + side_effects_only_info_id.set_has_use_info(mg); + if let Some(redirect) = redirect_to_id { + redirect.set_has_use_info(mg); + } else { + other_exports_info_id.set_has_use_info(mg); + let other_exports_info = mg.get_export_info_mut_by_id(&other_exports_info_id); + if other_exports_info.can_mangle_use.is_none() { + other_exports_info.can_mangle_use = Some(true); + } + } + } + + pub fn set_used_without_info(&self, mg: &mut ModuleGraph, runtime: Option<&RuntimeSpec>) -> bool { + let mut changed = false; + let exports_info = mg.get_exports_info_mut_by_id(self); + let redirect = exports_info.redirect_to; + let other_exports_info_id = exports_info.other_exports_info; + // avoid use ref and mut ref at the same time + let export_info_id_list = exports_info.exports.values().cloned().collect::>(); + for export_info_id in export_info_id_list { + let flag = export_info_id.set_used_without_info(mg, runtime); + changed |= flag; + } + if let Some(redirect_to) = redirect { + let flag = redirect_to.set_used_without_info(mg, runtime); + changed |= flag; + } else { + let flag = other_exports_info_id.set_used(mg, UsageState::NoInfo, None); + changed |= flag; + let other_export_info = mg.get_export_info_mut_by_id(&other_exports_info_id); + if !matches!(other_export_info.can_mangle_use, Some(false)) { + other_export_info.can_mangle_use = Some(false); + changed = true; + } + } + changed + } + + pub fn set_used_in_unknown_way( + &self, + _mg: &mut ModuleGraph, + _runtime: Option<&RuntimeSpec>, + ) -> bool { + todo!() + } + + pub fn set_used_for_side_effects_only( + &self, + mg: &mut ModuleGraph, + runtime: Option<&RuntimeSpec>, + ) -> bool { + let exports_info = mg.get_exports_info_by_id(self); + let side_effects_only_info_id = exports_info._side_effects_only_info; + side_effects_only_info_id.set_used_conditionally( + mg, + Box::new(|value| value == &UsageState::Unused), + UsageState::Used, + runtime, + ) + } } impl Default for ExportsInfoId { @@ -344,7 +415,7 @@ impl ExportsInfo { pub fn get_used( &self, name: UsedName, - runtime: &RuntimeSpec, + runtime: Option<&RuntimeSpec>, module_graph: &ModuleGraph, ) -> UsageState { match &name { @@ -438,6 +509,80 @@ impl ExportInfoId { pub fn new() -> Self { Self(EXPORT_INFO_ID.fetch_add(1, Relaxed)) } + + fn set_has_use_info(&self, mg: &mut ModuleGraph) { + let export_info = mg.get_export_info_mut_by_id(self); + if !export_info.has_use_in_runtime_info { + export_info.has_use_in_runtime_info = true; + } + if export_info.can_mangle_use.is_none() { + export_info.can_mangle_use = Some(true); + } + if let Some(exports_info) = export_info.exports_info { + exports_info.set_has_use_info(mg); + } + } + + fn set_used_without_info(&self, mg: &mut ModuleGraph, runtime: Option<&RuntimeSpec>) -> bool { + let mut changed = false; + let flag = self.set_used(mg, UsageState::NoInfo, runtime); + changed |= flag; + let export_info = mg.get_export_info_mut_by_id(self); + if !matches!(export_info.can_mangle_use, Some(false)) { + export_info.can_mangle_use = Some(false); + changed = true; + } + changed + } + + fn set_used( + &self, + mg: &mut ModuleGraph, + new_value: UsageState, + runtime: Option<&RuntimeSpec>, + ) -> bool { + if runtime.is_some() { + // TODO: runtime optimization + todo!() + } else { + let export_info = mg.get_export_info_mut_by_id(self); + if export_info.global_used != Some(new_value) { + export_info.global_used = Some(new_value); + return true; + } + } + false + } + + pub fn set_used_conditionally( + &self, + mg: &mut ModuleGraph, + condition: UsageFilterFnTy, + new_value: UsageState, + runtime: Option<&RuntimeSpec>, + ) -> bool { + if runtime.is_some() { + // TODO: runtime optimization + todo!() + } else { + let export_info = mg.get_export_info_mut_by_id(self); + if let Some(global_used) = export_info.global_used { + if global_used != new_value && condition(&global_used) { + export_info.global_used = Some(new_value); + return true; + } + } else { + export_info.global_used = Some(new_value); + return true; + } + } + false + } + + pub fn get_nested_exports_info(&self, mg: &ModuleGraph) -> Option { + let export_info = mg.get_export_info_by_id(self); + export_info.exports_info + } } impl Default for ExportInfoId { fn default() -> Self { @@ -462,7 +607,7 @@ impl From for ExportInfoId { #[derive(Debug, Clone, Default)] #[allow(unused)] pub struct ExportInfo { - name: JsWord, + pub name: JsWord, module_identifier: Option, pub usage_state: UsageState, used_name: Option, @@ -477,6 +622,9 @@ pub struct ExportInfo { max_target_is_set: bool, pub exports_info: Option, pub exports_info_owned: bool, + pub has_use_in_runtime_info: bool, + pub can_mangle_use: Option, + pub global_used: Option, } impl ExportsHash for ExportInfo { @@ -513,6 +661,7 @@ pub enum ExportInfoProvided { Null, } +#[derive(Clone)] pub struct ResolvedExportInfoTarget { pub module: ModuleIdentifier, pub exports: Option>, @@ -529,23 +678,25 @@ pub enum ResolvedExportInfoTargetWithCircular { Circular, } -pub type ResolveFilterFnTy = Box; +pub type ResolveFilterFnTy = Box>; +pub type UsageFilterFnTy = Box>; -pub trait ResolveFilterFn: Fn(&ResolvedExportInfoTarget) -> bool + Send + Sync { - fn clone_boxed(&self) -> Box; +pub trait FilterFn: Fn(&T) -> bool + Send + Sync { + fn clone_boxed(&self) -> Box>; } /// Copy from https://github.com/rust-lang/rust/issues/24000#issuecomment-479425396 -impl ResolveFilterFn for T +impl FilterFn for T where - T: 'static + Fn(&ResolvedExportInfoTarget) -> bool + Send + Sync + Clone, + T: 'static + Fn(&F) -> bool + Send + Sync + Clone, + F: 'static, { - fn clone_boxed(&self) -> Box { + fn clone_boxed(&self) -> Box> { Box::new(self.clone()) } } -impl Clone for Box { +impl Clone for Box> { fn clone(&self) -> Self { self.clone_boxed() } @@ -553,7 +704,14 @@ impl Clone for Box { impl ExportInfo { // TODO: remove usage_state in the future - pub fn new(name: JsWord, usage_state: UsageState, _init_from: Option<&ExportInfo>) -> Self { + pub fn new(name: JsWord, usage_state: UsageState, init_from: Option<&ExportInfo>) -> Self { + let has_use_in_runtime_info = if let Some(init_from) = init_from { + init_from.has_use_in_runtime_info + } else { + false + }; + let can_mangle_use = init_from.and_then(|init_from| init_from.can_mangle_use); + Self { name, module_identifier: None, @@ -570,11 +728,14 @@ impl ExportInfo { exports_info: None, max_target: HashMap::default(), exports_info_owned: false, + has_use_in_runtime_info, + can_mangle_use, + global_used: None, } } // TODO - pub fn get_used(&self, _runtime: &RuntimeSpec) -> UsageState { + pub fn get_used(&self, _runtime: Option<&RuntimeSpec>) -> UsageState { UsageState::Unused } @@ -685,12 +846,7 @@ impl ExportInfo { .module_graph_module_by_identifier(&target.module) .expect("should have mgm") .exports; - let exports_info_id = id.export_info_mut(name, mg); - let exports_info = mg.get_exports_info_mut_by_id(&exports_info_id); - *exports_info - .exports - .get(name) - .expect("should have export info") + id.get_export_info(name, mg) }; if already_visited.contains(&export_info_id) { return Some(ResolvedExportInfoTargetWithCircular::Circular); @@ -927,36 +1083,78 @@ pub fn get_dependency_used_by_exports_condition( } } +/// refer https://github.com/webpack/webpack/blob/d15c73469fd71cf98734685225250148b68ddc79/lib/FlagDependencyUsagePlugin.js#L64 +#[derive(Clone)] +pub enum ExtendedReferencedExport { + Array(Vec), + Export(ReferencedExport), +} + +pub fn is_no_exports_referenced(exports: &[ExtendedReferencedExport]) -> bool { + exports.is_empty() +} + +pub fn is_exports_object_referenced(exports: &[ExtendedReferencedExport]) -> bool { + matches!(exports[..], [ExtendedReferencedExport::Array(ref arr)] if arr.is_empty()) +} + +pub fn create_no_exports_referenced() -> Vec { + vec![] +} + +pub fn create_exports_object_referenced() -> Vec { + vec![ExtendedReferencedExport::Array(vec![])] +} + +impl From> for ExtendedReferencedExport { + fn from(value: Vec) -> Self { + ExtendedReferencedExport::Array(value) + } +} +impl From for ExtendedReferencedExport { + fn from(value: ReferencedExport) -> Self { + ExtendedReferencedExport::Export(value) + } +} + +#[derive(Clone)] pub struct ReferencedExport { - _name: Vec, - _can_mangle: bool, + pub name: Vec, + pub can_mangle: bool, } impl ReferencedExport { pub fn new(_name: Vec, _can_mangle: bool) -> Self { - Self { _name, _can_mangle } + Self { + name: _name, + can_mangle: _can_mangle, + } } } impl Default for ReferencedExport { fn default() -> Self { Self { - _name: vec![], - _can_mangle: true, + name: vec![], + can_mangle: true, } } } pub fn process_export_info( module_graph: &ModuleGraph, - runtime: &RuntimeSpec, + runtime: Option<&RuntimeSpec>, referenced_export: &mut Vec>, prefix: Vec, - export_info: Option<&ExportInfo>, + export_info: Option, default_points_to_self: bool, already_visited: &mut HashSet, ) { - if let Some(export_info) = export_info { + if let Some(export_info_id) = export_info { + let export_info = module_graph + .export_info_map + .get(&export_info_id) + .expect("should have export info"); let used = export_info.get_used(runtime); if used == UsageState::Unused { return; @@ -991,7 +1189,7 @@ pub fn process_export_info( value.push(export_info.name.clone()); value }, - Some(export_info), + Some(export_info.id), false, already_visited, ); diff --git a/crates/rspack_core/src/logger.rs b/crates/rspack_core/src/logger.rs index 36b1898cb2c..b89354f2ce7 100644 --- a/crates/rspack_core/src/logger.rs +++ b/crates/rspack_core/src/logger.rs @@ -53,6 +53,11 @@ pub enum LogType { Status { message: String, }, + Cache { + label: &'static str, + hit: u32, + total: u32, + }, } impl LogType { @@ -72,6 +77,7 @@ impl LogType { LogType::Time { .. } => 1 << 11, LogType::Clear => 1 << 12, LogType::Status { .. } => 1 << 13, + LogType::Cache { .. } => 1 << 14, } } } @@ -208,6 +214,24 @@ pub trait Logger { subsec_nanos, }) } + + fn cache(&self, label: &'static str) -> CacheCount { + CacheCount { + label, + total: 0, + hit: 0, + } + } + + fn cache_end(&self, count: CacheCount) { + if count.total != 0 { + self.raw(LogType::Cache { + label: count.label, + hit: count.hit, + total: count.total, + }) + } + } } pub struct StartTime { @@ -246,6 +270,23 @@ impl StartTimeAggregate { } } +pub struct CacheCount { + label: &'static str, + hit: u32, + total: u32, +} + +impl CacheCount { + pub fn hit(&mut self) { + self.total += 1; + self.hit += 1; + } + + pub fn miss(&mut self) { + self.total += 1; + } +} + pub type CompilationLogging = Arc, BuildHasherDefault>>; pub struct CompilationLogger { diff --git a/crates/rspack_core/src/module.rs b/crates/rspack_core/src/module.rs index f01b5471b87..a7418582e98 100644 --- a/crates/rspack_core/src/module.rs +++ b/crates/rspack_core/src/module.rs @@ -10,12 +10,14 @@ use rspack_identifier::{Identifiable, Identifier}; use rspack_sources::Source; use rspack_util::ext::{AsAny, DynEq, DynHash}; use rustc_hash::FxHashSet as HashSet; +use swc_core::ecma::atoms::JsWord; use crate::tree_shaking::visitor::OptimizeAnalyzeResult; use crate::{ BoxDependency, ChunkUkey, CodeGenerationResult, Compilation, CompilerContext, CompilerOptions, - ConnectionState, Context, ContextModule, DependencyTemplate, ExternalModule, ModuleDependency, - ModuleGraph, ModuleType, NormalModule, RawModule, Resolve, SharedPluginDriver, SourceType, + ConnectionState, Context, ContextModule, DependencyId, DependencyTemplate, ExternalModule, + ModuleDependency, ModuleGraph, ModuleType, NormalModule, RawModule, Resolve, SharedPluginDriver, + SourceType, }; pub struct BuildContext<'a> { @@ -42,6 +44,8 @@ pub struct BuildInfo { pub missing_dependencies: HashSet, pub build_dependencies: HashSet, pub asset_filenames: HashSet, + pub harmony_named_exports: HashSet, + pub all_star_exports: Vec, } #[derive(Debug, Default, Clone, Hash)] @@ -127,7 +131,7 @@ pub struct BuildResult { #[derive(Debug, Default, Clone)] pub struct FactoryMeta { - pub side_effects: Option, + pub side_effect_free: Option, } pub type ModuleIdentifier = Identifier; diff --git a/crates/rspack_core/src/module_factory.rs b/crates/rspack_core/src/module_factory.rs index a8bac146b63..a9aa695ee14 100644 --- a/crates/rspack_core/src/module_factory.rs +++ b/crates/rspack_core/src/module_factory.rs @@ -19,6 +19,7 @@ pub struct ModuleFactoryResult { pub context_dependencies: HashSet, pub missing_dependencies: HashSet, pub factory_meta: FactoryMeta, + pub from_cache: bool, } impl ModuleFactoryResult { @@ -29,6 +30,7 @@ impl ModuleFactoryResult { context_dependencies: Default::default(), missing_dependencies: Default::default(), factory_meta: Default::default(), + from_cache: false, } } @@ -68,6 +70,11 @@ impl ModuleFactoryResult { self.factory_meta = factory_meta; self } + + pub fn from_cache(mut self, from_cache: bool) -> Self { + self.from_cache = from_cache; + self + } } #[async_trait::async_trait] diff --git a/crates/rspack_core/src/module_graph/connection.rs b/crates/rspack_core/src/module_graph/connection.rs index 57481709a87..f2b84606def 100644 --- a/crates/rspack_core/src/module_graph/connection.rs +++ b/crates/rspack_core/src/module_graph/connection.rs @@ -83,7 +83,7 @@ impl ModuleGraphConnection { self.active = value; } - pub fn is_active(&self, module_graph: &ModuleGraph, runtime: &RuntimeSpec) -> bool { + pub fn is_active(&self, module_graph: &ModuleGraph, runtime: Option<&RuntimeSpec>) -> bool { if !self.conditional { return self.active; } @@ -92,7 +92,11 @@ impl ModuleGraphConnection { .is_not_false() } - pub fn is_target_active(&self, module_graph: &ModuleGraph, runtime: &RuntimeSpec) -> bool { + pub fn is_target_active( + &self, + module_graph: &ModuleGraph, + runtime: Option<&RuntimeSpec>, + ) -> bool { if !self.conditional { return self.active; } @@ -102,7 +106,7 @@ impl ModuleGraphConnection { pub fn get_active_state( &self, module_graph: &ModuleGraph, - runtime: &RuntimeSpec, + runtime: Option<&RuntimeSpec>, ) -> ConnectionState { if !self.conditional { return ConnectionState::Bool(self.active); @@ -117,7 +121,7 @@ impl ModuleGraphConnection { pub fn get_condition_state( &self, module_graph: &ModuleGraph, - runtime: &RuntimeSpec, + runtime: Option<&RuntimeSpec>, ) -> ConnectionState { match self.condition.as_ref().expect("should have condition") { DependencyCondition::False => ConnectionState::Bool(false), diff --git a/crates/rspack_core/src/module_graph/mod.rs b/crates/rspack_core/src/module_graph/mod.rs index 04aae0181c9..73614dc3552 100644 --- a/crates/rspack_core/src/module_graph/mod.rs +++ b/crates/rspack_core/src/module_graph/mod.rs @@ -548,12 +548,34 @@ impl ModuleGraph { // } pub fn get_exports_info_by_id(&self, id: &ExportsInfoId) -> &ExportsInfo { - let exports_info = self.exports_info_map.get(id).expect("should have mgm"); + let exports_info = self + .exports_info_map + .get(id) + .expect("should have exports_info"); exports_info } pub fn get_exports_info_mut_by_id(&mut self, id: &ExportsInfoId) -> &mut ExportsInfo { - let exports_info = self.exports_info_map.get_mut(id).expect("should have mgm"); + let exports_info = self + .exports_info_map + .get_mut(id) + .expect("should have exports_info"); + exports_info + } + + pub fn get_export_info_by_id(&self, id: &ExportInfoId) -> &ExportInfo { + let export_info = self + .export_info_map + .get(id) + .expect("should have export info"); + export_info + } + + pub fn get_export_info_mut_by_id(&mut self, id: &ExportInfoId) -> &mut ExportInfo { + let exports_info = self + .export_info_map + .get_mut(id) + .expect("should have export info"); exports_info } } diff --git a/crates/rspack_core/src/normal_module.rs b/crates/rspack_core/src/normal_module.rs index 3796cefe6eb..edab59323ad 100644 --- a/crates/rspack_core/src/normal_module.rs +++ b/crates/rspack_core/src/normal_module.rs @@ -496,8 +496,8 @@ impl Module for NormalModule { module_chain: &mut HashSet, ) -> ConnectionState { if let Some(mgm) = module_graph.module_graph_module_by_identifier(&self.identifier()) { - if let Some(side_effect) = mgm.factory_meta.as_ref().and_then(|m| m.side_effects) { - return ConnectionState::Bool(side_effect); + if let Some(side_effect_free) = mgm.factory_meta.as_ref().and_then(|m| m.side_effect_free) { + return ConnectionState::Bool(!side_effect_free); } if let Some(side_effect_free) = mgm.build_meta.as_ref().and_then(|m| m.side_effect_free) && side_effect_free { // use module chain instead of is_evaluating_side_effects to mut module graph diff --git a/crates/rspack_core/src/normal_module_factory.rs b/crates/rspack_core/src/normal_module_factory.rs index a936dccb546..1cdfe180c32 100644 --- a/crates/rspack_core/src/normal_module_factory.rs +++ b/crates/rspack_core/src/normal_module_factory.rs @@ -183,16 +183,19 @@ impl NormalModuleFactory { let mut no_pre_post_auto_loaders = false; // with scheme, windows absolute path is considered scheme by `url` - let resource_data = if scheme != Scheme::None + let (resource_data, from_cache) = if scheme != Scheme::None && !Path::is_absolute(Path::new(request_without_match_resource)) { // resource with scheme - plugin_driver - .normal_module_factory_resolve_for_scheme(ResourceData::new( - request_without_match_resource.to_string(), - "".into(), - )) - .await? + ( + plugin_driver + .normal_module_factory_resolve_for_scheme(ResourceData::new( + request_without_match_resource.to_string(), + "".into(), + )) + .await?, + false, + ) } // TODO: resource within scheme, call resolveInScheme hook else { @@ -313,18 +316,25 @@ impl NormalModuleFactory { }; // default resolve - let resource_data = self + let (resource_data, from_cache) = match self .cache .resolve_module_occasion .use_cache(resolve_args, |args| resolve(args, plugin_driver)) - .await; + .await + { + Ok(result) => result, + Err(err) => (Err(err), false), + }; match resource_data { Ok(ResolveResult::Resource(resource)) => { let uri = resource.full_path().display().to_string(); - ResourceData::new(uri, resource.path) - .query_optional(resource.query) - .fragment_optional(resource.fragment) - .description_optional(resource.description_data) + ( + ResourceData::new(uri, resource.path) + .query_optional(resource.query) + .fragment_optional(resource.fragment) + .description_optional(resource.description_data), + from_cache, + ) } Ok(ResolveResult::Ignored) => { let ident = format!("{}/{}", &data.context, request_without_match_resource); @@ -340,7 +350,9 @@ impl NormalModuleFactory { self.context.module_type = Some(*raw_module.module_type()); return Ok(Some( - ModuleFactoryResult::new(raw_module).with_empty_diagnostic(), + ModuleFactoryResult::new(raw_module) + .from_cache(from_cache) + .with_empty_diagnostic(), )); } Err(ResolveError(runtime_error, internal_error)) => { @@ -350,6 +362,7 @@ impl NormalModuleFactory { let mut missing_dependencies = Default::default(); let diagnostics: Vec = internal_error.into(); let mut diagnostic = diagnostics[0].clone(); + let mut from_cache_result = from_cache; if !data .resolve_options .as_ref() @@ -373,11 +386,16 @@ impl NormalModuleFactory { missing_dependencies: &mut missing_dependencies, file_dependencies: &mut file_dependencies, }; - let resource_data = self + let (resource_data, from_cache) = match self .cache .resolve_module_occasion .use_cache(new_args, |args| resolve(args, plugin_driver)) - .await; + .await + { + Ok(result) => result, + Err(err) => (Err(err), false), + }; + from_cache_result = from_cache; if let Ok(ResolveResult::Resource(resource)) = resource_data { // TODO: Here windows resolver will return normalized path. // eg. D:\a\rspack\rspack\packages\rspack\tests\fixtures\errors\resolve-fail-esm\answer.js @@ -402,7 +420,9 @@ impl NormalModuleFactory { .boxed(); self.context.module_type = Some(*missing_module.module_type()); return Ok(Some( - ModuleFactoryResult::new(missing_module).with_diagnostic(vec![diagnostic]), + ModuleFactoryResult::new(missing_module) + .from_cache(from_cache_result) + .with_diagnostic(vec![diagnostic]), )); } } @@ -585,7 +605,9 @@ impl NormalModuleFactory { let (resolved_parser_options, resolved_generator_options) = self.calculate_parser_and_generator_options(&resolved_module_rules); let factory_meta = FactoryMeta { - side_effects: self.calculate_side_effects(&resolved_module_rules, &resource_data), + side_effect_free: self + .calculate_side_effects(&resolved_module_rules, &resource_data) + .map(|side_effects| !side_effects), }; let resolved_parser_and_generator = self @@ -636,6 +658,7 @@ impl NormalModuleFactory { .file_dependencies(file_dependencies) .missing_dependencies(missing_dependencies) .factory_meta(factory_meta) + .from_cache(from_cache) .with_empty_diagnostic(), )) } diff --git a/crates/rspack_core/src/options/builtins.rs b/crates/rspack_core/src/options/builtins.rs index c5eee1f09b1..3dada906cdf 100644 --- a/crates/rspack_core/src/options/builtins.rs +++ b/crates/rspack_core/src/options/builtins.rs @@ -1,15 +1,13 @@ use std::fmt::Debug; -use std::{collections::HashMap, fmt::Display, path::PathBuf}; +use std::{fmt::Display, path::PathBuf}; use glob::Pattern as GlobPattern; use rspack_error::Result; -use swc_core::ecma::transforms::react::Runtime; -use swc_plugin_import::PluginImportConfig; +pub use rspack_swc_visitors::{Define, Provide}; +use rspack_swc_visitors::{EmotionOptions, ImportOptions, ReactOptions, RelayOptions}; use crate::{ApplyContext, AssetInfo, CompilerOptions, Plugin, PluginContext}; -pub type Define = HashMap; - #[derive(Debug)] pub struct DefinePlugin { options: Define, @@ -36,8 +34,6 @@ impl Plugin for DefinePlugin { } } -pub type Provide = HashMap>; - #[derive(Debug)] pub struct ProvidePlugin { options: Provide, @@ -64,19 +60,6 @@ impl Plugin for ProvidePlugin { } } -#[derive(Debug, Clone, Default)] -pub struct ReactOptions { - pub runtime: Option, - pub import_source: Option, - pub pragma: Option, - pub pragma_frag: Option, - pub throw_if_namespace: Option, - pub development: Option, - pub use_builtins: Option, - pub use_spread: Option, - pub refresh: Option, -} - #[derive(Debug, Clone, Default)] pub struct DecoratorOptions { // https://swc.rs/docs/configuration/compilation#jsctransformlegacydecorator @@ -151,11 +134,11 @@ pub struct Builtins { // TODO: remove this when drop support for builtin options (0.6.0) pub no_emit_assets: bool, // TODO: migrate to builtin:swc-loader - pub emotion: Option, + pub emotion: Option, // TODO: migrate to builtin:swc-loader - pub plugin_import: Option>, + pub plugin_import: Option>, // TODO: migrate to builtin:swc-loader - pub relay: Option, + pub relay: Option, } #[derive(Debug, Clone)] @@ -212,22 +195,3 @@ pub struct PresetEnv { pub mode: Option, pub core_js: Option, } - -#[derive(Debug, Default, Clone)] -pub struct RelayConfig { - pub artifact_directory: Option, - pub language: RelayLanguageConfig, -} - -#[derive(Copy, Clone, Debug)] -pub enum RelayLanguageConfig { - JavaScript, - TypeScript, - Flow, -} - -impl Default for RelayLanguageConfig { - fn default() -> Self { - Self::Flow - } -} diff --git a/crates/rspack_core/src/options/experiments.rs b/crates/rspack_core/src/options/experiments.rs index 5561be010f3..c5afa9cce27 100644 --- a/crates/rspack_core/src/options/experiments.rs +++ b/crates/rspack_core/src/options/experiments.rs @@ -22,8 +22,9 @@ impl IncrementalRebuildMakeState { } #[derive(Debug, Default)] -#[allow(clippy::empty_structs_with_brackets)] -pub struct RspackFuture {} +pub struct RspackFuture { + pub new_resolver: bool, +} #[derive(Debug, Default)] pub struct Experiments { diff --git a/crates/rspack_core/src/options/externals.rs b/crates/rspack_core/src/options/externals.rs index a4d41ff268f..3f1f2f418a2 100644 --- a/crates/rspack_core/src/options/externals.rs +++ b/crates/rspack_core/src/options/externals.rs @@ -11,7 +11,7 @@ pub type Externals = Vec; pub enum ExternalItemValue { String(String), Bool(bool), - Array(Vec), // TODO: string[] | Record + Array(Vec), // TODO: Record } pub type ExternalItemObject = HashMap; diff --git a/crates/rspack_core/src/resolver/factory.rs b/crates/rspack_core/src/resolver/factory.rs index cd259c8049d..b36f28733d5 100644 --- a/crates/rspack_core/src/resolver/factory.rs +++ b/crates/rspack_core/src/resolver/factory.rs @@ -26,7 +26,7 @@ pub struct ResolverFactory { impl Default for ResolverFactory { fn default() -> Self { - Self::new(Resolve::default()) + Self::new(false, Resolve::default()) } } @@ -35,11 +35,10 @@ impl ResolverFactory { self.resolver.clear_cache(); } - pub fn new(options: Resolve) -> Self { - let resolver = Resolver::new(options.clone()); + pub fn new(new_resolver: bool, options: Resolve) -> Self { Self { - resolver, - base_options: options, + base_options: options.clone(), + resolver: Resolver::new(new_resolver, options), resolvers: Default::default(), } } diff --git a/crates/rspack_core/src/resolver/mod.rs b/crates/rspack_core/src/resolver/mod.rs index 6cf2269fdcd..0461ee44182 100644 --- a/crates/rspack_core/src/resolver/mod.rs +++ b/crates/rspack_core/src/resolver/mod.rs @@ -1,7 +1,7 @@ mod factory; mod resolver_impl; -use std::path::PathBuf; +use std::{fmt, path::PathBuf}; use rspack_error::Error; use rspack_loader_runner::DescriptionData; @@ -11,7 +11,7 @@ pub use self::resolver_impl::{ResolveInnerOptions, Resolver}; use crate::{ResolveArgs, SharedPluginDriver}; /// A successful path resolution or an ignored path. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum ResolveResult { Resource(Resource), Ignored, @@ -20,7 +20,7 @@ pub enum ResolveResult { /// A successful path resolution. /// /// Contains the raw `package.json` value if there is one. -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Resource { pub path: PathBuf, pub query: Option, @@ -28,6 +28,19 @@ pub struct Resource { pub description_data: Option, } +impl fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.full_path()) + } +} + +impl PartialEq for Resource { + fn eq(&self, other: &Self) -> bool { + self.path == other.path && self.query == other.query && self.fragment == other.fragment + } +} +impl Eq for Resource {} + impl Resource { /// Get the full path with query and fragment attached. pub fn full_path(&self) -> PathBuf { @@ -43,8 +56,16 @@ impl Resource { } /// A runtime error message and an error for rspack stats. +#[derive(Debug)] pub struct ResolveError(pub String, pub Error); +impl PartialEq for ResolveError { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} +impl Eq for ResolveError {} + /// Main entry point for module resolution. pub async fn resolve( args: ResolveArgs<'_>, @@ -52,21 +73,24 @@ pub async fn resolve( ) -> Result { let mut args = args; - let resolver = plugin_driver - .resolver_factory - .get(ResolveOptionsWithDependencyType { - resolve_options: args.resolve_options.take(), - resolve_to_context: args.resolve_to_context, - dependency_type: args.dependency_type.clone(), - dependency_category: *args.dependency_category, - }); + let dep = ResolveOptionsWithDependencyType { + resolve_options: args.resolve_options.take(), + resolve_to_context: args.resolve_to_context, + dependency_type: args.dependency_type.clone(), + dependency_category: *args.dependency_category, + }; + + let base_dir = args.context.clone(); + let base_dir = base_dir.as_ref(); - let base_dir = args.context.as_ref(); - let result = resolver.resolve(base_dir, args.specifier); + let resolver = plugin_driver.resolver_factory.get(dep); + let result = resolver + .resolve(base_dir, args.specifier) + .map_err(|error| error.into_resolve_error(&args, plugin_driver)); let (file_dependencies, missing_dependencies) = resolver.dependencies(); args.file_dependencies.extend(file_dependencies); args.missing_dependencies.extend(missing_dependencies); - result.map_err(|error| error.into_resolve_error(args, plugin_driver)) + result } diff --git a/crates/rspack_core/src/resolver/resolver_impl.rs b/crates/rspack_core/src/resolver/resolver_impl.rs index 9385bd552d6..47cf507cbdc 100644 --- a/crates/rspack_core/src/resolver/resolver_impl.rs +++ b/crates/rspack_core/src/resolver/resolver_impl.rs @@ -1,4 +1,5 @@ use std::{ + fmt, path::{Path, PathBuf}, sync::Arc, }; @@ -16,14 +17,28 @@ use crate::{ #[derive(Debug)] pub enum ResolveInnerError { NodejsResolver(nodejs_resolver::Error), - // OxcResolver(oxc_resolver::ResolveError), + OxcResolver(oxc_resolver::ResolveError), } /// Proxy to [nodejs_resolver::Options] or [oxc_resolver::ResolveOptions] -#[derive(Debug)] pub enum ResolveInnerOptions<'a> { NodejsResolver(&'a nodejs_resolver::Options), - // OxcResolver(&'a oxc_resolver::ResolveOptions), + OxcResolver(&'a oxc_resolver::ResolveOptions), +} + +impl<'a> fmt::Debug for ResolveInnerOptions<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::NodejsResolver(options) => { + let mut options = (*options).clone(); + options.external_cache = None; + write!(f, "{:?}", options) + } + Self::OxcResolver(options) => { + write!(f, "{:?}", options) + } + } + } } impl<'a> ResolveInnerOptions<'a> { @@ -33,31 +48,31 @@ impl<'a> ResolveInnerOptions<'a> { options.enforce_extension, nodejs_resolver::EnforceExtension::Enabled ), - // Self::OxcResolver(options) => matches!( - // options.enforce_extension, - // oxc_resolver::EnforceExtension::Enabled - // ), + Self::OxcResolver(options) => matches!( + options.enforce_extension, + oxc_resolver::EnforceExtension::Enabled + ), } } pub fn extensions(&self) -> impl Iterator { match self { Self::NodejsResolver(options) => options.extensions.iter(), - // Self::OxcResolver(options) => options.extensions.iter(), + Self::OxcResolver(options) => options.extensions.iter(), } } pub fn main_files(&self) -> impl Iterator { match self { Self::NodejsResolver(options) => options.main_files.iter(), - // Self::OxcResolver(options) => options.main_files.iter(), + Self::OxcResolver(options) => options.main_files.iter(), } } pub fn modules(&self) -> impl Iterator { match self { Self::NodejsResolver(options) => options.modules.iter(), - // Self::OxcResolver(options) => options.modules.iter(), + Self::OxcResolver(options) => options.modules.iter(), } } } @@ -68,12 +83,16 @@ impl<'a> ResolveInnerOptions<'a> { #[derive(Debug)] pub enum Resolver { NodejsResolver(nodejs_resolver::Resolver, Arc), - // OxcResolver(oxc_resolver::Resolver), + OxcResolver(oxc_resolver::Resolver), } impl Resolver { - pub fn new(options: Resolve) -> Self { - Self::new_nodejs_resolver(options) + pub fn new(new_resolver: bool, options: Resolve) -> Self { + if new_resolver { + Self::new_oxc_resolver(options) + } else { + Self::new_nodejs_resolver(options) + } } fn new_nodejs_resolver(options: Resolve) -> Self { @@ -84,11 +103,17 @@ impl Resolver { Self::NodejsResolver(resolver, cache) } + fn new_oxc_resolver(options: Resolve) -> Self { + let options = to_oxc_resolver_options(options, false, DependencyCategory::Unknown); + let resolver = oxc_resolver::Resolver::new(options); + Self::OxcResolver(resolver) + } + /// Clear cache for all resolver instances pub fn clear_cache(&self) { match self { Self::NodejsResolver(_, cache) => cache.entries.clear(), - // Self::OxcResolver(resolver) => resolver.clear_cache(), + Self::OxcResolver(resolver) => resolver.clear_cache(), } } @@ -108,9 +133,16 @@ impl Resolver { ); let resolver = nodejs_resolver::Resolver::new(options); Self::NodejsResolver(resolver, cache.clone()) - } /* Self::OxcResolver(_resolver) => { - * unimplemented!() - * } */ + } + Self::OxcResolver(resolver) => { + let options = to_oxc_resolver_options( + options, + options_with_dependency_type.resolve_to_context, + options_with_dependency_type.dependency_category, + ); + let resolver = resolver.clone_with_options(options); + Self::OxcResolver(resolver) + } } } @@ -125,7 +157,7 @@ impl Resolver { pub fn options(&self) -> ResolveInnerOptions<'_> { match self { Self::NodejsResolver(resolver, _) => ResolveInnerOptions::NodejsResolver(&resolver.options), - // Self::OxcResolver(resolver) => ResolveInnerOptions::OxcResolver(resolver.options()), + Self::OxcResolver(resolver) => ResolveInnerOptions::OxcResolver(resolver.options()), } } @@ -146,9 +178,20 @@ impl Resolver { nodejs_resolver::ResolveResult::Ignored => ResolveResult::Ignored, }) .map_err(ResolveInnerError::NodejsResolver), - // Self::OxcResolver(_resolver) => { - // unimplemented!() - // } + Self::OxcResolver(resolver) => match resolver.resolve(path, request) { + Ok(r) => Ok(ResolveResult::Resource(Resource { + path: r.path().to_path_buf(), + query: r.query().map(ToString::to_string), + fragment: r.fragment().map(ToString::to_string), + description_data: r + .package_json() + .map(|d| DescriptionData::new(d.directory().to_path_buf(), Arc::clone(&d.raw_json))), + })), + Err(error) if matches!(error, oxc_resolver::ResolveError::Ignored(_)) => { + Ok(ResolveResult::Ignored) + } + Err(error) => Err(ResolveInnerError::OxcResolver(error)), + }, } } } @@ -156,14 +199,12 @@ impl Resolver { impl ResolveInnerError { pub fn into_resolve_error( self, - args: ResolveArgs<'_>, + args: &ResolveArgs<'_>, plugin_driver: &SharedPluginDriver, ) -> ResolveError { match self { Self::NodejsResolver(error) => map_nodejs_resolver_error(error, args, plugin_driver), - // Self::OxcResolver(_error) => { - // unimplemented!() - // } + Self::OxcResolver(error) => map_oxc_resolver_error(error, args, plugin_driver), } } } @@ -231,13 +272,109 @@ fn to_nodejs_resolver_options( } } +fn to_oxc_resolver_options( + options: Resolve, + resolve_to_context: bool, + dependency_type: DependencyCategory, +) -> oxc_resolver::ResolveOptions { + let options = options.merge_by_dependency(dependency_type); + let tsconfig = options.tsconfig; + let enforce_extension = oxc_resolver::EnforceExtension::Auto; + let description_files = vec!["package.json".to_string()]; + let extensions = options.extensions.unwrap_or_else(|| { + vec![".tsx", ".ts", ".jsx", ".js", ".mjs", ".json"] + .into_iter() + .map(|s| s.to_string()) + .collect() + }); + let alias = options + .alias + .unwrap_or_default() + .into_iter() + .map(|(key, value)| { + let value = value + .into_iter() + .map(|x| match x { + nodejs_resolver::AliasMap::Target(target) => oxc_resolver::AliasValue::Path(target), + nodejs_resolver::AliasMap::Ignored => oxc_resolver::AliasValue::Ignore, + }) + .collect(); + (key, value) + }) + .collect(); + let prefer_relative = options.prefer_relative.unwrap_or(false); + let symlinks = options.symlinks.unwrap_or(true); + let main_files = options + .main_files + .unwrap_or_else(|| vec![String::from("index")]); + let main_fields = options + .main_fields + .unwrap_or_else(|| vec![String::from("module"), String::from("main")]); + let alias_fields = (if options.browser_field.unwrap_or(true) { + vec!["browser".to_string()] + } else { + vec![] + }) + .into_iter() + .map(|x| vec![x]) + .collect(); + let condition_names = options + .condition_names + .unwrap_or_else(|| vec!["module".to_string(), "import".to_string()]); + let modules = options + .modules + .unwrap_or_else(|| vec!["node_modules".to_string()]); + let fallback = options + .fallback + .unwrap_or_default() + .into_iter() + .map(|(key, value)| { + let value = value + .into_iter() + .map(|x| match x { + nodejs_resolver::AliasMap::Target(target) => oxc_resolver::AliasValue::Path(target), + nodejs_resolver::AliasMap::Ignored => oxc_resolver::AliasValue::Ignore, + }) + .collect(); + (key, value) + }) + .collect(); + let fully_specified = options.fully_specified.unwrap_or_default(); + let exports_fields = options + .exports_field + .unwrap_or_else(|| vec![vec!["exports".to_string()]]); + let extension_alias = options.extension_alias.unwrap_or_default(); + oxc_resolver::ResolveOptions { + fallback, + modules, + extensions, + enforce_extension, + alias, + prefer_relative, + symlinks, + alias_fields, + description_files, + main_files, + main_fields, + condition_names, + tsconfig, + resolve_to_context, + fully_specified, + exports_fields, + extension_alias, + // not supported by rspack yet + prefer_absolute: false, + restrictions: vec![], + roots: vec![], + builtin_modules: false, + } +} + fn map_nodejs_resolver_error( error: nodejs_resolver::Error, - args: ResolveArgs<'_>, + args: &ResolveArgs<'_>, plugin_driver: &SharedPluginDriver, ) -> ResolveError { - let base_dir: &Path = args.context.as_ref(); - let importer = args.importer.map(|i| i.as_str()); match error { nodejs_resolver::Error::Io(error) => { ResolveError(error.to_string(), Error::Io { source: error }) @@ -262,68 +399,104 @@ fn map_nodejs_resolver_error( internal_error!("{} is not a tsconfig", path.display()), ), _ => { - if let Some(importer) = &importer { - let span = args.span.unwrap_or_default(); - // Use relative path in runtime for stable hashing - let (runtime_message, internal_message) = if let nodejs_resolver::Error::Overflow = error { - ( - format!( - "Can't resolve {:?} in {} , maybe it had cycle alias", - args.specifier, - Path::new(&importer) - .relative(&plugin_driver.options.context) - .display() - ), - format!( - "Can't resolve {:?} in {} , maybe it had cycle alias", - args.specifier, importer - ), - ) - } else { - ( - format!( - "Failed to resolve {} in {}", - args.specifier, - base_dir.display() - ), - format!("Failed to resolve {} in {}", args.specifier, importer), - ) - }; - ResolveError( - runtime_message, - TraceableError::from_real_file_path( - Path::new( - importer - .split_once('|') - .map(|(_, path)| path) - .unwrap_or(importer), - ), - span.start as usize, - span.end as usize, - "Resolve error".to_string(), - internal_message.clone(), - ) - .map(|e| { - if args.optional { - Error::TraceableError(e.with_severity(Severity::Warn)) - } else { - Error::TraceableError(e) - } - }) - .unwrap_or_else(|_| { - if args.optional { - Error::InternalError(InternalError::new(internal_message, Severity::Warn)) - } else { - internal_error!(internal_message) - } - }), - ) - } else { - ResolveError( - format!("Failed to resolve {} in project root", args.specifier), - internal_error!("Failed to resolve {} in project root", args.specifier), - ) - } + let is_recursion = matches!(error, nodejs_resolver::Error::Overflow); + map_resolver_error(is_recursion, args, plugin_driver) + } + } +} + +fn map_oxc_resolver_error( + error: oxc_resolver::ResolveError, + args: &ResolveArgs<'_>, + plugin_driver: &SharedPluginDriver, +) -> ResolveError { + match error { + oxc_resolver::ResolveError::InvalidPackageTarget(specifier) => { + let message = format!( + "Export should be relative path and start with \"./\", but got {}", + specifier + ); + ResolveError( + message.clone(), + Error::Anyhow { + source: anyhow::Error::msg(message), + }, + ) } + _ => { + let is_recursion = matches!(error, oxc_resolver::ResolveError::Recursion); + map_resolver_error(is_recursion, args, plugin_driver) + } + } +} + +fn map_resolver_error( + is_recursion: bool, + args: &ResolveArgs<'_>, + plugin_driver: &SharedPluginDriver, +) -> ResolveError { + let base_dir: &Path = args.context.as_ref(); + let importer = args.importer.map(|i| i.as_str()); + if let Some(importer) = &importer { + let span = args.span.unwrap_or_default(); + // Use relative path in runtime for stable hashing + let (runtime_message, internal_message) = if is_recursion { + ( + format!( + "Can't resolve {:?} in {} , maybe it had cycle alias", + args.specifier, + Path::new(importer) + .relative(&plugin_driver.options.context) + .display() + ), + format!( + "Can't resolve {:?} in {} , maybe it had cycle alias", + args.specifier, importer + ), + ) + } else { + ( + format!( + "Failed to resolve {} in {}", + args.specifier, + base_dir.display() + ), + format!("Failed to resolve {} in {}", args.specifier, importer), + ) + }; + ResolveError( + runtime_message, + TraceableError::from_real_file_path( + Path::new( + importer + .split_once('|') + .map(|(_, path)| path) + .unwrap_or(importer), + ), + span.start as usize, + span.end as usize, + "Resolve error".to_string(), + internal_message.clone(), + ) + .map(|e| { + if args.optional { + Error::TraceableError(e.with_severity(Severity::Warn)) + } else { + Error::TraceableError(e) + } + }) + .unwrap_or_else(|_| { + if args.optional { + Error::InternalError(InternalError::new(internal_message, Severity::Warn)) + } else { + internal_error!(internal_message) + } + }), + ) + } else { + ResolveError( + format!("Failed to resolve {} in project root", args.specifier), + internal_error!("Failed to resolve {} in project root", args.specifier), + ) } } diff --git a/crates/rspack_core/src/stats.rs b/crates/rspack_core/src/stats.rs index 9fdbb450ebd..3e56fc98e0e 100644 --- a/crates/rspack_core/src/stats.rs +++ b/crates/rspack_core/src/stats.rs @@ -443,6 +443,7 @@ impl Stats<'_> { r#type: "module", module_type: *module.module_type(), identifier, + name_for_condition: module.name_for_condition().map(|n| n.to_string()), name: module .readable_identifier(&self.compilation.options.context) .into(), @@ -562,6 +563,7 @@ pub struct StatsModule<'a> { pub module_type: ModuleType, pub identifier: ModuleIdentifier, pub name: String, + pub name_for_condition: Option, pub id: Option, pub chunks: Vec, pub size: f64, diff --git a/crates/rspack_core/src/tree_shaking/visitor.rs b/crates/rspack_core/src/tree_shaking/visitor.rs index cc825d60130..d507d74465e 100644 --- a/crates/rspack_core/src/tree_shaking/visitor.rs +++ b/crates/rspack_core/src/tree_shaking/visitor.rs @@ -1335,10 +1335,10 @@ impl<'a> ModuleRefAnalyze<'a> { // sideEffects in module.rule has higher priority, // we could early return if we match a rule. if let Some(FactoryMeta { - side_effects: Some(side_effects), + side_effect_free: Some(side_effect_free), }) = factory_meta { - return Some(SideEffectType::Configuration(*side_effects)); + return Some(SideEffectType::Configuration(!*side_effect_free)); } None } diff --git a/crates/rspack_hash/Cargo.toml b/crates/rspack_hash/Cargo.toml index c5477e364c2..9a3f27e1082 100644 --- a/crates/rspack_hash/Cargo.toml +++ b/crates/rspack_hash/Cargo.toml @@ -10,4 +10,4 @@ version = "0.1.0" data-encoding = { version = "2.4.0" } md4 = "0.10.2" smol_str = { version = "*" } -xxhash-rust = { version = "0.8.6", features = ["xxh3"] } +xxhash-rust = { workspace = true, features = ["xxh3"] } diff --git a/crates/rspack_loader_runner/src/runner.rs b/crates/rspack_loader_runner/src/runner.rs index 77aeb847508..bc378a60066 100644 --- a/crates/rspack_loader_runner/src/runner.rs +++ b/crates/rspack_loader_runner/src/runner.rs @@ -167,13 +167,22 @@ pub struct LoaderContext<'c, C> { pub asset_filenames: HashSet, + // Only used for cross-crate accessing. + // This field should not be accessed in builtin loaders. pub __loader_index: usize, + // Only used for cross-crate accessing. + // This field should not be accessed in builtin loaders. pub __loader_items: LoaderItemList<'c, C>, + // Only used for cross-crate accessing. + // This field should not be accessed in builtin loaders. #[derivative(Debug = "ignore")] pub __plugins: &'c [Box], + // Only used for cross-crate accessing. + // This field should not be accessed in builtin loaders. pub __resource_data: &'c ResourceData, - - pub diagnostics: Vec, + // Only used for cross-crate accessing. + // This field should not be accessed in builtin loaders. + pub __diagnostics: Vec, } impl<'c, C> LoaderContext<'c, C> { @@ -203,6 +212,11 @@ impl<'c, C> LoaderContext<'c, C> { pub fn loader_index(&self) -> usize { self.__loader_index } + + /// Emit a diagnostic, it can be a `warning` or `error`. + pub fn emit_diagnostic(&mut self, diagnostic: Diagnostic) { + self.__diagnostics.push(diagnostic) + } } async fn process_resource(loader_context: &mut LoaderContext<'_, C>) -> Result<()> { @@ -276,7 +290,7 @@ async fn create_loader_context<'c, C: 'c>( __loader_items: LoaderItemList(__loader_items), __plugins: plugins, __resource_data: resource_data, - diagnostics: vec![], + __diagnostics: vec![], }; Ok(loader_context) @@ -377,7 +391,7 @@ impl TryFrom> for TWithDiagnosticArray { source_map: loader_context.source_map, additional_data: loader_context.additional_data, } - .with_diagnostic(loader_context.diagnostics), + .with_diagnostic(loader_context.__diagnostics), ) } } diff --git a/crates/rspack_loader_sass/src/lib.rs b/crates/rspack_loader_sass/src/lib.rs index d95a7f364de..7928d3b0668 100644 --- a/crates/rspack_loader_sass/src/lib.rs +++ b/crates/rspack_loader_sass/src/lib.rs @@ -506,9 +506,9 @@ impl Loader for SassLoader { loader_context.content = Some(result.css.into()); loader_context.source_map = source_map; - loader_context - .diagnostics - .append(&mut rx.into_iter().flatten().collect_vec()); + rx.into_iter().flatten().for_each(|d| { + loader_context.emit_diagnostic(d); + }); Ok(()) } } diff --git a/crates/rspack_loader_swc/Cargo.toml b/crates/rspack_loader_swc/Cargo.toml index 86e871ff133..a40371847a6 100644 --- a/crates/rspack_loader_swc/Cargo.toml +++ b/crates/rspack_loader_swc/Cargo.toml @@ -10,9 +10,11 @@ version = "0.1.0" [dependencies] anyhow = { workspace = true } async-trait = { workspace = true } +either = "1" rspack_core = { path = "../rspack_core" } rspack_error = { path = "../rspack_error" } rspack_loader_runner = { path = "../rspack_loader_runner" } +rspack_swc_visitors = { path = "../rspack_swc_visitors" } serde = { workspace = true, features = ["derive"] } serde_json = "1.0.100" swc_config = { workspace = true } @@ -24,6 +26,9 @@ swc_core = { workspace = true, features = [ # "plugin_transform_host_native_filesystem_cache", # "plugin_transform_host_native", ] } +swc_emotion = { workspace = true } +swc_plugin_import = { path = "../swc_plugin_import" } +xxhash-rust = { workspace = true, features = ["xxh32"] } [dev-dependencies] indexmap = { workspace = true } diff --git a/crates/rspack_loader_swc/src/lib.rs b/crates/rspack_loader_swc/src/lib.rs index 947a491429a..ef186a0c488 100644 --- a/crates/rspack_loader_swc/src/lib.rs +++ b/crates/rspack_loader_swc/src/lib.rs @@ -4,143 +4,45 @@ use std::default::Default; use std::sync::Arc; use rspack_core::{rspack_sources::SourceMap, LoaderRunnerContext, Mode}; -use rspack_error::{errors_to_diagnostics, internal_error, Diagnostic, Error, Result}; +use rspack_error::{internal_error, Diagnostic, Result}; use rspack_loader_runner::{Identifiable, Identifier, Loader, LoaderContext}; -use serde::Deserialize; -use swc_config::config_types::{BoolConfig, MergingOption}; -use swc_config::merge::Merge; -use swc_core::base::config::{ - Config, ErrorConfig, FileMatcher, InputSourceMap, IsModule, JscConfig, ModuleConfig, Options, - SourceMapsConfig, TransformConfig, +use swc_config::{config_types::MergingOption, merge::Merge}; +use swc_core::base::{ + config::{InputSourceMap, TransformConfig}, + try_with_handler, Compiler, +}; +use swc_core::common::{ + comments::SingleThreadedComments, FileName, FilePathMapping, Mark, GLOBALS, }; -use swc_core::base::{try_with_handler, Compiler}; -use swc_core::common::comments::SingleThreadedComments; -use swc_core::common::{FileName, FilePathMapping, GLOBALS}; use swc_core::ecma::transforms::base::pass::noop; +use xxhash_rust::xxh32::xxh32; -pub const SOURCE_MAP_INLINE: &str = "inline"; - -#[derive(Debug, Default, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct SwcLoaderJsOptions { - #[serde(default)] - pub source_maps: Option, - - pub source_map: Option, - #[serde(default)] - pub env: Option, - - #[serde(default)] - pub test: Option, - - #[serde(default)] - pub exclude: Option, - - #[serde(default)] - pub jsc: JscConfig, - - #[serde(default)] - pub module: Option, - - #[serde(default)] - pub minify: BoolConfig, +mod options; +mod transformer; - #[serde(default)] - pub input_source_map: Option, - - #[serde(default)] - pub inline_sources_content: BoolConfig, - - #[serde(default)] - pub emit_source_map_columns: BoolConfig, - - #[serde(default)] - pub error: ErrorConfig, - - #[serde(default)] - pub is_module: Option, - - #[serde(rename = "$schema")] - pub schema: Option, -} - -#[derive(Debug, Default, Deserialize)] -#[serde(rename_all = "camelCase", default)] -struct SwcLoaderOptions { - pub config: Option, - pub source_maps: Option, -} - -impl From for Options { - fn from(value: SwcLoaderJsOptions) -> Self { - let SwcLoaderJsOptions { - source_maps, - source_map, - env, - test, - exclude, - jsc, - module, - minify, - input_source_map, - inline_sources_content, - emit_source_map_columns, - error, - is_module, - schema, - } = value; - let mut source_maps: Option = source_maps; - if source_maps.is_none() && source_map.is_some() { - source_maps = source_map - } - if let Some(SourceMapsConfig::Str(str)) = &source_maps { - if str == SOURCE_MAP_INLINE { - source_maps = Some(SourceMapsConfig::Bool(true)) - } - } - Options { - config: Config { - env, - test, - exclude, - jsc, - module, - minify, - input_source_map, - source_maps, - inline_sources_content, - emit_source_map_columns, - error, - is_module, - schema, - }, - ..Default::default() - } - } -} +use options::SwcCompilerOptionsWithAdditional; +pub use options::SwcLoaderJsOptions; #[derive(Debug)] pub struct SwcLoader { - options: Options, identifier: Identifier, + options_with_additional: SwcCompilerOptionsWithAdditional, } impl SwcLoader { - /// Panics: - /// Panics if `identifier` passed in is not starting with `builtin:swc-loader`. - pub fn new(options: SwcLoaderJsOptions, identifier: Option) -> Self { - // TODO: should stringify loader options to identifier - Self::validate_identifier(&identifier); + pub fn new(options: SwcLoaderJsOptions) -> Self { Self { - options: Options::from(options), - identifier: identifier.unwrap_or(SWC_LOADER_IDENTIFIER.into()), + identifier: SWC_LOADER_IDENTIFIER.into(), + options_with_additional: options.into(), } } - fn validate_identifier(identifier: &Option) { - if let Some(i) = identifier { - assert!(i.starts_with(SWC_LOADER_IDENTIFIER)); - } + /// Panics: + /// Panics if `identifier` passed in is not starting with `builtin:swc-loader`. + pub fn with_identifier(mut self, identifier: Identifier) -> Self { + assert!(identifier.starts_with(SWC_LOADER_IDENTIFIER)); + self.identifier = identifier; + self } } @@ -150,18 +52,19 @@ pub const SWC_LOADER_IDENTIFIER: &str = "builtin:swc-loader"; impl Loader for SwcLoader { async fn run(&self, loader_context: &mut LoaderContext<'_, LoaderRunnerContext>) -> Result<()> { let resource_path = loader_context.resource_path.to_path_buf(); - let content = std::mem::take(&mut loader_context.content).expect("content should available"); + let Some(content) = std::mem::take(&mut loader_context.content) else { + return Err(internal_error!("Content should be available")) + }; - let c: Compiler = Compiler::new(Arc::from(swc_core::common::SourceMap::new( + let c = Compiler::new(Arc::from(swc_core::common::SourceMap::new( FilePathMapping::empty(), ))); - let mut errors: Vec = Default::default(); let default_development = matches!(loader_context.context.options.mode, Mode::Development); - let mut options = self.options.clone(); - if options.config.jsc.transform.as_ref().is_some() { + let mut swc_options = self.options_with_additional.swc_options.clone(); + if swc_options.config.jsc.transform.as_ref().is_some() { let mut transform = TransformConfig::default(); transform.react.development = Some(default_development); - options + swc_options .config .jsc .transform @@ -169,12 +72,12 @@ impl Loader for SwcLoader { } if let Some(pre_source_map) = std::mem::take(&mut loader_context.source_map) { if let Ok(source_map) = pre_source_map.to_json() { - options.config.input_source_map = Some(InputSourceMap::Str(source_map)) + swc_options.config.input_source_map = Some(InputSourceMap::Str(source_map)) } } - if options.config.jsc.experimental.plugins.is_some() { - loader_context.diagnostics.push(Diagnostic::warn( + if swc_options.config.jsc.experimental.plugins.is_some() { + loader_context.emit_diagnostic(Diagnostic::warn( SWC_LOADER_IDENTIFIER.to_string(), "Experimental plugins are not currently supported.".to_string(), 0, @@ -183,56 +86,54 @@ impl Loader for SwcLoader { } GLOBALS.set(&Default::default(), || { - match try_with_handler(c.cm.clone(), Default::default(), |handler| { + try_with_handler(c.cm.clone(), Default::default(), |handler| { c.run(|| { + let top_level_mark = Mark::new(); + let unresolved_mark = Mark::new(); + swc_options.top_level_mark = Some(top_level_mark); + swc_options.unresolved_mark = Some(unresolved_mark); + let source = content.try_into_string()?; + let rspack_options = &*loader_context.context.options; + let source_content_hash = self + .options_with_additional + .rspack_experiments + .emotion + .as_ref() + .map(|_| xxh32(source.as_bytes(), 0)); + let fm = c .cm - .new_source_file(FileName::Real(resource_path), content.try_into_string()?); + .new_source_file(FileName::Real(resource_path.clone()), source); let comments = SingleThreadedComments::default(); - let out = match anyhow::Context::context( - c.process_js_with_custom_pass( - fm, - None, - handler, - &options, - comments, - |_a| noop(), - |_a| noop(), - ), - "failed to process js file", - ) { - Ok(out) => Some(out), - Err(e) => { - errors.push(Error::Anyhow { source: e }); - None - } - }; - if let Some(out) = out { - loader_context.content = Some(out.code.into()); - loader_context.source_map = if let Some(map) = out.map { - match SourceMap::from_json(&map).map_err(|e| internal_error!(e.to_string())) { - Ok(map) => Some(map), - Err(e) => { - errors.push(e); - None - } - } - } else { - None - }; - } + let out = c.process_js_with_custom_pass( + fm, + None, + handler, + &swc_options, + comments, + |_| { + transformer::transform( + &resource_path, + rspack_options, + Some(c.comments()), + top_level_mark, + unresolved_mark, + c.cm.clone(), + source_content_hash, + &self.options_with_additional.rspack_experiments, + ) + }, + |_| noop(), + )?; + loader_context.content = Some(out.code.into()); + loader_context.source_map = out.map.map(|m| SourceMap::from_json(&m)).transpose()?; Ok(()) }) - }) { - Ok(_) => {} - Err(e) => errors.push(Error::Anyhow { source: e }), - } - }); - loader_context - .diagnostics - .append(&mut errors_to_diagnostics(errors)); + }) + })?; + Ok(()) } } diff --git a/crates/rspack_loader_swc/src/options.rs b/crates/rspack_loader_swc/src/options.rs new file mode 100644 index 00000000000..54a9d08c0cc --- /dev/null +++ b/crates/rspack_loader_swc/src/options.rs @@ -0,0 +1,147 @@ +use rspack_swc_visitors::{ + EmotionOptions, ImportOptions, RawEmotionOptions, RawImportOptions, RawReactOptions, + RawRelayOptions, ReactOptions, RelayOptions, +}; +use serde::Deserialize; +use swc_config::config_types::BoolConfig; +use swc_core::base::config::{ + Config, ErrorConfig, FileMatcher, InputSourceMap, IsModule, JscConfig, ModuleConfig, Options, + SourceMapsConfig, +}; + +#[derive(Default, Deserialize, Debug)] +#[serde(rename_all = "camelCase", default)] +pub struct RawRspackExperiments { + pub relay: Option, + pub react: Option, + pub import: Option>, + pub emotion: Option, +} + +#[derive(Default, Debug)] +pub(crate) struct RspackExperiments { + pub(crate) relay: Option, + pub(crate) react: Option, + pub(crate) import: Option>, + pub(crate) emotion: Option, +} + +impl From for RspackExperiments { + fn from(value: RawRspackExperiments) -> Self { + Self { + relay: value.relay.map(|v| v.into()), + react: value.react.map(|v| v.into()), + import: value + .import + .map(|i| i.into_iter().map(|v| v.into()).collect()), + emotion: value.emotion, + } + } +} + +#[derive(Debug, Default, Deserialize)] +#[serde(rename_all = "camelCase", default)] +pub struct SwcLoaderJsOptions { + #[serde(default)] + pub source_maps: Option, + + pub source_map: Option, + #[serde(default)] + pub env: Option, + + #[serde(default)] + pub test: Option, + + #[serde(default)] + pub exclude: Option, + + #[serde(default)] + pub jsc: JscConfig, + + #[serde(default)] + pub module: Option, + + #[serde(default)] + pub minify: BoolConfig, + + #[serde(default)] + pub input_source_map: Option, + + #[serde(default)] + pub inline_sources_content: BoolConfig, + + #[serde(default)] + pub emit_source_map_columns: BoolConfig, + + #[serde(default)] + pub error: ErrorConfig, + + #[serde(default)] + pub is_module: Option, + + #[serde(rename = "$schema")] + pub schema: Option, + + #[serde(default)] + pub rspack_experiments: Option, +} + +#[derive(Debug)] +pub(crate) struct SwcCompilerOptionsWithAdditional { + pub(crate) swc_options: Options, + pub(crate) rspack_experiments: RspackExperiments, +} + +const SOURCE_MAP_INLINE: &str = "inline"; + +impl From for SwcCompilerOptionsWithAdditional { + fn from(value: SwcLoaderJsOptions) -> Self { + let SwcLoaderJsOptions { + source_maps, + source_map, + env, + test, + exclude, + jsc, + module, + minify, + input_source_map, + inline_sources_content, + emit_source_map_columns, + error, + is_module, + schema, + rspack_experiments, + } = value; + let mut source_maps: Option = source_maps; + if source_maps.is_none() && source_map.is_some() { + source_maps = source_map + } + if let Some(SourceMapsConfig::Str(str)) = &source_maps { + if str == SOURCE_MAP_INLINE { + source_maps = Some(SourceMapsConfig::Bool(true)) + } + } + SwcCompilerOptionsWithAdditional { + swc_options: Options { + config: Config { + env, + test, + exclude, + jsc, + module, + minify, + input_source_map, + source_maps, + inline_sources_content, + emit_source_map_columns, + error, + is_module, + schema, + }, + ..Default::default() + }, + rspack_experiments: rspack_experiments.unwrap_or_default().into(), + } + } +} diff --git a/crates/rspack_loader_swc/src/transformer.rs b/crates/rspack_loader_swc/src/transformer.rs new file mode 100644 index 00000000000..1e7048740a2 --- /dev/null +++ b/crates/rspack_loader_swc/src/transformer.rs @@ -0,0 +1,79 @@ +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use either::Either; +use rspack_core::CompilerOptions; +use swc_core::common::{chain, comments::Comments, Mark, SourceMap}; +use swc_core::ecma::{ + transforms::base::pass::{noop, Optional}, + visit::Fold, +}; + +use crate::options::RspackExperiments; + +macro_rules! either { + ($config:expr, $f:expr) => { + if let Some(config) = &$config { + Either::Left($f(config)) + } else { + Either::Right(noop()) + } + }; + ($config:expr, $f:expr, $enabled:expr) => { + if $enabled { + either!($config, $f) + } else { + Either::Right(noop()) + } + }; +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn transform<'a>( + resource_path: &'a Path, + rspack_options: &'a CompilerOptions, + comments: Option<&'a dyn Comments>, + top_level_mark: Mark, + unresolved_mark: Mark, + cm: Arc, + content_hash: Option, + rspack_experiments: &'a RspackExperiments, +) -> impl Fold + 'a { + use rspack_swc_visitors::EmotionOptions; + + chain!( + either!(rspack_experiments.react, |options| { + rspack_swc_visitors::react(top_level_mark, comments, &cm, options, unresolved_mark) + }), + Optional::new( + rspack_swc_visitors::fold_react_refresh(unresolved_mark), + rspack_experiments + .react + .as_ref() + .and_then(|v| v.refresh) + .unwrap_or_default() + ), + either!(rspack_experiments.emotion, |options: &EmotionOptions| { + // SAFETY: Source content hash should always available if emotion is turned on. + let content_hash = content_hash.expect("Content hash should be available"); + rspack_swc_visitors::emotion( + options.clone(), + resource_path, + content_hash, + cm.clone(), + comments, + ) + }), + either!(rspack_experiments.relay, |options| { + rspack_swc_visitors::relay( + options, + resource_path, + PathBuf::from(AsRef::::as_ref(&rspack_options.context)), + unresolved_mark, + ) + }), + either!(rspack_experiments.import, |options| { + rspack_swc_visitors::import(options) + }), + ) +} diff --git a/crates/rspack_loader_swc/tests/fixtures.rs b/crates/rspack_loader_swc/tests/fixtures.rs index c2ca3dc687f..de50e0c32d2 100644 --- a/crates/rspack_loader_swc/tests/fixtures.rs +++ b/crates/rspack_loader_swc/tests/fixtures.rs @@ -27,7 +27,7 @@ async fn loader_test(actual: impl AsRef, expected: impl AsRef) { json!(null), )]); let (result, _) = run_loaders( - &[Arc::new(SwcLoader::new(options, None)) as Arc>], + &[Arc::new(SwcLoader::new(options)) as Arc>], &ResourceData::new(actual_path.to_string_lossy().to_string(), actual_path), &[], CompilerContext { diff --git a/crates/rspack_plugin_banner/Cargo.toml b/crates/rspack_plugin_banner/Cargo.toml index bf8231567a7..f5361d5d0b7 100644 --- a/crates/rspack_plugin_banner/Cargo.toml +++ b/crates/rspack_plugin_banner/Cargo.toml @@ -7,13 +7,12 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dev-dependencies] -rspack_testing = { path = "../rspack_testing" } -testing_macros = { workspace = true } - [dependencies] async-recursion = { workspace = true } async-trait = { workspace = true } +futures = { workspace = true } +once_cell = { workspace = true } +regex = { workspace = true } rspack_core = { path = "../rspack_core" } rspack_error = { path = "../rspack_error" } rspack_regex = { path = "../rspack_regex" } diff --git a/crates/rspack_plugin_banner/src/lib.rs b/crates/rspack_plugin_banner/src/lib.rs index 79bf49ae786..9a252480e92 100644 --- a/crates/rspack_plugin_banner/src/lib.rs +++ b/crates/rspack_plugin_banner/src/lib.rs @@ -4,19 +4,24 @@ use std::fmt::{self, Debug}; use async_recursion::async_recursion; use async_trait::async_trait; +use futures::future::BoxFuture; +use once_cell::sync::Lazy; +use regex::Regex; use rspack_core::{ rspack_sources::{BoxSource, ConcatSource, RawSource, SourceExt}, - to_comment, Logger, Plugin, + to_comment, Chunk, Filename, Logger, PathData, Plugin, }; use rspack_error::Result; use rspack_regex::RspackRegex; use rspack_util::try_any; +#[derive(Debug)] pub enum BannerCondition { String(String), Regexp(RspackRegex), } +#[derive(Debug)] pub enum BannerConditions { String(String), Regexp(RspackRegex), @@ -46,52 +51,41 @@ impl BannerConditions { #[derive(Debug)] pub struct BannerConfig { - /** - * Specifies the banner. - */ - pub banner: String, - /** - * If true, the banner will only be added to the entry chunks. - */ + // Specifies the banner. + pub banner: BannerContent, + // If true, the banner will only be added to the entry chunks. pub entry_only: Option, - /** - * If true, banner will be placed at the end of the output. - */ + // If true, banner will be placed at the end of the output. pub footer: Option, - /** - * If true, banner will not be wrapped in a comment. - */ + // If true, banner will not be wrapped in a comment. pub raw: Option, - - /** - * Include all modules that pass test assertion. - */ + // Include all modules that pass test assertion. pub test: Option, - /** - * Include all modules matching any of these conditions. - */ + // Include all modules matching any of these conditions. pub include: Option, - /** - * Exclude all modules matching any of these conditions. - */ + // Exclude all modules matching any of these conditions. pub exclude: Option, } -impl fmt::Debug for BannerCondition { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::String(i) => i.fmt(f), - Self::Regexp(i) => i.fmt(f), - } - } +pub struct BannerContentFnCtx<'a> { + pub hash: &'a str, + pub chunk: &'a Chunk, + pub filename: &'a str, } -impl fmt::Debug for BannerConditions { +pub type BannerContentFn = + Box Fn(BannerContentFnCtx<'a>) -> BoxFuture<'a, Result> + Sync + Send>; + +pub enum BannerContent { + String(String), + Fn(BannerContentFn), +} + +impl fmt::Debug for BannerContent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::String(i) => i.fmt(f), - Self::Regexp(i) => i.fmt(f), - Self::Array(i) => i.fmt(f), + Self::String(arg0) => f.debug_tuple("String").field(arg0).finish(), + Self::Fn(_) => f.debug_tuple("Fn").finish(), } } } @@ -116,18 +110,21 @@ async fn match_object(obj: &BannerConfig, str: &str) -> Result { Ok(true) } +static TRIALING_WHITESPACE: Lazy = + Lazy::new(|| Regex::new(r"\s+\n").expect("invalid regexp")); + fn wrap_comment(str: &str) -> String { if !str.contains('\n') { return to_comment(str); } - let binding = str - .replace("* /", "*/") + let result = str + .replace("*/", "* /") .split('\n') .collect::>() - .join("\n * ") - .replace(|c: char| c.is_whitespace() && c != '\n', " "); - let result = binding.trim_end(); + .join("\n * "); + let result = TRIALING_WHITESPACE.replace_all(&result, "\n"); + let result = result.trim_end(); format!("/*!\n * {}\n */", result) } @@ -135,18 +132,19 @@ fn wrap_comment(str: &str) -> String { #[derive(Debug)] pub struct BannerPlugin { config: BannerConfig, - comment: String, } impl BannerPlugin { pub fn new(config: BannerConfig) -> Self { - let comment = if let Some(raw) = config.raw && raw { - config.banner.clone() - } else { - wrap_comment(&config.banner) - }; + Self { config } + } - Self { config, comment } + fn wrap_comment(&self, value: &str) -> String { + if let Some(true) = self.config.raw { + value.to_owned() + } else { + wrap_comment(value) + } } fn update_source(&self, comment: String, old: BoxSource, footer: Option) -> BoxSource { @@ -182,7 +180,7 @@ impl Plugin for BannerPlugin { let compilation = args.compilation; let logger = compilation.get_logger(self.name()); let start = logger.time("add banner"); - let mut chunk_files = vec![]; + let mut updates = vec![]; // filter file for chunk in compilation.chunk_by_ukey.values() { @@ -198,19 +196,41 @@ impl Plugin for BannerPlugin { if !is_match { continue; } - chunk_files.push(file.clone()); + // add comment to the matched file + let hash = compilation + .hash + .as_ref() + .expect("should have compilation.hash in process_assets hook") + .encoded() + .to_owned(); + // todo: support placeholder, such as [fullhash]、[chunkhash] + let banner = match &self.config.banner { + BannerContent::String(content) => self.wrap_comment(content), + BannerContent::Fn(func) => { + let res = func(BannerContentFnCtx { + hash: &hash, + chunk, + filename: file, + }) + .await?; + self.wrap_comment(&res) + } + }; + let comment = compilation.get_path( + &Filename::from(banner), + PathData::default().chunk(chunk).hash(&hash).filename(file), + ); + updates.push((file.clone(), comment)); } } - // add comment to the matched file - for file in chunk_files { - // todo: support placeholder, such as [fullhash]、[chunkhash] - let comment = self.comment.to_owned(); + for (file, comment) in updates { let _res = compilation.update_asset(file.as_str(), |old, info| { let new = self.update_source(comment, old, self.config.footer); Ok((new, info)) }); } + logger.time_end(start); Ok(()) diff --git a/crates/rspack_plugin_css/tests/fixtures.rs b/crates/rspack_plugin_css/tests/fixtures.rs index cc20d037d7b..e1a59ca4335 100644 --- a/crates/rspack_plugin_css/tests/fixtures.rs +++ b/crates/rspack_plugin_css/tests/fixtures.rs @@ -1,13 +1,18 @@ use std::path::PathBuf; -use rspack_testing::{fixture, test_fixture_css}; +use rspack_testing::{fixture, test_fixture_css, test_fixture_css_modules}; #[fixture("tests/fixtures/webpack/*")] fn webpack_css(fixture_path: PathBuf) { test_fixture_css(&fixture_path); } -#[fixture("tests/fixtures/custom/*")] +#[fixture("tests/fixtures/custom/*", exclude("tests/fixtures/custom/modules-*"))] fn custom(fixture_path: PathBuf) { test_fixture_css(&fixture_path); } + +#[fixture("tests/fixtures/custom/modules-*")] +fn custom_modules(fixture_path: PathBuf) { + test_fixture_css_modules(&fixture_path); +} diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/b.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/b.module.css new file mode 100644 index 00000000000..270e110cd9c --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/b.module.css @@ -0,0 +1,6 @@ +.b-1 { + color: red; +} +.b { + composes: b-1; +} \ No newline at end of file diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/d.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/d.module.css new file mode 100644 index 00000000000..8b0b000bab6 --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/d.module.css @@ -0,0 +1,7 @@ +.d-1 { + color: green; +} + +.d { + composes: d-1; +} diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/e.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/e.module.css new file mode 100644 index 00000000000..44640418b1a --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/e.module.css @@ -0,0 +1,7 @@ +.e-1 { + color: blue; +} + +.e { + composes: e-1; +} diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/f.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/f.module.css new file mode 100644 index 00000000000..35793822fed --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/f.module.css @@ -0,0 +1,6 @@ +.f-1 { + color: black; +} +.f { + composes: f-1; +} diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/index.js b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/index.js new file mode 100644 index 00000000000..d31bab07a9d --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/index.js @@ -0,0 +1 @@ +import './style.module.css' diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/snapshot/output.snap new file mode 100644 index 00000000000..fc4f198b438 --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/snapshot/output.snap @@ -0,0 +1,70 @@ +--- +source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 +--- +```css title=main.css +._d-module__d-1---__c54e45bb853e1f4b-c54 { + color: green; +} +._d-module__d---___34b192ed7623f91e-_34 {} +._f-module__f-1---___85bca5b4a297a364-_85 { + color: black; +} +._f-module__f---___141af47168767e48-_14 {} +._b-module__b-1---___1d9724eea6aa131c-_1d { + color: red; +} +._b-module__b---__c156016816e95195-c15 {} +._style-module__chain2---___980d4cb026fb8411-_98 { + background: blue; +} +._style-module__chain1---___3a2c0a3269e6f63c-_3a { + background: green; +} +._style-module__root-class---__aae4264cfe73c127-aae { + background: red; +} + +``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _style_module_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.module.css */"./style.module.css"); + +}, +"./b.module.css": function (module, exports, __webpack_require__) { +module.exports = { + "b": "_b-module__b---__c156016816e95195-c15" + " " + "_b-module__b-1---___1d9724eea6aa131c-_1d", + "b-1": "_b-module__b-1---___1d9724eea6aa131c-_1d", +}; +}, +"./d.module.css": function (module, exports, __webpack_require__) { +module.exports = { + "d": "_d-module__d---___34b192ed7623f91e-_34" + " " + "_d-module__d-1---__c54e45bb853e1f4b-c54", + "d-1": "_d-module__d-1---__c54e45bb853e1f4b-c54", +}; +}, +"./f.module.css": function (module, exports, __webpack_require__) { +module.exports = { + "f": "_f-module__f---___141af47168767e48-_14" + " " + "_f-module__f-1---___85bca5b4a297a364-_85", + "f-1": "_f-module__f-1---___85bca5b4a297a364-_85", +}; +}, +"./style.module.css": function (module, exports, __webpack_require__) { +module.exports = { + "chain1": "_style-module__chain1---___3a2c0a3269e6f63c-_3a" + " " + "_style-module__chain2---___980d4cb026fb8411-_98" + " " + "c" + " " + __webpack_require__("./d.module.css")["d"] + " " + "e" + " " + __webpack_require__("./f.module.css")["f"], + "chain2": "_style-module__chain2---___980d4cb026fb8411-_98" + " " + "e" + " " + __webpack_require__("./f.module.css")["f"], + "root-class": "_style-module__root-class---__aae4264cfe73c127-aae" + " " + "_style-module__chain1---___3a2c0a3269e6f63c-_3a" + " " + "a" + " " + __webpack_require__("./b.module.css")["b"] + " " + "_style-module__chain2---___980d4cb026fb8411-_98" + " " + "c" + " " + __webpack_require__("./d.module.css")["d"] + " " + "e" + " " + __webpack_require__("./f.module.css")["f"], +}; +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/style.module.css b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/style.module.css new file mode 100644 index 00000000000..67f65eb50d0 --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/style.module.css @@ -0,0 +1,19 @@ +.chain2 { + composes: e from global; + composes: f from "./f.module.css"; + background: blue; +} + +.chain1 { + composes: chain2; + composes: c from global; + composes: d from "./d.module.css"; + background: green; +} + +.root-class { + composes: chain1; + composes: a from global; + composes: b from "./b.module.css"; + background: red; +} diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/test.config.json b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/test.config.json new file mode 100644 index 00000000000..0071292cce6 --- /dev/null +++ b/crates/rspack_plugin_css/tests/fixtures/custom/modules-composes/test.config.json @@ -0,0 +1,20 @@ +{ + "builtins": { + "css": { + "modules": { + "localIdentName": "[path]_[name]_[path]_[local]--/__[hash:42]<[hash:3]" + } + } + }, + "module": { + "rules": [ + { + "test": { + "type": "regexp", + "matcher": "\\.module\\.css$" + }, + "type": "css/module" + } + ] + } +} \ No newline at end of file diff --git a/crates/rspack_plugin_css/tests/fixtures/custom/modules-ident-name/snapshot/output.snap b/crates/rspack_plugin_css/tests/fixtures/custom/modules-ident-name/snapshot/output.snap index 46648882420..7b4070971fc 100644 --- a/crates/rspack_plugin_css/tests/fixtures/custom/modules-ident-name/snapshot/output.snap +++ b/crates/rspack_plugin_css/tests/fixtures/custom/modules-ident-name/snapshot/output.snap @@ -1,5 +1,6 @@ --- source: crates/rspack_testing/src/run_fixture.rs +assertion_line: 101 --- ```css title=main.css ._style-module__foo---___6655ebe837449348-_66 { @@ -7,3 +8,26 @@ source: crates/rspack_testing/src/run_fixture.rs } ``` + +```js title=main.js +(self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { +"./index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { +'use strict'; +__webpack_require__.r(__webpack_exports__); +/* harmony import */var _style_module_css__WEBPACK_IMPORTED_MODULE_0_ = __webpack_require__(/* ./style.module.css */"./style.module.css"); + +console.log(_style_module_css__WEBPACK_IMPORTED_MODULE_0_); +}, +"./style.module.css": function (module, exports, __webpack_require__) { +module.exports = { + "foo": "_style-module__foo---___6655ebe837449348-_66", +}; +}, + +},function(__webpack_require__) { +var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId) } +var __webpack_exports__ = (__webpack_exec__("./index.js")); + +} +]); +``` diff --git a/crates/rspack_plugin_javascript/Cargo.toml b/crates/rspack_plugin_javascript/Cargo.toml index 1f12181f0cf..b6bf7c3ede5 100644 --- a/crates/rspack_plugin_javascript/Cargo.toml +++ b/crates/rspack_plugin_javascript/Cargo.toml @@ -24,6 +24,7 @@ rspack_error = { path = "../rspack_error" } rspack_hash = { path = "../rspack_hash" } rspack_identifier = { path = "../rspack_identifier" } rspack_regex = { path = "../rspack_regex" } +rspack_swc_visitors = { path = "../rspack_swc_visitors" } rustc-hash = { workspace = true } serde_json = { workspace = true } sourcemap = "6.2.3" @@ -44,4 +45,4 @@ swc_emotion = { workspace = true } swc_node_comments = { workspace = true } swc_plugin_import = { path = "../swc_plugin_import" } url = "2.4.0" -xxhash-rust = { version = "0.8.6", features = ["xxh32"] } +xxhash-rust = { workspace = true, features = ["xxh32"] } diff --git a/crates/rspack_plugin_javascript/src/ast/stringify.rs b/crates/rspack_plugin_javascript/src/ast/stringify.rs index 6e72c7b49a2..1ff2a75e913 100644 --- a/crates/rspack_plugin_javascript/src/ast/stringify.rs +++ b/crates/rspack_plugin_javascript/src/ast/stringify.rs @@ -77,12 +77,10 @@ pub fn print( } let mut emitter = Emitter { - cfg: codegen::Config { - minify, - target, - ascii_only, - ..Default::default() - }, + cfg: codegen::Config::default() + .with_minify(minify) + .with_target(target) + .with_ascii_only(ascii_only), comments, cm: source_map.clone(), wr, diff --git a/crates/rspack_plugin_javascript/src/dependency/commonjs/require_resolve_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/commonjs/require_resolve_dependency.rs index f8357730715..678d36cacfe 100644 --- a/crates/rspack_plugin_javascript/src/dependency/commonjs/require_resolve_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/commonjs/require_resolve_dependency.rs @@ -1,6 +1,6 @@ use rspack_core::{ module_id, ContextOptions, Dependency, DependencyCategory, DependencyId, DependencyTemplate, - DependencyType, ErrorSpan, ExportsReferencedType, ModuleDependency, ModuleGraph, RuntimeSpec, + DependencyType, ErrorSpan, ExtendedReferencedExport, ModuleDependency, ModuleGraph, RuntimeSpec, TemplateContext, TemplateReplaceSource, }; @@ -82,9 +82,9 @@ impl ModuleDependency for RequireResolveDependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { - ExportsReferencedType::No + _runtime: Option<&RuntimeSpec>, + ) -> Vec { + vec![] } } diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs index 712e94b21f1..0d72b9c4966 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs @@ -1,8 +1,9 @@ use rspack_core::{ - export_from_import, get_exports_type, process_export_info, ConnectionState, Dependency, - DependencyCategory, DependencyId, DependencyTemplate, DependencyType, ErrorSpan, ExportInfo, - ExportsReferencedType, ExportsType, HarmonyExportInitFragment, ModuleDependency, ModuleGraph, - ModuleIdentifier, RuntimeSpec, TemplateContext, TemplateReplaceSource, + create_exports_object_referenced, create_no_exports_referenced, export_from_import, + get_exports_type, process_export_info, ConnectionState, Dependency, DependencyCategory, + DependencyId, DependencyTemplate, DependencyType, ErrorSpan, ExportInfoId, ExportInfoProvided, + ExportsType, ExtendedReferencedExport, HarmonyExportInitFragment, ModuleDependency, ModuleGraph, + ModuleIdentifier, RuntimeSpec, TemplateContext, TemplateReplaceSource, UsageState, }; use rustc_hash::FxHashSet as HashSet; use swc_core::ecma::atoms::JsWord; @@ -10,7 +11,9 @@ use swc_core::ecma::atoms::JsWord; use super::create_resource_identifier_for_esm_dependency; // Create _webpack_require__.d(__webpack_exports__, {}). -// import { a } from 'a'; export { a } +// case1: `import { a } from 'a'; export { a }` +// case2: `export { a } from 'a';` +// TODO case3: `export * from 'a'` #[derive(Debug, Clone)] pub struct HarmonyExportImportedSpecifierDependency { pub id: DependencyId, @@ -18,19 +21,353 @@ pub struct HarmonyExportImportedSpecifierDependency { pub ids: Vec<(JsWord, Option)>, name: Option, resource_identifier: String, + // Because it is shared by multiply HarmonyExportImportedSpecifierDependency, so put it to `BuildInfo` + // pub active_exports: HashSet, + // pub all_star_exports: Option>, + pub other_star_exports: Option>, // look like it is unused } impl HarmonyExportImportedSpecifierDependency { - pub fn new(request: JsWord, ids: Vec<(JsWord, Option)>) -> Self { + pub fn new(request: JsWord, ids: Vec<(JsWord, Option)>, name: Option) -> Self { let resource_identifier = create_resource_identifier_for_esm_dependency(&request); Self { id: DependencyId::new(), - name: None, + name, request, ids, resource_identifier, + other_star_exports: None, } } + + pub fn active_exports<'a>(&self, module_graph: &'a ModuleGraph) -> &'a HashSet { + let build_info = module_graph + .module_graph_module_by_dependency_id(&self.id) + .expect("should have mgm") + .build_info + .as_ref() + .expect("should have build info"); + &build_info.harmony_named_exports + } + + pub fn all_star_exports<'a>(&self, module_graph: &'a ModuleGraph) -> &'a Vec { + let build_info = module_graph + .module_graph_module_by_dependency_id(&self.id) + .expect("should have mgm") + .build_info + .as_ref() + .expect("should have build info"); + &build_info.all_star_exports + } + + // TODO cache get_mode result + #[allow(unused)] + pub fn get_mode( + &self, + name: Option, + ids: &Vec, + module_graph: &ModuleGraph, + id: &DependencyId, + runtime: Option<&RuntimeSpec>, + ) -> ExportMode { + let imported_module_identifier = if let Some(imported_module_identifier) = + module_graph.module_identifier_by_dependency_id(id) + { + imported_module_identifier + } else { + return ExportMode { + kind: ExportModeType::Missing, + ..Default::default() + }; + }; + + let parent_module = module_graph + .parent_module_by_dependency_id(id) + .expect("should have parent module"); + let exports_type = get_exports_type(module_graph, id, &parent_module); + let exports_info = module_graph.get_exports_info(&parent_module); + + // Special handling for reexporting the default export + // from non-namespace modules + if let Some(name) = name.as_ref() && !ids.is_empty() && let Some(id) = ids.get(0) && id == "default" { + match exports_type { + ExportsType::Dynamic => { + return ExportMode { kind: ExportModeType::ReexportDynamicDefault, name: Some(name.clone()), ..Default::default() } + }, + ExportsType::DefaultOnly | ExportsType::DefaultWithNamed => { + let export_info = exports_info.id.get_read_only_export_info(name, module_graph).id; + return ExportMode { kind: ExportModeType::ReexportNamedDefault, name: Some(name.clone()), partial_namespace_export_info: Some(export_info), ..Default::default() } + }, + _ => {} + } + } + + // reexporting with a fixed name + if let Some(name) = name { + let export_info = exports_info + .id + .get_read_only_export_info(&name, module_graph) + .id; + if !ids.is_empty() { + // export { name as name } + match exports_type { + ExportsType::DefaultOnly => { + return ExportMode { + kind: ExportModeType::ReexportUndefined, + name: Some(name), + ..Default::default() + } + } + _ => { + return ExportMode { + kind: ExportModeType::NormalReexport, + items: Some(vec![NormalReexportItem { + name, + ids: ids.to_vec(), + hidden: false, + checked: false, + export_info, + }]), + ..Default::default() + } + } + } + } else { + // export * as name + match exports_type { + ExportsType::DefaultOnly => { + return ExportMode { + kind: ExportModeType::ReexportFakeNamespaceObject, + name: Some(name), + partial_namespace_export_info: Some(export_info), + fake_type: Some(0), + ..Default::default() + } + } + ExportsType::DefaultWithNamed => { + return ExportMode { + kind: ExportModeType::ReexportFakeNamespaceObject, + name: Some(name), + partial_namespace_export_info: Some(export_info), + fake_type: Some(2), + ..Default::default() + } + } + _ => { + return ExportMode { + kind: ExportModeType::ReexportNamespaceObject, + name: Some(name), + partial_namespace_export_info: Some(export_info), + ..Default::default() + } + } + } + } + } + + let StarReexportsInfo { + exports, + checked, + ignored_exports, + hidden, + } = self.get_star_reexports(module_graph, runtime, imported_module_identifier); + + if let Some(exports) = exports { + if exports.is_empty() { + return ExportMode { + kind: ExportModeType::EmptyStar, + hidden, + ..Default::default() + }; + } + + let mut items = exports + .into_iter() + .map(|export_name| NormalReexportItem { + name: export_name.clone(), + ids: vec![export_name.clone()], + hidden: false, + checked: checked.as_ref().map(|c| c.contains(&export_name)).is_some(), + export_info: exports_info + .id + .get_read_only_export_info(&export_name, module_graph) + .id, + }) + .collect::>(); + + if let Some(hidden) = &hidden { + for export_name in hidden.iter() { + items.push(NormalReexportItem { + name: export_name.clone(), + ids: vec![export_name.clone()], + hidden: true, + checked: false, + export_info: exports_info + .id + .get_read_only_export_info(export_name, module_graph) + .id, + }); + } + } + + ExportMode { + kind: ExportModeType::NormalReexport, + items: Some(items), + ..Default::default() + } + } else { + ExportMode { + kind: ExportModeType::DynamicReexport, + ignored: Some(ignored_exports), + hidden, + ..Default::default() + } + } + } + + pub fn get_star_reexports( + &self, + module_graph: &ModuleGraph, + runtime: Option<&RuntimeSpec>, + imported_module_identifier: &ModuleIdentifier, + ) -> StarReexportsInfo { + let imported_exports_info = module_graph.get_exports_info(imported_module_identifier); + let other_export_info = module_graph + .export_info_map + .get(&imported_exports_info.other_exports_info) + .expect("should have export info"); + let no_extra_exports = matches!(other_export_info.provided, Some(ExportInfoProvided::False)); + let no_extra_imports = matches!(other_export_info.get_used(runtime), UsageState::Unused); + let ignored_exports: HashSet = { + let mut e = self.active_exports(module_graph).clone(); + e.insert("default".into()); + e + }; + let mut hidden_exports = self.discover_active_exports_from_other_star_exports(module_graph); + if !no_extra_exports && !no_extra_imports { + if let Some(hidden_exports) = hidden_exports.as_mut() { + for e in ignored_exports.iter() { + hidden_exports.remove(e); + } + } + return StarReexportsInfo { + ignored_exports, + hidden: hidden_exports, + ..Default::default() + }; + } + + let mut exports = HashSet::default(); + let mut checked = HashSet::default(); + let mut hidden = if hidden_exports.is_some() { + Some(HashSet::default()) + } else { + None + }; + + let parent_module = module_graph + .parent_module_by_dependency_id(&self.id) + .expect("should have parent module"); + let exports_info = module_graph.get_exports_info(&parent_module); + if no_extra_imports { + for export_info_id in exports_info.get_ordered_exports() { + let export_info = module_graph + .export_info_map + .get(export_info_id) + .expect("should have export info"); + if ignored_exports.contains(&export_info.name) + || matches!(export_info.get_used(runtime), UsageState::Unused) + { + continue; + } + let imported_export_info = imported_exports_info + .id + .get_read_only_export_info(&export_info.name, module_graph); + if matches!( + imported_export_info.provided, + Some(ExportInfoProvided::False) + ) { + continue; + } + if let Some(hidden) = hidden.as_mut() && hidden_exports.as_ref() + .map(|hidden_exports| hidden_exports.contains(&export_info.name)) + .is_some() + { + hidden.insert(export_info.name.clone()); + continue; + } + exports.insert(export_info.name.clone()); + if matches!( + imported_export_info.provided, + Some(ExportInfoProvided::True) + ) { + continue; + } + checked.insert(export_info.name.clone()); + } + } else if no_extra_exports { + for import_export_info_id in imported_exports_info.get_ordered_exports() { + let import_export_info = module_graph + .export_info_map + .get(import_export_info_id) + .expect("should have export info"); + if ignored_exports.contains(&import_export_info.name) + || matches!(import_export_info.provided, Some(ExportInfoProvided::False)) + { + continue; + } + let export_info = exports_info + .id + .get_read_only_export_info(&import_export_info.name, module_graph); + if matches!(export_info.get_used(runtime), UsageState::Unused) { + continue; + } + if let Some(hidden) = hidden.as_mut() && hidden_exports.as_ref() + .map(|hidden_exports| hidden_exports.contains(&import_export_info.name)) + .is_some() + { + hidden.insert(import_export_info.name.clone()); + continue; + } + exports.insert(import_export_info.name.clone()); + if matches!(import_export_info.provided, Some(ExportInfoProvided::True)) { + continue; + } + checked.insert(import_export_info.name.clone()); + } + } + + StarReexportsInfo { + ignored_exports, + exports: Some(exports), + checked: Some(checked), + hidden, + } + } + + pub fn discover_active_exports_from_other_star_exports( + &self, + module_graph: &ModuleGraph, + ) -> Option> { + if let Some(other_star_exports) = &self.other_star_exports { + if other_star_exports.is_empty() { + return None; + } + } + + let all_star_exports = self.all_star_exports(module_graph); + if !all_star_exports.is_empty() { + let names = determine_export_assignments(module_graph, all_star_exports.clone(), None); + return Some(names); + } + + if let Some(other_star_exports) = &self.other_star_exports { + let names = + determine_export_assignments(module_graph, other_star_exports.clone(), Some(self.id)); + return Some(names); + } + None + } } impl DependencyTemplate for HarmonyExportImportedSpecifierDependency { @@ -129,21 +466,22 @@ impl ModuleDependency for HarmonyExportImportedSpecifierDependency { fn get_referenced_exports( &self, module_graph: &ModuleGraph, - runtime: &RuntimeSpec, - ) -> ExportsReferencedType { - let mode = get_mode( + runtime: Option<&RuntimeSpec>, + ) -> Vec { + let mode = self.get_mode( self.name.clone(), &self.ids.iter().map(|id| id.0.clone()).collect::>(), module_graph, &self.id, + runtime, ); match mode.kind { ExportModeType::Missing | ExportModeType::Unused | ExportModeType::EmptyStar - | ExportModeType::ReexportUndefined => ExportsReferencedType::No, + | ExportModeType::ReexportUndefined => create_no_exports_referenced(), ExportModeType::ReexportDynamicDefault | ExportModeType::DynamicReexport => { - ExportsReferencedType::Object + create_exports_object_referenced() } ExportModeType::ReexportNamedDefault | ExportModeType::ReexportNamespaceObject @@ -155,13 +493,16 @@ impl ModuleDependency for HarmonyExportImportedSpecifierDependency { runtime, &mut referenced_exports, vec![], - Some(partial_namespace_export_info), + Some(*partial_namespace_export_info), mode.kind == ExportModeType::ReexportFakeNamespaceObject, &mut Default::default(), ); - referenced_exports.into() + referenced_exports + .into_iter() + .map(ExtendedReferencedExport::Array) + .collect::>() } else { - ExportsReferencedType::Object + create_exports_object_referenced() } } ExportModeType::NormalReexport => { @@ -182,7 +523,10 @@ impl ModuleDependency for HarmonyExportImportedSpecifierDependency { ); } } - referenced_exports.into() + referenced_exports + .into_iter() + .map(ExtendedReferencedExport::Array) + .collect::>() } ExportModeType::Unset => { unreachable!("should not export mode unset"); @@ -209,108 +553,61 @@ pub enum ExportModeType { } #[derive(Debug)] -pub struct NormalReexportItem<'a> { +pub struct NormalReexportItem { pub name: JsWord, pub ids: Vec, pub hidden: bool, pub checked: bool, - pub export_info: &'a ExportInfo, + pub export_info: ExportInfoId, } #[derive(Debug, Default)] -pub struct ExportMode<'a> { +pub struct ExportMode { pub kind: ExportModeType, - pub items: Option>>, + pub items: Option>, pub name: Option, pub fake_type: Option, - pub partial_namespace_export_info: Option, + pub partial_namespace_export_info: Option, + pub ignored: Option>, + pub hidden: Option>, } -// TODO cache get_mode result -#[allow(unused)] -pub fn get_mode<'a>( - name: Option, - ids: &Vec, - module_graph: &'a ModuleGraph, - id: &DependencyId, -) -> ExportMode<'a> { - let parent_module = module_graph - .parent_module_by_dependency_id(id) - .expect("should have parent module"); - let exports_type = get_exports_type(module_graph, id, &parent_module); - let exports_info = module_graph.get_exports_info(&parent_module); - if let Some(name) = name.as_ref() && !ids.is_empty() && let Some(id) = ids.get(0) && id == "default" { - match exports_type { - ExportsType::Dynamic => { - return ExportMode { kind: ExportModeType::ReexportDynamicDefault, name: Some(name.clone()), ..Default::default() } - }, - ExportsType::DefaultOnly | ExportsType::DefaultWithNamed => { - return ExportMode { kind: ExportModeType::ReexportNamedDefault, name: Some(name.clone()), ..Default::default() } - }, - _ => {} - } +#[derive(Debug, Default)] +pub struct StarReexportsInfo { + exports: Option>, + checked: Option>, + ignored_exports: HashSet, + hidden: Option>, +} + +fn determine_export_assignments( + module_graph: &ModuleGraph, + mut dependencies: Vec, + additional_dependency: Option, +) -> HashSet { + if let Some(additional_dependency) = additional_dependency { + dependencies.push(additional_dependency); } - if let Some(name) = name { - let export_info = exports_info - .id - .get_read_only_export_info(&name, module_graph); - if !ids.is_empty() { - match exports_type { - ExportsType::DefaultOnly => { - return ExportMode { - kind: ExportModeType::ReexportUndefined, - name: Some(name), - ..Default::default() - } - } - _ => { - return ExportMode { - kind: ExportModeType::NormalReexport, - items: Some(vec![NormalReexportItem { - name, - ids: ids.to_vec(), - hidden: false, - checked: false, - export_info, - }]), - ..Default::default() - } - } - } - } else { - match exports_type { - ExportsType::DefaultOnly => { - return ExportMode { - kind: ExportModeType::ReexportFakeNamespaceObject, - name: Some(name), - fake_type: Some(0), - ..Default::default() - } - } - ExportsType::DefaultWithNamed => { - return ExportMode { - kind: ExportModeType::ReexportFakeNamespaceObject, - name: Some(name), - fake_type: Some(2), - ..Default::default() - } - } - _ => { - return ExportMode { - kind: ExportModeType::ReexportNamespaceObject, - name: Some(name), - ..Default::default() - } + let mut names = HashSet::default(); + + for dependency in dependencies.iter() { + if let Some(module_identifier) = module_graph.module_identifier_by_dependency_id(dependency) { + let exports_info = module_graph.get_exports_info(module_identifier); + for export_info_id in exports_info.exports.values() { + let export_info = module_graph + .export_info_map + .get(export_info_id) + .expect("should have export info"); + if matches!(export_info.provided, Some(ExportInfoProvided::True)) + && &export_info.name != "default" + && !names.contains(&export_info.name) + { + names.insert(export_info.name.clone()); } } } } - // todo star reexporting - ExportMode { - kind: ExportModeType::NormalReexport, - items: Some(vec![]), - ..Default::default() - } + names } diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_specifier_dependency.rs index c22c6e43798..fc44da3b8e2 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_specifier_dependency.rs @@ -9,14 +9,16 @@ use swc_core::ecma::atoms::JsWord; #[derive(Debug, Clone)] pub struct HarmonyExportSpecifierDependency { id: DependencyId, - export: (JsWord, JsWord), + name: JsWord, + value: JsWord, // id } impl HarmonyExportSpecifierDependency { - pub fn new(export: (JsWord, JsWord)) -> Self { + pub fn new(name: JsWord, value: JsWord) -> Self { Self { id: DependencyId::new(), - export, + name, + value, } } } @@ -36,7 +38,7 @@ impl Dependency for HarmonyExportSpecifierDependency { fn get_exports(&self) -> Option { Some(ExportsSpec { - exports: ExportsOfExportsSpec::Array(vec![ExportNameOrSpec::String(self.export.0.clone())]), + exports: ExportsOfExportsSpec::Array(vec![ExportNameOrSpec::String(self.name.clone())]), priority: Some(1), can_mangle: None, terminal_binding: Some(true), @@ -68,14 +70,15 @@ impl DependencyTemplate for HarmonyExportSpecifierDependency { .module_graph .get_exports_info(&module.identifier()) .get_used_exports() - .contains(&self.export.0) + .contains(&self.name) } else { true }; if used { - init_fragments.push(Box::new(HarmonyExportInitFragment::new( - self.export.clone(), - ))); + init_fragments.push(Box::new(HarmonyExportInitFragment::new(( + self.name.clone(), + self.value.clone(), + )))); } } } diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_dependency.rs index 8e1a9abd72a..bf8e91fd756 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_dependency.rs @@ -2,10 +2,11 @@ use rspack_core::tree_shaking::symbol::{self, IndirectTopLevelSymbol}; use rspack_core::tree_shaking::visitor::SymbolRef; use rspack_core::{ import_statement, ConnectionState, Dependency, DependencyCategory, DependencyCondition, - DependencyId, DependencyTemplate, DependencyType, ErrorSpan, InitFragmentStage, ModuleDependency, - ModuleIdentifier, NormalInitFragment, RuntimeGlobals, TemplateContext, TemplateReplaceSource, + DependencyId, DependencyTemplate, DependencyType, ErrorSpan, ExtendedReferencedExport, + InitFragmentStage, ModuleDependency, ModuleIdentifier, NormalInitFragment, RuntimeGlobals, + TemplateContext, TemplateReplaceSource, }; -use rspack_core::{ExportsReferencedType, ModuleGraph, RuntimeSpec}; +use rspack_core::{ModuleGraph, RuntimeSpec}; use rustc_hash::FxHashSet as HashSet; use swc_core::ecma::atoms::JsWord; @@ -234,9 +235,9 @@ impl ModuleDependency for HarmonyImportDependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { - ExportsReferencedType::No + _runtime: Option<&RuntimeSpec>, + ) -> Vec { + vec![] } // It's from HarmonyImportSideEffectDependency. diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs index 28648a75814..d9d1ca26ae4 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs @@ -1,10 +1,11 @@ use rspack_core::{ - export_from_import, get_dependency_used_by_exports_condition, get_exports_type, + create_exports_object_referenced, create_no_exports_referenced, export_from_import, + get_dependency_used_by_exports_condition, get_exports_type, tree_shaking::symbol::DEFAULT_JS_WORD, Compilation, ConnectionState, Dependency, DependencyCategory, DependencyCondition, DependencyId, DependencyTemplate, DependencyType, - ErrorSpan, ExportsReferencedType, ExportsType, ModuleDependency, ModuleGraph, ModuleGraphModule, - ModuleIdentifier, ReferencedExport, RuntimeSpec, TemplateContext, TemplateReplaceSource, - UsedByExports, + ErrorSpan, ExportsType, ExtendedReferencedExport, ModuleDependency, ModuleGraph, + ModuleGraphModule, ModuleIdentifier, ReferencedExport, RuntimeSpec, TemplateContext, + TemplateReplaceSource, UsedByExports, }; use rustc_hash::FxHashSet as HashSet; use swc_core::ecma::atoms::JsWord; @@ -39,6 +40,7 @@ impl HarmonyImportSpecifierDependency { call: bool, direct_import: bool, specifier: Specifier, + referenced_properties_in_destructuring: Option>, ) -> Self { let resource_identifier = create_resource_identifier_for_esm_dependency(&request); Self { @@ -53,7 +55,7 @@ impl HarmonyImportSpecifierDependency { specifier, used_by_exports: UsedByExports::default(), namespace_object_as_context: false, - referenced_properties_in_destructuring: None, + referenced_properties_in_destructuring, resource_identifier, } } @@ -92,7 +94,7 @@ impl HarmonyImportSpecifierDependency { pub fn get_referenced_exports_in_destructuring( &self, ids: Option<&Vec>, - ) -> ExportsReferencedType { + ) -> Vec { if let Some(referenced_properties) = &self.referenced_properties_in_destructuring { referenced_properties .iter() @@ -105,12 +107,12 @@ impl HarmonyImportSpecifierDependency { ReferencedExport::new(vec![prop.clone()], false) } }) + .map(ExtendedReferencedExport::Export) .collect::>() - .into() } else if let Some(v) = ids { - vec![ReferencedExport::new(v.clone(), true)].into() + vec![ReferencedExport::new(v.clone(), true).into()] } else { - ExportsReferencedType::Object + create_exports_object_referenced() } } } @@ -213,8 +215,9 @@ impl ModuleDependency for HarmonyImportSpecifierDependency { fn get_referenced_exports( &self, module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { + _runtime: Option<&RuntimeSpec>, + ) -> Vec { + // namespace import if self.ids.is_empty() { return self.get_referenced_exports_in_destructuring(None); } @@ -233,7 +236,7 @@ impl ModuleDependency for HarmonyImportSpecifierDependency { namespace_object_as_context = true; } ExportsType::Dynamic => { - return ExportsReferencedType::No; + return create_no_exports_referenced(); } _ => {} } @@ -241,7 +244,7 @@ impl ModuleDependency for HarmonyImportSpecifierDependency { if self.call && !self.direct_import && (namespace_object_as_context || ids.len() > 1) { if ids.len() == 1 { - return ExportsReferencedType::Object; + return create_exports_object_referenced(); } // remove last one ids.shrink_to(ids.len() - 1); diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs index 21e80410cdc..ff0f4d00584 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs @@ -1,7 +1,7 @@ use rspack_core::{ module_namespace_promise, ChunkGroupOptions, ChunkGroupOptionsKindRef, Dependency, DependencyCategory, DependencyId, DependencyTemplate, DependencyType, ErrorSpan, - ExportsReferencedType, ModuleDependency, ModuleGraph, ReferencedExport, RuntimeSpec, + ExtendedReferencedExport, ModuleDependency, ModuleGraph, ReferencedExport, RuntimeSpec, TemplateContext, TemplateReplaceSource, }; use swc_core::ecma::atoms::JsWord; @@ -78,12 +78,12 @@ impl ModuleDependency for ImportDependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { + _runtime: Option<&RuntimeSpec>, + ) -> Vec { if let Some(referenced_exports) = &self.referenced_exports { - vec![ReferencedExport::new(referenced_exports.clone(), false)].into() + vec![ReferencedExport::new(referenced_exports.clone(), false).into()] } else { - ExportsReferencedType::Object + vec![ExtendedReferencedExport::Array(vec![])] } } } diff --git a/crates/rspack_plugin_javascript/src/dependency/worker/mod.rs b/crates/rspack_plugin_javascript/src/dependency/worker/mod.rs index df00122736c..7c81390a5bc 100644 --- a/crates/rspack_plugin_javascript/src/dependency/worker/mod.rs +++ b/crates/rspack_plugin_javascript/src/dependency/worker/mod.rs @@ -1,6 +1,6 @@ use rspack_core::{ ChunkGroupOptionsKindRef, Dependency, DependencyCategory, DependencyId, DependencyTemplate, - DependencyType, EntryOptions, ErrorSpan, ExportsReferencedType, ModuleDependency, ModuleGraph, + DependencyType, EntryOptions, ErrorSpan, ExtendedReferencedExport, ModuleDependency, ModuleGraph, RuntimeGlobals, RuntimeSpec, TemplateContext, TemplateReplaceSource, }; @@ -74,9 +74,9 @@ impl ModuleDependency for WorkerDependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { - ExportsReferencedType::No + _runtime: Option<&RuntimeSpec>, + ) -> Vec { + vec![] } } diff --git a/crates/rspack_plugin_javascript/src/plugin/flag_usage_plugin.rs b/crates/rspack_plugin_javascript/src/plugin/flag_usage_plugin.rs new file mode 100644 index 00000000000..7cf5a905d75 --- /dev/null +++ b/crates/rspack_plugin_javascript/src/plugin/flag_usage_plugin.rs @@ -0,0 +1,337 @@ +use std::collections::hash_map::Entry; +use std::collections::VecDeque; + +use rspack_core::{ + is_exports_object_referenced, is_no_exports_referenced, BuildMetaExportsType, Compilation, + ConnectionState, DependencyId, ExportsInfoId, ExtendedReferencedExport, ModuleIdentifier, + ReferencedExport, RuntimeSpec, UsageState, +}; +use rspack_identifier::IdentifierMap; +use rustc_hash::FxHashMap as HashMap; + +use crate::utils::join_jsword; + +#[allow(unused)] +pub struct FlagDependencyUsagePlugin<'a> { + global: bool, + compilation: &'a mut Compilation, + exports_info_module_map: HashMap, +} + +#[allow(unused)] +impl<'a> FlagDependencyUsagePlugin<'a> { + pub fn new(global: bool, compilation: &'a mut Compilation) -> Self { + Self { + global, + compilation, + exports_info_module_map: HashMap::default(), + } + } + + fn apply(&mut self) { + for mgm in self + .compilation + .module_graph + .module_graph_modules() + .values() + { + self + .exports_info_module_map + .insert(mgm.exports, mgm.module_identifier); + } + let mut q = VecDeque::new(); + let mg = &mut self.compilation.module_graph; + for exports_info_id in self.exports_info_module_map.keys() { + exports_info_id.set_has_use_info(mg); + } + // SAFETY: we can make sure that entries will not be used other place at the same time, + // this take is aiming to avoid use self ref and mut ref at the same time; + let entries = std::mem::take(&mut self.compilation.entries); + for entry in entries.values() { + for &dep in entry.dependencies.iter() { + self.process_entry_dependency(dep, None, &mut q); + } + } + // TODO: compilation.globalEntry.dependencies, we don't have now https://github.com/webpack/webpack/blob/3f71468514ae2f179ff34c837ce82fcc8f97e24c/lib/FlagDependencyUsagePlugin.js#L328-L330 + _ = std::mem::replace(&mut self.compilation.entries, entries); + + while let Some((module_id, runtime)) = q.pop_front() { + self.process_module(module_id, runtime, false, &mut q); + } + } + + fn process_module( + &mut self, + root_module_id: ModuleIdentifier, + runtime: Option, + force_side_effects: bool, + q: &mut VecDeque<(ModuleIdentifier, Option)>, + ) { + enum ProcessModuleReferencedExports { + Map(HashMap), + ExtendRef(Vec), + } + + let mut map: IdentifierMap = IdentifierMap::default(); + let mut queue = VecDeque::new(); + queue.push_back(root_module_id); + while let Some(module_id) = queue.pop_front() { + // TODO: we don't have blocks.blocks https://github.com/webpack/webpack/blob/3f71468514ae2f179ff34c837ce82fcc8f97e24c/lib/FlagDependencyUsagePlugin.js#L180-L194 + let mgm = self + .compilation + .module_graph + .module_graph_module_by_identifier(&module_id) + .expect("should have module graph module"); + let dep_id_list = mgm.dependencies.clone(); + for dep_id in dep_id_list.into_iter() { + let connection = self + .compilation + .module_graph + .connection_by_dependency(&dep_id); + let connection = if let Some(connection) = connection { + connection + } else { + continue; + }; + let active_state = + connection.get_active_state(&self.compilation.module_graph, runtime.as_ref()); + match active_state { + ConnectionState::Bool(false) => { + continue; + } + ConnectionState::TransitiveOnly => { + self.process_module(connection.module_identifier, runtime.clone(), false, q); + continue; + } + _ => {} + } + let old_referenced_exports = map.remove(&connection.module_identifier); + let dep = self + .compilation + .module_graph + .dependency_by_id(&dep_id) + .expect("should have dep"); + let referenced_exports = if let Some(md) = dep.as_module_dependency() { + md.get_referenced_exports(&self.compilation.module_graph, runtime.as_ref()) + } else { + continue; + }; + if old_referenced_exports.is_none() + || matches!(old_referenced_exports.as_ref().expect("should be some"), ProcessModuleReferencedExports::ExtendRef(v) if is_no_exports_referenced(v)) + || is_exports_object_referenced(&referenced_exports) + { + map.insert( + connection.module_identifier, + ProcessModuleReferencedExports::ExtendRef(referenced_exports), + ); + } else if old_referenced_exports.is_some() && is_no_exports_referenced(&referenced_exports) + { + continue; + } else { + let mut exports_map = if let Some(old_referenced_exports) = old_referenced_exports { + match old_referenced_exports { + ProcessModuleReferencedExports::Map(map) => map, + ProcessModuleReferencedExports::ExtendRef(ref_items) => { + let mut exports_map = HashMap::default(); + for item in ref_items.iter() { + match item { + ExtendedReferencedExport::Array(arr) => { + exports_map.insert(join_jsword(arr, "\n"), item.clone()); + } + ExtendedReferencedExport::Export(export) => { + exports_map.insert(join_jsword(&export.name, "\n"), item.clone()); + } + } + } + exports_map + } + } + } else { + // in else branch above old_referenced_exports must be `Some(T)`, use if let Pattern + // just avoid rust clippy complain + unreachable!() + }; + for mut item in referenced_exports.into_iter() { + match item { + ExtendedReferencedExport::Array(ref arr) => { + let key = join_jsword(arr, "\n"); + exports_map.entry(key).or_insert(item); + } + ExtendedReferencedExport::Export(ref mut export) => { + let key = join_jsword(&export.name, "\n"); + match exports_map.entry(key) { + Entry::Occupied(mut occ) => { + let old_item = occ.get(); + match old_item { + ExtendedReferencedExport::Array(_) => { + occ.insert(item); + } + ExtendedReferencedExport::Export(old_item) => { + occ.insert(ExtendedReferencedExport::Export(ReferencedExport { + name: std::mem::take(&mut export.name), + can_mangle: export.can_mangle && old_item.can_mangle, + })); + } + } + } + Entry::Vacant(vac) => { + vac.insert(item); + } + } + } + } + } + } + } + } + for (module_id, referenced_exports) in map { + let normalized_refs = match referenced_exports { + ProcessModuleReferencedExports::Map(map) => map.into_values().collect::>(), + ProcessModuleReferencedExports::ExtendRef(extend_ref) => extend_ref, + }; + self.process_referenced_module( + module_id, + normalized_refs, + runtime.clone(), + force_side_effects, + q, + ); + } + } + + fn process_entry_dependency( + &mut self, + dep: DependencyId, + _runtime: Option, + queue: &mut VecDeque<(ModuleIdentifier, Option)>, + ) { + if let Some(module) = self + .compilation + .module_graph + .module_graph_module_by_dependency_id(&dep) + { + self.process_referenced_module(module.module_identifier, vec![], None, true, queue); + } + } + + /// TODO: currently we don't impl runtime optimization, runtime is always none + fn process_referenced_module( + &mut self, + module_id: ModuleIdentifier, + used_exports: Vec, + runtime: Option, + force_side_effects: bool, + queue: &mut VecDeque<(ModuleIdentifier, Option)>, + ) { + let mgm = self + .compilation + .module_graph + .module_graph_module_by_identifier(&module_id) + .expect("should have mgm"); + let mgm_exports_info_id = mgm.exports; + if !used_exports.is_empty() { + let need_insert = match mgm.build_meta { + Some(ref build_meta) => matches!(build_meta.exports_type, BuildMetaExportsType::Unset), + None => true, + }; + if need_insert { + let flag = mgm_exports_info_id + .set_used_without_info(&mut self.compilation.module_graph, runtime.as_ref()); + if flag { + queue.push_back((module_id, None)); + } + return; + } + for used_export_info in used_exports { + let (can_mangle, used_exports) = match used_export_info { + ExtendedReferencedExport::Array(used_exports) => (true, used_exports), + ExtendedReferencedExport::Export(export) => (export.can_mangle, export.name), + }; + if used_exports.is_empty() { + let flag = mgm_exports_info_id + .set_used_in_unknown_way(&mut self.compilation.module_graph, runtime.as_ref()); + if flag { + queue.push_back((module_id, runtime.clone())); + } + } else { + let mut current_exports_info_id = mgm_exports_info_id; + let len = used_exports.len(); + for (i, used_export) in used_exports.into_iter().enumerate() { + let export_info_id = current_exports_info_id + .get_export_info(&used_export, &mut self.compilation.module_graph); + let export_info = self + .compilation + .module_graph + .get_export_info_mut_by_id(&export_info_id); + if !can_mangle { + export_info.can_mangle_use = Some(false); + } + let last_one = i == len - 1; + if !last_one { + let nested_info = + export_info_id.get_nested_exports_info(&self.compilation.module_graph); + if let Some(nested_info) = nested_info { + let changed_flag = export_info_id.set_used_conditionally( + &mut self.compilation.module_graph, + Box::new(|used| used == &UsageState::Unused), + UsageState::OnlyPropertiesUsed, + runtime.as_ref(), + ); + if changed_flag { + let current_module = if current_exports_info_id == mgm_exports_info_id { + Some(module_id) + } else { + self + .exports_info_module_map + .get(¤t_exports_info_id) + .cloned() + }; + if let Some(current_module) = current_module { + queue.push_back((current_module, runtime.clone())); + } + } + current_exports_info_id = nested_info; + continue; + } + } + + let changed_flag = export_info_id.set_used_conditionally( + &mut self.compilation.module_graph, + Box::new(|v| v != &UsageState::Used), + UsageState::Used, + runtime.as_ref(), + ); + if changed_flag { + let current_module = if current_exports_info_id == mgm_exports_info_id { + Some(module_id) + } else { + self + .exports_info_module_map + .get(¤t_exports_info_id) + .cloned() + }; + if let Some(current_module) = current_module { + queue.push_back((current_module, runtime.clone())); + } + } + break; + } + } + } + } else { + if !force_side_effects + && match mgm.factory_meta { + Some(ref meta) => meta.side_effect_free.unwrap_or_default(), + None => false, + } + { + return; + } + let flag = mgm_exports_info_id + .set_used_for_side_effects_only(&mut self.compilation.module_graph, runtime.as_ref()); + if flag { + queue.push_back((module_id, runtime)); + } + } + } +} diff --git a/crates/rspack_plugin_javascript/src/plugin/mod.rs b/crates/rspack_plugin_javascript/src/plugin/mod.rs index 1d0cc6d9164..2085384fea6 100644 --- a/crates/rspack_plugin_javascript/src/plugin/mod.rs +++ b/crates/rspack_plugin_javascript/src/plugin/mod.rs @@ -1,3 +1,4 @@ +pub mod flag_usage_plugin; pub mod impl_plugin_for_js_plugin; pub mod infer_async_modules_plugin; pub mod provided_exports_plugin; diff --git a/crates/rspack_plugin_javascript/src/plugin/provided_exports_plugin.rs b/crates/rspack_plugin_javascript/src/plugin/provided_exports_plugin.rs index e6aaae2e045..418d9018209 100644 --- a/crates/rspack_plugin_javascript/src/plugin/provided_exports_plugin.rs +++ b/crates/rspack_plugin_javascript/src/plugin/provided_exports_plugin.rs @@ -91,16 +91,11 @@ impl<'a> ProvidedExportsPlugin<'a> { let export_dependencies = &export_desc.dependencies; if let Some(hide_export) = export_desc.hide_export { for name in hide_export.iter() { - let from_exports_info_id = exports_info_id.export_info_mut(name, self.mg); - let exports_info = self.mg.get_exports_info_mut_by_id(&from_exports_info_id); - let export_info_id = *exports_info - .exports - .get(name) - .expect("should have exports info"); + let from_exports_info_id = exports_info_id.get_export_info(name, self.mg); let export_info = self .mg .export_info_map - .get_mut(&export_info_id) + .get_mut(&from_exports_info_id) .expect("should have export info"); export_info.unuset_target(&dep_id); } @@ -189,12 +184,7 @@ impl<'a> ProvidedExportsPlugin<'a> { spec.hidden.unwrap_or(false), ), }; - let exports_info_id = exports_info.export_info_mut(&name, self.mg); - let exports_info = self.mg.get_exports_info_mut_by_id(&exports_info_id); - let export_info_id = *exports_info - .exports - .get(&name) - .expect("should have export info"); + let export_info_id = exports_info.get_export_info(&name, self.mg); let mut export_info = self .mg @@ -245,15 +235,10 @@ impl<'a> ProvidedExportsPlugin<'a> { // Recalculate target exportsInfo let target = export_info.get_target(self.mg, None); - let exports_info = self.mg.get_exports_info_by_id(&exports_info_id); - let export_info_old_id = *exports_info - .exports - .get(&name) - .expect("should have export info"); let export_info_old = self .mg .export_info_map - .get_mut(&export_info_old_id) + .get_mut(&export_info_id) .expect("should have export info"); _ = std::mem::replace(export_info_old, export_info); @@ -272,11 +257,7 @@ impl<'a> ProvidedExportsPlugin<'a> { } } } - let exports_info = self.mg.get_exports_info_by_id(&exports_info_id); - let export_info_id = *exports_info - .exports - .get(&name) - .expect("should have export info"); + let export_info = self .mg .export_info_map diff --git a/crates/rspack_plugin_javascript/src/utils.rs b/crates/rspack_plugin_javascript/src/utils.rs index b5f6f7ac166..f78825336b7 100644 --- a/crates/rspack_plugin_javascript/src/utils.rs +++ b/crates/rspack_plugin_javascript/src/utils.rs @@ -4,7 +4,7 @@ use rspack_core::{ErrorSpan, ModuleType}; use rspack_error::{DiagnosticKind, Error}; use swc_core::common::{SourceFile, Span, Spanned, SyntaxContext, DUMMY_SP}; use swc_core::ecma::ast::{CallExpr, Callee, Expr, ExprOrSpread, Ident, Lit, Str}; -use swc_core::ecma::atoms::js_word; +use swc_core::ecma::atoms::{js_word, JsWord}; use swc_core::ecma::parser::Syntax; use swc_core::ecma::parser::{EsConfig, TsConfig}; @@ -152,3 +152,15 @@ pub fn ecma_parse_error_to_rspack_error( .with_kind(diagnostic_kind); Error::TraceableError(traceable_error) } + +pub fn join_jsword(arr: &[JsWord], separator: &str) -> String { + let mut ret = String::new(); + if let Some(item) = arr.get(0) { + ret.push_str(item); + } + for item in arr.iter().skip(1) { + ret.push_str(separator); + ret.push_str(item); + } + ret +} diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_export_dependency_scanner.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_export_dependency_scanner.rs index 5e4023ae100..4582bde8024 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_export_dependency_scanner.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_export_dependency_scanner.rs @@ -1,6 +1,6 @@ use rspack_core::{ - tree_shaking::symbol::DEFAULT_JS_WORD, BoxDependency, BoxDependencyTemplate, ConstDependency, - SpanExt, + tree_shaking::symbol::DEFAULT_JS_WORD, BoxDependency, BoxDependencyTemplate, BuildInfo, + ConstDependency, SpanExt, }; use swc_core::{ common::Spanned, @@ -24,6 +24,7 @@ pub struct HarmonyExportDependencyScanner<'a> { pub dependencies: &'a mut Vec, pub presentational_dependencies: &'a mut Vec, pub import_map: &'a mut ImportMap, + pub build_info: &'a mut BuildInfo, } impl<'a> HarmonyExportDependencyScanner<'a> { @@ -31,11 +32,13 @@ impl<'a> HarmonyExportDependencyScanner<'a> { dependencies: &'a mut Vec, presentational_dependencies: &'a mut Vec, import_map: &'a mut ImportMap, + build_info: &'a mut BuildInfo, ) -> Self { Self { dependencies, presentational_dependencies, import_map, + build_info, } } } @@ -52,10 +55,14 @@ impl Visit for HarmonyExportDependencyScanner<'_> { Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => { self .dependencies - .push(Box::new(HarmonyExportSpecifierDependency::new(( + .push(Box::new(HarmonyExportSpecifierDependency::new( ident.sym.clone(), ident.sym.clone(), - )))); + ))); + self + .build_info + .harmony_named_exports + .insert(ident.sym.clone()); } Decl::Var(v) => { find_pat_ids::<_, Ident>(&v.decls) @@ -63,10 +70,11 @@ impl Visit for HarmonyExportDependencyScanner<'_> { .for_each(|ident| { self .dependencies - .push(Box::new(HarmonyExportSpecifierDependency::new(( + .push(Box::new(HarmonyExportSpecifierDependency::new( + ident.sym.clone(), ident.sym.clone(), - ident.sym, - )))) + ))); + self.build_info.harmony_named_exports.insert(ident.sym); }); } _ => {} @@ -96,15 +104,17 @@ impl Visit for HarmonyExportDependencyScanner<'_> { .dependencies .push(Box::new(HarmonyExportImportedSpecifierDependency::new( reference.request.clone(), - vec![(export, reference.names.clone())], + vec![(export.clone(), reference.names.clone())], + Some(export), ))); } else { self .dependencies - .push(Box::new(HarmonyExportSpecifierDependency::new(( - export, + .push(Box::new(HarmonyExportSpecifierDependency::new( + export.clone(), orig.sym.clone(), - )))); + ))); + self.build_info.harmony_named_exports.insert(export); } } } @@ -122,12 +132,13 @@ impl Visit for HarmonyExportDependencyScanner<'_> { } fn visit_export_default_expr(&mut self, export_default_expr: &'_ ExportDefaultExpr) { + // TODO this should be at `HarmonyExportExpressionDependency` self .dependencies - .push(Box::new(HarmonyExportSpecifierDependency::new(( + .push(Box::new(HarmonyExportSpecifierDependency::new( DEFAULT_JS_WORD.clone(), DEFAULT_EXPORT.into(), - )))); + ))); self .presentational_dependencies @@ -146,15 +157,16 @@ impl Visit for HarmonyExportDependencyScanner<'_> { _ => unreachable!(), }; + // TODO this should be at `HarmonyExportExpressionDependency` self .dependencies - .push(Box::new(HarmonyExportSpecifierDependency::new(( + .push(Box::new(HarmonyExportSpecifierDependency::new( DEFAULT_JS_WORD.clone(), match &ident { Some(ident) => ident.sym.clone(), None => DEFAULT_EXPORT.into(), }, - )))); + ))); self .presentational_dependencies diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_import_dependency_scanner.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_import_dependency_scanner.rs index d324a3a078d..141df218b50 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_import_dependency_scanner.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/harmony_import_dependency_scanner.rs @@ -1,21 +1,23 @@ use indexmap::IndexMap; use rspack_core::{ - tree_shaking::symbol::DEFAULT_JS_WORD, BoxDependency, BoxDependencyTemplate, ConstDependency, - DependencyType, SpanExt, + tree_shaking::symbol::DEFAULT_JS_WORD, BoxDependency, BoxDependencyTemplate, BuildInfo, + ConstDependency, DependencyType, SpanExt, }; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; use swc_core::{ common::Span, ecma::{ ast::{ - Callee, ExportAll, ExportSpecifier, Expr, Id, Ident, ImportDecl, ImportSpecifier, Lit, - MemberExpr, MemberProp, ModuleExportName, NamedExport, Program, Prop, + AssignExpr, AssignOp, Callee, ExportAll, ExportSpecifier, Expr, Id, Ident, ImportDecl, + ImportSpecifier, Lit, MemberExpr, MemberProp, ModuleExportName, NamedExport, Pat, PatOrExpr, + Program, Prop, }, atoms::JsWord, visit::{noop_visit_type, Visit, VisitWith}, }, }; +use super::collect_destructuring_assignment_properties; use crate::dependency::{ HarmonyExportImportedSpecifierDependency, HarmonyImportDependency, HarmonyImportSpecifierDependency, Specifier, @@ -37,7 +39,7 @@ impl ImporterReferenceInfo { } } -pub type ImportMap = FxHashMap; +pub type ImportMap = HashMap; pub struct ImporterInfo { pub span: Span, @@ -62,6 +64,7 @@ pub struct HarmonyImportDependencyScanner<'a> { pub presentational_dependencies: &'a mut Vec, pub import_map: &'a mut ImportMap, pub imports: Imports, + pub build_info: &'a mut BuildInfo, } impl<'a> HarmonyImportDependencyScanner<'a> { @@ -69,12 +72,14 @@ impl<'a> HarmonyImportDependencyScanner<'a> { dependencies: &'a mut Vec, presentational_dependencies: &'a mut Vec, import_map: &'a mut ImportMap, + build_info: &'a mut BuildInfo, ) -> Self { Self { dependencies, presentational_dependencies, import_map, imports: Default::default(), + build_info, } } } @@ -85,41 +90,52 @@ impl Visit for HarmonyImportDependencyScanner<'_> { fn visit_program(&mut self, program: &Program) { // collect import map info program.visit_children_with(self); - for ((request, dependency_type), importer_info) in std::mem::take(&mut self.imports).into_iter() { if matches!(dependency_type, DependencyType::EsmExport) && !importer_info.specifiers.is_empty() { - let ids = importer_info + importer_info .specifiers .iter() - .map(|specifier| match specifier { - Specifier::Namespace(n) => (n.clone(), None), + .for_each(|specifier| match specifier { + Specifier::Namespace(n) => { + self + .dependencies + .push(Box::new(HarmonyExportImportedSpecifierDependency::new( + request.clone(), + vec![(n.clone(), None)], + Some(n.clone()), + ))); + self.build_info.harmony_named_exports.insert(n.clone()); + } Specifier::Default(_) => { unreachable!() } Specifier::Named(orig, exported) => { - (exported.clone().unwrap_or(orig.clone()), Some(orig.clone())) + let name = exported.clone().unwrap_or(orig.clone()); + self + .dependencies + .push(Box::new(HarmonyExportImportedSpecifierDependency::new( + request.clone(), + vec![(name.clone(), Some(orig.clone()))], + Some(name.clone()), + ))); + self.build_info.harmony_named_exports.insert(name); } - }) - .collect::>(); - self - .dependencies - .push(Box::new(HarmonyExportImportedSpecifierDependency::new( - request.clone(), - ids, - ))); + }); } - self - .dependencies - .push(Box::new(HarmonyImportDependency::new( - request.clone(), - Some(importer_info.span.into()), - importer_info.specifiers, - dependency_type, - importer_info.exports_all, - ))); + let dependency = HarmonyImportDependency::new( + request.clone(), + Some(importer_info.span.into()), + importer_info.specifiers, + dependency_type, + importer_info.exports_all, + ); + if importer_info.exports_all { + self.build_info.all_star_exports.push(dependency.id); + } + self.dependencies.push(Box::new(dependency)); } // collect import reference info @@ -265,6 +281,7 @@ pub struct HarmonyImportRefDependencyScanner<'a> { pub enter_callee: bool, pub import_map: &'a ImportMap, pub dependencies: &'a mut Vec, + pub properties_in_destructuring: HashMap>, } impl<'a> HarmonyImportRefDependencyScanner<'a> { @@ -273,6 +290,7 @@ impl<'a> HarmonyImportRefDependencyScanner<'a> { import_map, dependencies, enter_callee: false, + properties_in_destructuring: HashMap::default(), } } } @@ -280,6 +298,18 @@ impl<'a> HarmonyImportRefDependencyScanner<'a> { impl Visit for HarmonyImportRefDependencyScanner<'_> { noop_visit_type!(); + // collect referenced properties in destructuring + // import * as a from 'a'; + // const { value } = a; + fn visit_assign_expr(&mut self, assign_expr: &AssignExpr) { + if let PatOrExpr::Pat(box Pat::Object(object_pat)) = &assign_expr.left && assign_expr.op == AssignOp::Assign && let box Expr::Ident(ident) = &assign_expr.right && let Some(reference) = self.import_map.get(&ident.to_id()) && matches!(reference.specifier, Specifier::Namespace(_)) { + if let Some(value) = collect_destructuring_assignment_properties(object_pat) { + self.properties_in_destructuring.entry(ident.sym.clone()).and_modify(|v| v.extend(value.clone())).or_insert(value); + } + } + assign_expr.visit_children_with(self); + } + fn visit_prop(&mut self, n: &Prop) { match n { Prop::Shorthand(shorthand) => { @@ -295,6 +325,7 @@ impl Visit for HarmonyImportRefDependencyScanner<'_> { false, false, reference.specifier.clone(), + None, ))); } } @@ -315,6 +346,7 @@ impl Visit for HarmonyImportRefDependencyScanner<'_> { self.enter_callee, true, // x() reference.specifier.clone(), + self.properties_in_destructuring.remove(&ident.sym), ))); } } @@ -347,6 +379,7 @@ impl Visit for HarmonyImportRefDependencyScanner<'_> { self.enter_callee, !self.enter_callee, // x.xx() reference.specifier.clone(), + None, ))); return; } diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs index 66619f337d5..24c702aaf07 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/mod.rs @@ -36,7 +36,6 @@ use self::{ node_stuff_scanner::NodeStuffScanner, require_context_scanner::RequireContextScanner, url_scanner::UrlScanner, worker_scanner::WorkerScanner, }; - pub type ScanDependenciesResult = (Vec, Vec); #[allow(clippy::too_many_arguments)] @@ -112,11 +111,13 @@ pub fn scan_dependencies( &mut dependencies, &mut presentational_dependencies, &mut import_map, + build_info, )); program.visit_with(&mut HarmonyExportDependencyScanner::new( &mut dependencies, &mut presentational_dependencies, &mut import_map, + build_info, )); let mut worker_syntax_scanner = rspack_core::needs_refactor::WorkerSyntaxScanner::new( rspack_core::needs_refactor::DEFAULT_WORKER_SYNTAX, diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/util.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/util.rs index fcae8389fae..df2d34d28e8 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/util.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/util.rs @@ -1,13 +1,35 @@ +use rustc_hash::FxHashSet as HashSet; use swc_core::{ - common::{pass::AstNodePath, SyntaxContext}, + common::SyntaxContext, ecma::{ - ast::{CallExpr, Expr, MemberExpr}, - visit::{AstParentKind, AstParentNodeRef}, + ast::{CallExpr, Expr, MemberExpr, ObjectPat, ObjectPatProp, PropName}, + atoms::JsWord, }, }; -pub fn as_parent_path(ast_path: &AstNodePath>) -> Vec { - ast_path.iter().map(|n| n.kind()).collect() +pub fn collect_destructuring_assignment_properties( + object_pat: &ObjectPat, +) -> Option> { + let mut properties = HashSet::default(); + + for property in &object_pat.props { + match property { + ObjectPatProp::Assign(assign) => { + properties.insert(assign.key.sym.clone()); + } + ObjectPatProp::KeyValue(key_value) => { + if let PropName::Ident(ident) = &key_value.key { + properties.insert(ident.sym.clone()); + } + } + ObjectPatProp::Rest(_) => {} + } + } + + if properties.is_empty() { + return None; + } + Some(properties) } pub(crate) mod expr_matcher { diff --git a/crates/rspack_plugin_javascript/src/visitors/mod.rs b/crates/rspack_plugin_javascript/src/visitors/mod.rs index 808f4fe8dc3..6989e2d185f 100644 --- a/crates/rspack_plugin_javascript/src/visitors/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/mod.rs @@ -7,21 +7,14 @@ use swc_core::common::comments::Comments; use xxhash_rust::xxh32::xxh32; mod clear_mark; -mod plugin_import; - use rspack_core::{BuildInfo, ModuleType}; use swc_core::ecma::transforms::base::Assumptions; -pub mod relay; pub mod swc_visitor; use rspack_core::{ast::javascript::Ast, CompilerOptions, ResourceData}; use rspack_error::Result; use swc_core::common::chain; use swc_core::ecma::parser::Syntax; use swc_core::ecma::transforms::base::pass::{noop, Optional}; -use swc_emotion::EmotionOptions; - -use crate::visitors::plugin_import::plugin_import; -use crate::visitors::relay::relay; macro_rules! either { ($config: expr, $f: expr) => { @@ -54,7 +47,7 @@ pub fn run_before_pass( ast.transform_with_handler(cm.clone(), |_handler, program, context| { let top_level_mark = context.top_level_mark; let unresolved_mark = context.unresolved_mark; - let comments = None; + let comments: Option<&dyn Comments> = None; let mut assumptions = Assumptions::default(); if syntax.typescript() { @@ -83,7 +76,7 @@ pub fn run_before_pass( syntax.typescript() ), Optional::new( - swc_visitor::react( + rspack_swc_visitors::react( top_level_mark, comments, &cm, @@ -93,7 +86,7 @@ pub fn run_before_pass( should_transform_by_react ), Optional::new( - swc_visitor::fold_react_refresh(unresolved_mark), + rspack_swc_visitors::fold_react_refresh(unresolved_mark), should_transform_by_react && options .builtins @@ -104,8 +97,8 @@ pub fn run_before_pass( ), either!( options.builtins.emotion, - |emotion_options: &EmotionOptions| { - swc_emotion::emotion( + |emotion_options: &rspack_swc_visitors::EmotionOptions| { + rspack_swc_visitors::emotion( emotion_options.clone(), &resource_data.resource_path, xxh32(source.as_bytes(), 0), @@ -115,22 +108,24 @@ pub fn run_before_pass( } ), either!(options.builtins.relay, |relay_option| { - relay( + rspack_swc_visitors::relay( relay_option, resource_data.resource_path.as_path(), PathBuf::from(AsRef::::as_ref(&options.context)), unresolved_mark, ) }), - plugin_import(options.builtins.plugin_import.as_ref()), + either!(options.builtins.plugin_import, |options| { + rspack_swc_visitors::import(options) + }), // enable if configurable // swc_visitor::const_modules(cm, globals), Optional::new( - swc_visitor::define(&options.builtins.define), + rspack_swc_visitors::define(&options.builtins.define), !options.builtins.define.is_empty() ), Optional::new( - swc_visitor::provide_builtin(&options.builtins.provide, unresolved_mark), + rspack_swc_visitors::provide_builtin(&options.builtins.provide, unresolved_mark), !options.builtins.provide.is_empty() ), Optional::new( diff --git a/crates/rspack_plugin_javascript/src/visitors/plugin_import.rs b/crates/rspack_plugin_javascript/src/visitors/plugin_import.rs deleted file mode 100644 index c43471778ee..00000000000 --- a/crates/rspack_plugin_javascript/src/visitors/plugin_import.rs +++ /dev/null @@ -1,11 +0,0 @@ -use either::Either; -use swc_core::ecma::{transforms::base::pass::noop, visit::Fold}; -use swc_plugin_import::PluginImportConfig; - -pub fn plugin_import(config: Option<&Vec>) -> impl Fold + '_ { - if let Some(config) = config { - Either::Left(swc_plugin_import::plugin_import(config)) - } else { - Either::Right(noop()) - } -} diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/compat.rs b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/compat.rs index 0e6b4f26cdc..fae2d7d8e21 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/compat.rs +++ b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/compat.rs @@ -1,6 +1,7 @@ use either::Either; use rspack_core::PresetEnv; -use swc_core::common::{chain, comments::SingleThreadedComments, pass::Optional, Mark}; +use swc_core::common::comments::Comments; +use swc_core::common::{chain, pass::Optional, Mark}; use swc_core::ecma::ast::EsVersion; use swc_core::ecma::preset_env as swc_ecma_preset_env; use swc_core::ecma::transforms::base::{feature::FeatureFlag, pass::noop, Assumptions}; @@ -11,7 +12,7 @@ fn compat_by_preset_env( preset_env_config: Option, unresolved_mark: Mark, assumptions: Assumptions, - comments: Option<&SingleThreadedComments>, + comments: Option<&dyn Comments>, ) -> impl Fold + '_ { if let Some(PresetEnv { mode, targets, core_js }) = preset_env_config && !targets.is_empty() { let core_js = if let Some(core_js) = &core_js && let Ok(core_js) = core_js.parse() { @@ -47,7 +48,7 @@ fn compat_by_es_version( es_version: Option, unresolved_mark: Mark, assumptions: Assumptions, - comments: Option<&SingleThreadedComments>, + comments: Option<&dyn Comments>, is_typescript: bool, ) -> impl Fold + '_ { if let Some(es_version) = es_version { @@ -153,7 +154,7 @@ pub fn compat( assumptions: Assumptions, _top_level_mark: Mark, unresolved_mark: Mark, - comments: Option<&SingleThreadedComments>, + comments: Option<&dyn Comments>, is_typescript: bool, ) -> impl Fold + '_ { chain!( diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/mod.rs b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/mod.rs index 47508336f75..814a3d796d1 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/mod.rs +++ b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/mod.rs @@ -7,15 +7,6 @@ pub use decorator::decorator; mod typescript; pub use typescript::typescript; -mod react; -pub use react::{fold_react_refresh, react}; - -mod define; -pub use define::define; - -mod provide; -pub use provide::provide_builtin; - mod compat; pub use compat::compat; diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/typescript.rs b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/typescript.rs index 90838010ae2..aac9a8a839f 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/typescript.rs +++ b/crates/rspack_plugin_javascript/src/visitors/swc_visitor/typescript.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use swc_core::common::{comments::SingleThreadedComments, Mark, SourceMap}; +use swc_core::common::{comments::Comments, Mark, SourceMap}; use swc_core::ecma::transforms::base::Assumptions; use swc_core::ecma::transforms::{ react::{default_pragma, default_pragma_frag}, @@ -11,7 +11,7 @@ use swc_core::ecma::visit::Fold; pub fn typescript<'a>( assumptions: Assumptions, top_level_mark: Mark, - comments: Option<&'a SingleThreadedComments>, + comments: Option<&'a dyn Comments>, cm: &Arc, ) -> impl Fold + 'a { /* let import_export_assign_config = match module { diff --git a/crates/rspack_plugin_runtime/src/runtime_module/css_loading.rs b/crates/rspack_plugin_runtime/src/runtime_module/css_loading.rs index 676ef4b522b..ce005b99aec 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/css_loading.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/css_loading.rs @@ -46,35 +46,21 @@ impl RuntimeModule for CssLoadingRuntimeModule { .contains(RuntimeGlobals::ENSURE_CHUNK_HANDLERS); let initial_chunks = chunk.get_all_initial_chunks(&compilation.chunk_group_by_ukey); + let mut initial_chunk_ids_with_css = HashSet::default(); + let mut initial_chunk_ids_without_css = HashSet::default(); + for chunk_ukey in initial_chunks.iter() { + let chunk = compilation + .chunk_by_ukey + .get(chunk_ukey) + .expect("Chunk not found"); + if chunk_has_css(chunk_ukey, compilation) { + initial_chunk_ids_with_css.insert(chunk.expect_id().to_string()); + } else { + initial_chunk_ids_without_css.insert(chunk.expect_id().to_string()); + } + } - // `initial_chunk_ids_without_css` is render to `installedChunks` - // `initial_chunk_ids_with_css` is render to `loadCssChunkData`, it will be add to `installedChunks` - // so here direct use `initial_chunk_ids` - let initial_chunk_ids = initial_chunks - .iter() - .map(|chunk_ukey| { - let chunk = compilation - .chunk_by_ukey - .get(chunk_ukey) - .expect("Chunk not found"); - chunk.expect_id().to_string() - }) - .collect::>(); - // let mut initial_chunk_ids_with_css = HashSet::default(); - // let mut initial_chunk_ids_without_css = HashSet::default(); - // for chunk_ukey in initial_chunks.iter() { - // let chunk = compilation - // .chunk_by_ukey - // .get(chunk_ukey) - // .expect("Chunk not found"); - // if chunk_has_css(chunk_ukey, compilation) { - // initial_chunk_ids_with_css.insert(chunk.expect_id().to_string()); - // } else { - // initial_chunk_ids_without_css.insert(chunk.expect_id().to_string()); - // } - // } - - if !with_hmr && !with_loading && initial_chunk_ids.is_empty() { + if !with_hmr && !with_loading && initial_chunk_ids_with_css.is_empty() { return RawSource::from("").boxed(); } @@ -87,7 +73,7 @@ impl RuntimeModule for CssLoadingRuntimeModule { // only render chunk without css. See packages/rspack/tests/runtimeCases/runtime/split-css-chunk test. source.add(RawSource::from(format!( "var installedChunks = {};\n", - &stringify_chunks(&initial_chunk_ids, 0) + &stringify_chunks(&initial_chunk_ids_without_css, 0) ))); source.add(RawSource::from( @@ -102,8 +88,14 @@ impl RuntimeModule for CssLoadingRuntimeModule { compilation .chunk_graph .get_chunk_condition_map(&chunk_ukey, compilation, chunk_has_css); + let chunk_loading_global_expr = format!( + "{}['{}']", + &compilation.options.output.global_object, + &compilation.options.output.chunk_loading_global + ); source.add(RawSource::from( include_str!("runtime/css_loading_with_loading.js") + .replace("$CHUNK_LOADING_GLOBAL_EXPR$", &chunk_loading_global_expr) .replace("CSS_MATCHER", &render_condition_map(&condition_map)), )); } diff --git a/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_filename.rs b/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_filename.rs index 82705199de7..d8b2720cd29 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_filename.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_filename.rs @@ -6,7 +6,7 @@ use rspack_core::{ use rspack_identifier::Identifier; use rustc_hash::FxHashMap as HashMap; -use super::utils::{chunk_has_css, chunk_has_js}; +use super::utils::chunk_has_css; use crate::impl_runtime_module; #[derive(Debug, Eq)] @@ -79,17 +79,13 @@ impl RuntimeModule for GetChunkFilenameRuntimeModule { for chunk_ukey in chunks.iter() { if let Some(chunk) = compilation.chunk_by_ukey.get(chunk_ukey) { let filename_template = match self.source_type { - SourceType::JavaScript => { - if chunk_has_js(chunk_ukey, compilation) { - Some(get_js_chunk_filename_template( - chunk, - &compilation.options.output, - &compilation.chunk_group_by_ukey, - )) - } else { - None - } - } + // TODO webpack different + // css chunk will generate a js chunk, so here add it. + SourceType::JavaScript => Some(get_js_chunk_filename_template( + chunk, + &compilation.options.output, + &compilation.chunk_group_by_ukey, + )), SourceType::Css => { if chunk_has_css(chunk_ukey, compilation) { Some(get_css_chunk_filename_template( diff --git a/crates/rspack_plugin_runtime/src/runtime_module/runtime/css_loading_with_loading.js b/crates/rspack_plugin_runtime/src/runtime_module/runtime/css_loading_with_loading.js index 4cbc5916e92..1f2aecd3caf 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/runtime/css_loading_with_loading.js +++ b/crates/rspack_plugin_runtime/src/runtime_module/runtime/css_loading_with_loading.js @@ -53,3 +53,18 @@ __webpack_require__.f.css = function (chunkId, promises) { } } }; +// TODO: diffrent with webpack +// webpack using `loadCssChunkData` and detect css variables to add install chunk. +// Because rspack the css chunk is always generate one js chunk, so here use js chunk to add install chunk. +var loadCssChunkCallback = function (parentChunkLoadingFunction, data) { + var chunkIds = data[0]; + if (parentChunkLoadingFunction) parentChunkLoadingFunction(data); + for (var i = 0; i < chunkIds.length; i++) { + installedChunks[chunkIds[i]] = 0; + } +}; +var chunkLoadingGlobal = $CHUNK_LOADING_GLOBAL_EXPR$ = $CHUNK_LOADING_GLOBAL_EXPR$ || []; +chunkLoadingGlobal.push = loadCssChunkCallback.bind( + null, + chunkLoadingGlobal.push.bind(chunkLoadingGlobal) +); \ No newline at end of file diff --git a/crates/rspack_plugin_swc_js_minimizer/src/lib.rs b/crates/rspack_plugin_swc_js_minimizer/src/lib.rs index 588a2c3d07c..ea3bda313ce 100644 --- a/crates/rspack_plugin_swc_js_minimizer/src/lib.rs +++ b/crates/rspack_plugin_swc_js_minimizer/src/lib.rs @@ -29,6 +29,8 @@ use swc_ecma_minifier::option::{ pub struct Minification { pub passes: usize, pub drop_console: bool, + pub keep_class_names: bool, + pub keep_fn_names: bool, pub pure_funcs: Vec, pub extract_comments: Option, pub ascii_only: bool, @@ -115,6 +117,12 @@ impl Plugin for SwcJsMinimizerPlugin { ..Default::default() }; + let mangle = MangleOptions { + keep_class_names: minify_options.keep_class_names, + keep_fn_names: minify_options.keep_fn_names, + ..Default::default() + }; + let comments = match minify_options.comments.as_str() { "false" => JsMinifyCommentOption::False, "all" => JsMinifyCommentOption::PreserveAllComments, @@ -146,6 +154,7 @@ impl Plugin for SwcJsMinimizerPlugin { let input_source_map = original_source.map(&MapOptions::default()); let js_minify_options = JsMinifyOptions { compress: BoolOrDataConfig::from_obj(compress.clone()), + mangle: BoolOrDataConfig::from_obj(mangle.clone()), format: format.clone(), source_map: BoolOrDataConfig::from_bool(input_source_map.is_some()), inline_sources_content: true, /* Using true so original_source can be None in SourceMapSource */ @@ -153,6 +162,7 @@ impl Plugin for SwcJsMinimizerPlugin { module: is_module, ..Default::default() }; + let output = match minify( &js_minify_options, input, @@ -236,8 +246,8 @@ pub struct JsMinifyOptions { pub mangle: BoolOrDataConfig, pub format: JsMinifyFormatOptions, pub ecma: TerserEcmaVersion, - pub keep_classnames: bool, - pub keep_fnames: bool, + pub keep_class_names: bool, + pub keep_fn_names: bool, pub module: bool, pub safari10: bool, pub toplevel: bool, diff --git a/crates/rspack_plugin_wasm/src/dependency.rs b/crates/rspack_plugin_wasm/src/dependency.rs index 2ddf8246989..59506941efc 100644 --- a/crates/rspack_plugin_wasm/src/dependency.rs +++ b/crates/rspack_plugin_wasm/src/dependency.rs @@ -1,6 +1,6 @@ use rspack_core::{ AsDependencyTemplate, Dependency, DependencyCategory, DependencyId, DependencyType, ErrorSpan, - ExportsReferencedType, ModuleDependency, ModuleGraph, RuntimeSpec, + ExtendedReferencedExport, ModuleDependency, ModuleGraph, RuntimeSpec, }; use swc_core::ecma::atoms::JsWord; @@ -68,9 +68,9 @@ impl ModuleDependency for WasmImportDependency { fn get_referenced_exports( &self, _module_graph: &ModuleGraph, - _runtime: &RuntimeSpec, - ) -> ExportsReferencedType { - self.name.clone().into() + _runtime: Option<&RuntimeSpec>, + ) -> Vec { + vec![ExtendedReferencedExport::Array(vec![self.name.clone()])] } } diff --git a/crates/rspack_swc_visitors/Cargo.toml b/crates/rspack_swc_visitors/Cargo.toml new file mode 100644 index 00000000000..89755bb232f --- /dev/null +++ b/crates/rspack_swc_visitors/Cargo.toml @@ -0,0 +1,27 @@ +[package] +edition = "2021" +license = "MIT" +name = "rspack_swc_visitors" +repository = "https://github.com/web-infra-dev/rspack" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +indexmap = { workspace = true } +once_cell = { workspace = true } +regex = { workspace = true } +serde = { workspace = true, features = ["derive"] } +swc_core = { workspace = true, features = ["ecma_ast"] } +swc_emotion = { workspace = true } +swc_plugin_import = { path = "../swc_plugin_import" } + +[dev-dependencies] +swc_core = { workspace = true, features = [ + "ecma_ast", + "__visit", + "__common", + "__parser", + "__utils", + "ecma_preset_env", +] } diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/define.rs b/crates/rspack_swc_visitors/src/define.rs similarity index 87% rename from crates/rspack_plugin_javascript/src/visitors/swc_visitor/define.rs rename to crates/rspack_swc_visitors/src/define.rs index eaa18e25e63..253aa30a27c 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/define.rs +++ b/crates/rspack_swc_visitors/src/define.rs @@ -1,7 +1,8 @@ +use std::collections::HashMap; use std::sync::Arc; -use rspack_core::Define; use swc_core::common::collections::AHashMap; +use swc_core::ecma::ast::EsVersion; use swc_core::ecma::parser::EsConfig; use swc_core::ecma::transforms::optimization::inline_globals2; use swc_core::ecma::utils::NodeIgnoringSpan; @@ -11,6 +12,9 @@ use swc_core::{ ecma::parser::{parse_file_as_expr, Syntax}, }; +pub type Define = HashMap; +pub type RawDefine = Define; + pub fn define(opts: &Define) -> impl Fold { let cm: Arc = Default::default(); let defs = opts @@ -21,7 +25,7 @@ pub fn define(opts: &Define) -> impl Fold { parse_file_as_expr( &fm, Syntax::Es(EsConfig::default()), - rspack_core::EsVersion::EsNext, + EsVersion::EsNext, None, &mut vec![], ) @@ -32,7 +36,7 @@ pub fn define(opts: &Define) -> impl Fold { parse_file_as_expr( &fm, Syntax::Es(EsConfig::default()), - rspack_core::EsVersion::EsNext, + EsVersion::EsNext, None, &mut vec![], ) diff --git a/crates/rspack_swc_visitors/src/emotion.rs b/crates/rspack_swc_visitors/src/emotion.rs new file mode 100644 index 00000000000..5786cebba40 --- /dev/null +++ b/crates/rspack_swc_visitors/src/emotion.rs @@ -0,0 +1,2 @@ +pub use swc_emotion::{emotion, EmotionOptions}; +pub type RawEmotionOptions = swc_emotion::EmotionOptions; diff --git a/crates/rspack_swc_visitors/src/import.rs b/crates/rspack_swc_visitors/src/import.rs new file mode 100644 index 00000000000..51bd3123909 --- /dev/null +++ b/crates/rspack_swc_visitors/src/import.rs @@ -0,0 +1,70 @@ +use serde::Deserialize; +pub use swc_plugin_import::plugin_import as import; +pub use swc_plugin_import::{CustomTransform, PluginImportConfig as ImportOptions, StyleConfig}; + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RawStyleConfig { + pub style_library_directory: Option, + pub custom: Option, + pub css: Option, + pub bool: Option, +} + +impl From for StyleConfig { + fn from(raw_style_config: RawStyleConfig) -> Self { + if let Some(style_library_directory) = raw_style_config.style_library_directory { + Self::StyleLibraryDirectory(style_library_directory) + } else if let Some(custom) = raw_style_config.custom { + Self::Custom(CustomTransform::Tpl(custom)) + } else if raw_style_config.css.is_some() { + Self::Css + } else if let Some(bool) = raw_style_config.bool { + Self::Bool(bool) + } else { + Self::None + } + } +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RawImportOptions { + pub library_name: String, + pub library_directory: Option, // default to `lib` + pub custom_name: Option, + pub custom_style_name: Option, // If this is set, `style` option will be ignored + pub style: Option, + pub camel_to_dash_component_name: Option, // default to true + pub transform_to_default_import: Option, + pub ignore_es_component: Option>, + pub ignore_style_component: Option>, +} + +impl From for ImportOptions { + fn from(plugin_import: RawImportOptions) -> Self { + let RawImportOptions { + library_name, + library_directory, + custom_name, + custom_style_name, + style, + camel_to_dash_component_name, + transform_to_default_import, + ignore_es_component, + ignore_style_component, + } = plugin_import; + + Self { + library_name, + library_directory, + custom_name: custom_name.map(CustomTransform::Tpl), + custom_style_name: custom_style_name.map(CustomTransform::Tpl), + style: style.map(Into::into), + camel_to_dash_component_name, + transform_to_default_import, + ignore_es_component, + ignore_style_component, + } + } +} diff --git a/crates/rspack_swc_visitors/src/lib.rs b/crates/rspack_swc_visitors/src/lib.rs new file mode 100644 index 00000000000..cdf9c6643cc --- /dev/null +++ b/crates/rspack_swc_visitors/src/lib.rs @@ -0,0 +1,17 @@ +mod react; +pub use react::{fold_react_refresh, react, RawReactOptions, ReactOptions}; + +mod define; +pub use define::{define, Define, RawDefine}; + +mod provide; +pub use provide::{provide_builtin, Provide, RawProvide}; + +mod relay; +pub use relay::{relay, RawRelayOptions, RelayLanguageConfig, RelayOptions}; + +mod import; +pub use import::{import, CustomTransform, ImportOptions, RawImportOptions, StyleConfig}; + +mod emotion; +pub use emotion::{emotion, EmotionOptions, RawEmotionOptions}; diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/provide.rs b/crates/rspack_swc_visitors/src/provide.rs similarity index 98% rename from crates/rspack_plugin_javascript/src/visitors/swc_visitor/provide.rs rename to crates/rspack_swc_visitors/src/provide.rs index f2e6eff3421..27b4d9a686e 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/provide.rs +++ b/crates/rspack_swc_visitors/src/provide.rs @@ -1,5 +1,6 @@ +use std::collections::HashMap; + use indexmap::IndexSet; -use rspack_core::Provide; use swc_core::common::util::take::Take; use swc_core::common::Span; use swc_core::common::{Mark, DUMMY_SP}; @@ -9,6 +10,9 @@ use swc_core::ecma::ast::{ }; use swc_core::ecma::visit::{as_folder, Fold, VisitMut, VisitMutWith}; +pub type Provide = HashMap>; +pub type RawProvide = Provide; + pub fn provide_builtin(opts: &Provide, unresolved_mark: Mark) -> impl Fold + '_ { as_folder(ProvideBuiltin::new(opts, unresolved_mark)) } diff --git a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/react.rs b/crates/rspack_swc_visitors/src/react.rs similarity index 65% rename from crates/rspack_plugin_javascript/src/visitors/swc_visitor/react.rs rename to crates/rspack_swc_visitors/src/react.rs index aa957e62d37..e6e88d5b88f 100644 --- a/crates/rspack_plugin_javascript/src/visitors/swc_visitor/react.rs +++ b/crates/rspack_swc_visitors/src/react.rs @@ -1,18 +1,73 @@ use std::sync::Arc; -use rspack_core::ReactOptions; +use serde::Deserialize; +use swc_core::common::comments::Comments; use swc_core::common::DUMMY_SP; -use swc_core::common::{comments::SingleThreadedComments, Mark, SourceMap}; +use swc_core::common::{Mark, SourceMap}; use swc_core::ecma::ast::{BlockStmt, FnDecl, Function, Ident, ModuleItem, Program, Stmt}; use swc_core::ecma::transforms::react::RefreshOptions; +use swc_core::ecma::transforms::react::Runtime; use swc_core::ecma::transforms::react::{react as swc_react, Options}; use swc_core::ecma::utils::quote_ident; use swc_core::ecma::visit::Fold; use swc_core::quote; +#[derive(Debug, Clone, Default)] +pub struct ReactOptions { + pub runtime: Option, + pub import_source: Option, + pub pragma: Option, + pub pragma_frag: Option, + pub throw_if_namespace: Option, + pub development: Option, + pub use_builtins: Option, + pub use_spread: Option, + pub refresh: Option, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RawReactOptions { + pub runtime: Option, + pub import_source: Option, + pub pragma: Option, + pub pragma_frag: Option, + pub throw_if_namespace: Option, + pub development: Option, + pub use_builtins: Option, + pub use_spread: Option, + pub refresh: Option, +} + +impl From for ReactOptions { + fn from(value: RawReactOptions) -> Self { + let runtime = if let Some(runtime) = &value.runtime { + match runtime.as_str() { + "automatic" => Some(Runtime::Automatic), + "classic" => Some(Runtime::Classic), + _ => None, + } + } else { + Some(Runtime::Automatic) + }; + + ReactOptions { + runtime, + import_source: value.import_source, + pragma: value.pragma, + pragma_frag: value.pragma_frag, + throw_if_namespace: value.throw_if_namespace, + development: value.development, + use_builtins: value.use_builtins, + use_spread: value.use_spread, + refresh: value.refresh, + } + } +} + pub fn react<'a>( top_level_mark: Mark, - comments: Option<&'a SingleThreadedComments>, + comments: Option<&'a dyn Comments>, cm: &Arc, options: &ReactOptions, unresolved_mark: Mark, diff --git a/crates/rspack_plugin_javascript/src/visitors/relay.rs b/crates/rspack_swc_visitors/src/relay.rs similarity index 82% rename from crates/rspack_plugin_javascript/src/visitors/relay.rs rename to crates/rspack_swc_visitors/src/relay.rs index e34f80e400b..3d6556bdd06 100644 --- a/crates/rspack_plugin_javascript/src/visitors/relay.rs +++ b/crates/rspack_swc_visitors/src/relay.rs @@ -20,7 +20,7 @@ use std::path::{Path, PathBuf}; use once_cell::sync::Lazy; use regex::Regex; -use rspack_core::{RelayConfig, RelayLanguageConfig}; +use serde::Deserialize; use swc_core::{ common::{Mark, DUMMY_SP}, ecma::{ @@ -35,7 +35,7 @@ struct Relay<'a> { unresolved_mark: Mark, root_dir: PathBuf, file_name: &'a Path, - config: &'a RelayConfig, + config: &'a RelayOptions, } fn pull_first_operation_name_from_tpl(tpl: &TaggedTpl) -> Option { @@ -167,8 +167,47 @@ impl<'a> Relay<'a> { } } +#[derive(Debug, Default, Clone)] +pub struct RelayOptions { + pub artifact_directory: Option, + pub language: RelayLanguageConfig, +} + +#[derive(Copy, Clone, Debug)] +pub enum RelayLanguageConfig { + JavaScript, + TypeScript, + Flow, +} + +impl Default for RelayLanguageConfig { + fn default() -> Self { + Self::Flow + } +} + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct RawRelayOptions { + pub artifact_directory: Option, + pub language: String, +} + +impl From for RelayOptions { + fn from(raw_config: RawRelayOptions) -> Self { + Self { + artifact_directory: raw_config.artifact_directory.map(PathBuf::from), + language: match raw_config.language.as_str() { + "typescript" => RelayLanguageConfig::TypeScript, + "flow" => RelayLanguageConfig::Flow, + _ => RelayLanguageConfig::JavaScript, + }, + } + } +} + pub fn relay<'a>( - config: &'a RelayConfig, + config: &'a RelayOptions, file_name: &'a Path, root_dir: PathBuf, unresolved_mark: Mark, diff --git a/crates/rspack_testing/src/lib.rs b/crates/rspack_testing/src/lib.rs index 57e117bce99..4a421d40e42 100644 --- a/crates/rspack_testing/src/lib.rs +++ b/crates/rspack_testing/src/lib.rs @@ -4,8 +4,8 @@ mod run_fixture; mod test_config; pub use eval_raw::evaluate_to_json; pub use run_fixture::{ - apply_from_fixture, test_fixture, test_fixture_css, test_fixture_html, test_fixture_insta, - test_fixture_js, test_rebuild_fixture, + apply_from_fixture, test_fixture, test_fixture_css, test_fixture_css_modules, test_fixture_html, + test_fixture_insta, test_fixture_js, test_rebuild_fixture, }; pub use test_config::TestConfig; pub use testing_macros::{self, fixture}; diff --git a/crates/rspack_testing/src/loader.rs b/crates/rspack_testing/src/loader.rs index aed76195e43..8e50ab89447 100644 --- a/crates/rspack_testing/src/loader.rs +++ b/crates/rspack_testing/src/loader.rs @@ -20,12 +20,14 @@ fn get_builtin_loader(builtin: &str, options: Option<&str>) -> BoxLoader { } if builtin.starts_with(SWC_LOADER_IDENTIFIER) { - return Arc::new(rspack_loader_swc::SwcLoader::new( - serde_json::from_str(options.unwrap_or("{}")).unwrap_or_else(|e| { - panic!("Could not parse builtin:swc-loader options:{options:?},error: {e:?}") - }), - Some(builtin.into()), - )); + return Arc::new( + rspack_loader_swc::SwcLoader::new( + serde_json::from_str(options.unwrap_or("{}")).unwrap_or_else(|e| { + panic!("Could not parse builtin:swc-loader options:{options:?},error: {e:?}") + }), + ) + .with_identifier(builtin.into()), + ); } unreachable!("Unexpected builtin loader: {builtin}") diff --git a/crates/rspack_testing/src/run_fixture.rs b/crates/rspack_testing/src/run_fixture.rs index 55b29365ecc..7b93d9fa2ec 100644 --- a/crates/rspack_testing/src/run_fixture.rs +++ b/crates/rspack_testing/src/run_fixture.rs @@ -40,6 +40,13 @@ pub async fn test_fixture_css(fixture_path: &Path) -> Compiler Compiler { + test_fixture_share(fixture_path, &|s| { + s.ends_with(".css") || (s.ends_with(".js") && !s.contains("runtime.js")) + }) + .await +} +#[tokio::main] pub async fn test_fixture_insta( fixture_path: &Path, stats_filter: &dyn Fn(&str) -> bool, diff --git a/crates/rspack_testing/test.config.scheme.json b/crates/rspack_testing/test.config.scheme.json index 31cceb8d6cd..539757d2b2d 100644 --- a/crates/rspack_testing/test.config.scheme.json +++ b/crates/rspack_testing/test.config.scheme.json @@ -346,6 +346,14 @@ "default": false, "type": "boolean" }, + "keepClassNames": { + "default": false, + "type": "boolean" + }, + "keepFnNames": { + "default": false, + "type": "boolean" + }, "extractComments": { "default": null, "type": [ diff --git a/examples/builtin-swc-loader/rspack.config.js b/examples/builtin-swc-loader/rspack.config.js index 8568b5f480d..60c31bfa10e 100644 --- a/examples/builtin-swc-loader/rspack.config.js +++ b/examples/builtin-swc-loader/rspack.config.js @@ -1,4 +1,3 @@ -const path = require("path"); /** @type {import('@rspack/cli').Configuration} */ const config = { entry: { diff --git a/examples/emotion/rspack.config.js b/examples/emotion/rspack.config.js index 937a957a0b7..3c2ded6087d 100644 --- a/examples/emotion/rspack.config.js +++ b/examples/emotion/rspack.config.js @@ -8,12 +8,28 @@ const config = { { template: "./index.html" } - ], - emotion: true, - react: { - importSource: "@emotion/react", - runtime: "automatic" - } + ] + }, + module: { + rules: [ + { + test: /\.jsx$/, + loader: "builtin:swc-loader", + options: { + jsc: { + parser: { + syntax: "ecmascript", + jsx: true + } + }, + rspackExperiments: { + emotion: true, + react: {} + } + }, + type: "javascript/auto" + } + ] } }; module.exports = config; diff --git a/examples/monaco-editor-js/package.json b/examples/monaco-editor-js/package.json index d1517be976e..d2b156f4bc7 100644 --- a/examples/monaco-editor-js/package.json +++ b/examples/monaco-editor-js/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "dev": "rspack serve", - "build": "rspack build" + "build:local": "rspack build" }, "license": "MIT", "dependencies": { diff --git a/examples/monaco-editor-ts-react/package.json b/examples/monaco-editor-ts-react/package.json index 1f7e51c4e0e..af87a78ced3 100644 --- a/examples/monaco-editor-ts-react/package.json +++ b/examples/monaco-editor-ts-react/package.json @@ -6,7 +6,7 @@ "private": true, "scripts": { "dev": "rspack serve", - "build": "rspack build" + "build:local": "rspack build" }, "keywords": [], "author": "", diff --git a/npm/darwin-arm64/package.json b/npm/darwin-arm64/package.json index 146f6985bc9..1ff4f4623b6 100644 --- a/npm/darwin-arm64/package.json +++ b/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/binding-darwin-arm64", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Node binding for rspack", "main": "rspack.darwin-arm64.node", diff --git a/npm/darwin-x64/package.json b/npm/darwin-x64/package.json index 4e13fb90dff..488bec64304 100644 --- a/npm/darwin-x64/package.json +++ b/npm/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/binding-darwin-x64", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Node binding for rspack", "main": "rspack.darwin-x64.node", diff --git a/npm/linux-x64-gnu/package.json b/npm/linux-x64-gnu/package.json index 899a89011ab..b9f230c3a1a 100644 --- a/npm/linux-x64-gnu/package.json +++ b/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/binding-linux-x64-gnu", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Node binding for rspack", "main": "rspack.linux-x64-gnu.node", diff --git a/npm/win32-x64-msvc/package.json b/npm/win32-x64-msvc/package.json index 4a484f34d37..ed6f8f80cc7 100644 --- a/npm/win32-x64-msvc/package.json +++ b/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/binding-win32-x64-msvc", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Node binding for rspack", "main": "rspack.win32-x64-msvc.node", diff --git a/package.json b/package.json index 717e9aeaeb9..084907596c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "monorepo", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "A Fast Rust-based web bundler", "private": true, diff --git a/packages/create-rspack/package.json b/packages/create-rspack/package.json index 2b24c5864e4..1fb2cb9f270 100644 --- a/packages/create-rspack/package.json +++ b/packages/create-rspack/package.json @@ -1,6 +1,6 @@ { "name": "create-rspack", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "main": "index.js", "bin": { diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/index.test.ts b/packages/playground/cases/react/basic-deprecated-dev-client/index.test.ts new file mode 100644 index 00000000000..a6a420c194b --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/index.test.ts @@ -0,0 +1,58 @@ +import { test, expect } from "@/fixtures"; + +test("render should work", async ({ page }) => { + expect(await page.textContent(".header")).toBe("Hello World"); +}); + +test("hmr should work", async ({ page, fileAction, rspack }) => { + expect(await page.textContent("button")).toBe("10"); + await page.click("button"); + expect(await page.textContent("button")).toBe("11"); + expect(await page.textContent(".placeholder")).toBe("__PLACE_HOLDER__"); + fileAction.updateFile("src/App.jsx", content => + content.replace("__PLACE_HOLDER__", "__EDITED__") + ); + await rspack.waitingForHmr(async function () { + return (await page.textContent(".placeholder")) === "__EDITED__"; + }); + expect(await page.textContent("button")).toBe("11"); +}); + +test("context+component should work", async ({ page, fileAction, rspack }) => { + expect(await page.textContent("#context")).toBe("context-value"); + await page.click("#context"); + expect(await page.textContent("#context")).toBe("context-value-click"); + fileAction.updateFile("src/CountProvider.jsx", content => + content.replace("context-value", "context-value-update") + ); + await rspack.waitingForHmr(async function () { + return (await page.textContent("#context")) === "context-value-update"; + }); +}); + +test("ReactRefreshFinder should work", async ({ page }) => { + expect(await page.textContent("#nest-function")).toBe("nest-function"); +}); + +test("update same export name from different module should work", async ({ + page, + fileAction, + rspack +}) => { + expect(await page.textContent(".same-export-name1")).toBe("__NAME_1__"); + expect(await page.textContent(".same-export-name2")).toBe("__NAME_2__"); + fileAction.updateFile("src/SameExportName1.jsx", content => + content.replace("__NAME_1__", "__name_1__") + ); + await rspack.waitingForHmr(async function () { + return (await page.textContent(".same-export-name1")) === "__name_1__"; + }); + expect(await page.textContent(".same-export-name2")).toBe("__NAME_2__"); + fileAction.updateFile("src/SameExportName2.jsx", content => + content.replace("__NAME_2__", "__name_2__") + ); + await rspack.waitingForHmr(async function () { + return (await page.textContent(".same-export-name2")) === "__name_2__"; + }); + expect(await page.textContent(".same-export-name1")).toBe("__name_1__"); +}); diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/react-refresh.js b/packages/playground/cases/react/basic-deprecated-dev-client/react-refresh.js new file mode 100644 index 00000000000..e4b58b1fff8 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/react-refresh.js @@ -0,0 +1,17 @@ +const reactRefresh = require("@rspack/plugin-react-refresh/react-refresh"); + +function shouldLooksLikeAModuleId(id) { + console.log(id); + if (typeof id === "string" && !id.includes("[object Object]")) { + return; + } + throw new Error(`Looks like ${id} is not a module.id`); +} + +module.exports = { + ...reactRefresh, + register(type, id) { + shouldLooksLikeAModuleId(id); + return reactRefresh.register(type, id); + } +}; diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/rspack.config.js b/packages/playground/cases/react/basic-deprecated-dev-client/rspack.config.js new file mode 100644 index 00000000000..e44d143da1f --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/rspack.config.js @@ -0,0 +1,36 @@ +/** @type { import('@rspack/core').RspackOptions } */ +module.exports = { + context: __dirname, + mode: "development", + module: { + // add this to test react refresh runtime shouldn't inject runtime,see #3984 + rules: [ + { + test: /\.js$/, + type: "jsx" + } + ] + }, + entry: ["@rspack/dev-client/react-refresh-entry", "./src/index.jsx"], + devServer: { + hot: true + }, + cache: false, + stats: "none", + infrastructureLogging: { + debug: false + }, + builtins: { + provide: { + $ReactRefreshRuntime$: [require.resolve("./react-refresh.js")] + }, + html: [ + { + template: "./src/index.html" + } + ] + }, + watchOptions: { + poll: 1000 + } +}; diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/App.jsx b/packages/playground/cases/react/basic-deprecated-dev-client/src/App.jsx new file mode 100644 index 00000000000..229770f9d16 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/App.jsx @@ -0,0 +1,25 @@ +import React from "react"; +import "./index.css"; +import { ContextComponent } from "./CountProvider"; +import { ReactRefreshFinder } from "./ReactRefreshFinder"; +import { SameExportName as SameExportName1 } from './SameExportName1' +import { SameExportName as SameExportName2 } from './SameExportName2' + +const Button = () => { + const [count, setCount] = React.useState(10); + return ; +}; + +export const App = () => { + return ( +
+
Hello World
+
+ ); +}; diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/CountProvider.jsx b/packages/playground/cases/react/basic-deprecated-dev-client/src/CountProvider.jsx new file mode 100644 index 00000000000..afbe1fcbe34 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/CountProvider.jsx @@ -0,0 +1,19 @@ +import React from 'react'; + +export const CountContext = React.createContext(); + +export function CountProvider({ children }) { + const [count, setCount] = React.useState('context-value'); + return ( + + {children} + + ); +} + +export function ContextComponent() { + const { count, setCount } = React.useContext(CountContext); + return
setCount((count) => count + '-click')}> + {count} +
+} \ No newline at end of file diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/ReactRefreshFinder.jsx b/packages/playground/cases/react/basic-deprecated-dev-client/src/ReactRefreshFinder.jsx new file mode 100644 index 00000000000..83b0509d747 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/ReactRefreshFinder.jsx @@ -0,0 +1,10 @@ +import React, { useState } from 'react'; + +function CreateReactRefreshFinder() { + return function Component() { + useState(1); + return
nest-function
; + }; +} + +export const ReactRefreshFinder = CreateReactRefreshFinder() \ No newline at end of file diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName1.jsx b/packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName1.jsx new file mode 100644 index 00000000000..2a7f2b45f15 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName1.jsx @@ -0,0 +1,3 @@ +export function SameExportName() { + return
__NAME_1__
+} diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName2.jsx b/packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName2.jsx new file mode 100644 index 00000000000..8bf62ab6bb5 --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/SameExportName2.jsx @@ -0,0 +1,3 @@ +export function SameExportName() { + return
__NAME_2__
+} diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/index.css b/packages/playground/cases/react/basic-deprecated-dev-client/src/index.css new file mode 100644 index 00000000000..753f48a591f --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/index.css @@ -0,0 +1,3 @@ +body { + background-color: rgba(0, 0, 0, 0); +} diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/index.html b/packages/playground/cases/react/basic-deprecated-dev-client/src/index.html new file mode 100644 index 00000000000..127a457455b --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/index.html @@ -0,0 +1,12 @@ + + + + + + + Document + + +
+ + diff --git a/packages/playground/cases/react/basic-deprecated-dev-client/src/index.jsx b/packages/playground/cases/react/basic-deprecated-dev-client/src/index.jsx new file mode 100644 index 00000000000..52e72c3334e --- /dev/null +++ b/packages/playground/cases/react/basic-deprecated-dev-client/src/index.jsx @@ -0,0 +1,11 @@ +import React from "react"; +import { createRoot } from "react-dom/client"; +import { App } from './App'; +import { CountProvider } from "./CountProvider"; + +const container = createRoot(document.getElementById("root")); +container.render( + + + +); diff --git a/packages/playground/cases/react/basic/react-refresh.js b/packages/playground/cases/react/basic/react-refresh.js index 17da450925d..e4b58b1fff8 100644 --- a/packages/playground/cases/react/basic/react-refresh.js +++ b/packages/playground/cases/react/basic/react-refresh.js @@ -1,4 +1,4 @@ -const reactRefresh = require("@rspack/dev-client/react-refresh"); +const reactRefresh = require("@rspack/plugin-react-refresh/react-refresh"); function shouldLooksLikeAModuleId(id) { console.log(id); diff --git a/packages/playground/cases/react/basic/rspack.config.js b/packages/playground/cases/react/basic/rspack.config.js index e44d143da1f..2d75adbfe2d 100644 --- a/packages/playground/cases/react/basic/rspack.config.js +++ b/packages/playground/cases/react/basic/rspack.config.js @@ -11,7 +11,10 @@ module.exports = { } ] }, - entry: ["@rspack/dev-client/react-refresh-entry", "./src/index.jsx"], + entry: [ + "@rspack/plugin-react-refresh/react-refresh-entry", + "./src/index.jsx" + ], devServer: { hot: true }, diff --git a/packages/playground/cases/react/class-component/react-refresh.js b/packages/playground/cases/react/class-component/react-refresh.js index 17da450925d..e4b58b1fff8 100644 --- a/packages/playground/cases/react/class-component/react-refresh.js +++ b/packages/playground/cases/react/class-component/react-refresh.js @@ -1,4 +1,4 @@ -const reactRefresh = require("@rspack/dev-client/react-refresh"); +const reactRefresh = require("@rspack/plugin-react-refresh/react-refresh"); function shouldLooksLikeAModuleId(id) { console.log(id); diff --git a/packages/playground/cases/react/class-component/rspack.config.js b/packages/playground/cases/react/class-component/rspack.config.js index f8b513d6e68..3872f4f3a0e 100644 --- a/packages/playground/cases/react/class-component/rspack.config.js +++ b/packages/playground/cases/react/class-component/rspack.config.js @@ -2,7 +2,10 @@ module.exports = { context: __dirname, mode: "development", - entry: ["@rspack/dev-client/react-refresh-entry", "./src/index.jsx"], + entry: [ + "@rspack/plugin-react-refresh/react-refresh-entry", + "./src/index.jsx" + ], devServer: { hot: true }, diff --git a/packages/playground/cases/vue3/index.test.ts b/packages/playground/cases/vue3/index.test.ts new file mode 100644 index 00000000000..0c111995b73 --- /dev/null +++ b/packages/playground/cases/vue3/index.test.ts @@ -0,0 +1,18 @@ +import { test, expect } from "@/fixtures"; + +test("should successfully render vue3", async ({ page }) => { + expect(await page.textContent("h1")).toBe("vue3"); +}); + +test("vue3 hmr", async ({ page, fileAction, rspack }) => { + await page.click("button"); + expect(await page.textContent("button")).toBe("1"); + fileAction.updateFile("src/App.vue", content => + content.replace("vue3", "vue3 hi") + ); + await rspack.waitingForHmr(async function () { + return (await page.textContent("h1")) === "vue3 hi"; + }); + // hmr should keep status + expect(await page.textContent("button")).toBe("1"); +}); diff --git a/packages/playground/cases/vue3/rspack.config.js b/packages/playground/cases/vue3/rspack.config.js new file mode 100644 index 00000000000..80f640340cd --- /dev/null +++ b/packages/playground/cases/vue3/rspack.config.js @@ -0,0 +1,42 @@ +const { VueLoaderPlugin } = require("vue-loader"); + +/** @type { import('@rspack/core').RspackOptions } */ +module.exports = { + context: __dirname, + mode: "development", + entry: "./src/main.js", + builtins: { + html: [ + { + template: "./src/index.html" + } + ], + define: { + __VUE_OPTIONS_API__: JSON.stringify(true), + __VUE_PROD_DEVTOOLS__: JSON.stringify(false) + } + }, + devServer: { + hot: true + }, + plugins: [new VueLoaderPlugin()], + module: { + rules: [ + { + test: /\.vue$/, + loader: "vue-loader", + options: { + experimentalInlineMatchResource: true + } + } + ] + }, + cache: false, + stats: "error", + infrastructureLogging: { + debug: false + }, + watchOptions: { + poll: 1000 + } +}; diff --git a/packages/playground/cases/vue3/src/App.vue b/packages/playground/cases/vue3/src/App.vue new file mode 100644 index 00000000000..335835d2ddc --- /dev/null +++ b/packages/playground/cases/vue3/src/App.vue @@ -0,0 +1,14 @@ + + + diff --git a/packages/playground/cases/vue3/src/index.html b/packages/playground/cases/vue3/src/index.html new file mode 100644 index 00000000000..127a457455b --- /dev/null +++ b/packages/playground/cases/vue3/src/index.html @@ -0,0 +1,12 @@ + + + + + + + Document + + +
+ + diff --git a/packages/playground/cases/vue3/src/main.js b/packages/playground/cases/vue3/src/main.js new file mode 100644 index 00000000000..a7a89ae0eaf --- /dev/null +++ b/packages/playground/cases/vue3/src/main.js @@ -0,0 +1,3 @@ +import { createApp } from "vue"; +import App from "./App.vue"; +createApp(App).mount("#root"); diff --git a/packages/playground/package.json b/packages/playground/package.json index 48d27ed4a95..727b958a185 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -14,6 +14,7 @@ "devDependencies": { "@playwright/test": "1.35.0", "@rspack/core": "workspace:*", + "@rspack/plugin-react-refresh": "workspace:*", "@rspack/dev-client": "workspace:*", "@rspack/dev-server": "workspace:*", "@types/fs-extra": "11.0.1", @@ -23,6 +24,8 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "tailwindcss": "^3.3.0", - "ws": "8.8.1" + "ws": "8.8.1", + "vue": "3.2.47", + "vue-loader": "^17.2.2" } -} +} \ No newline at end of file diff --git a/packages/rspack-cli/package.json b/packages/rspack-cli/package.json index 4941063453f..2d6f2eca4e4 100644 --- a/packages/rspack-cli/package.json +++ b/packages/rspack-cli/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/cli", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "CLI for rspack", "bin": { diff --git a/packages/rspack-cli/src/rspack-cli.ts b/packages/rspack-cli/src/rspack-cli.ts index 19672e81a73..2d6cf358e95 100644 --- a/packages/rspack-cli/src/rspack-cli.ts +++ b/packages/rspack-cli/src/rspack-cli.ts @@ -23,7 +23,7 @@ import { import { normalizeEnv } from "./utils/options"; import { loadRspackConfig } from "./utils/loadConfig"; import findConfig from "./utils/findConfig"; -import { RspackPluginInstance, RspackPluginFunction } from "@rspack/core"; +import type { RspackPluginInstance, RspackPluginFunction } from "@rspack/core"; import path from "path"; type Command = "serve" | "build"; diff --git a/packages/rspack-dev-client/README.md b/packages/rspack-dev-client/README.md index 84ea27106ad..e8cff60366b 100644 --- a/packages/rspack-dev-client/README.md +++ b/packages/rspack-dev-client/README.md @@ -3,6 +3,8 @@ Rspack Banner +# ⚠️ DEPRECATED: use @rspack/plugin-react-refresh instead + # @rspack/dev-client Development client for rspack. diff --git a/packages/rspack-dev-client/package.json b/packages/rspack-dev-client/package.json index cabc8667921..9d3317c5d4e 100644 --- a/packages/rspack-dev-client/package.json +++ b/packages/rspack-dev-client/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/dev-client", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Development client for rspack", "scripts": { @@ -18,12 +18,11 @@ "url": "https://github.com/web-infra-dev/rspack", "directory": "packages/rspack-dev-client" }, - "devDependencies": { - "react-refresh": "0.14.0", - "webpack": "5.76.0" - }, "dependencies": { - "@pmmmwh/react-refresh-webpack-plugin": "0.5.10" + "@rspack/plugin-react-refresh": "workspace:*" + }, + "devDependencies": { + "react-refresh": "0.14.0" }, "peerDependencies": { "react-refresh": ">=0.10.0 <1.0.0" @@ -35,7 +34,6 @@ }, "exports": { "./react-refresh": "./src/reactRefresh.js", - "./react-refresh-entry": "./src/reactRefreshEntry.js", - "./runtime-path-regexp": "./src/runtimePathRegexp.js" + "./react-refresh-entry": "./src/reactRefreshEntry.js" } } \ No newline at end of file diff --git a/packages/rspack-dev-client/src/reactRefresh.js b/packages/rspack-dev-client/src/reactRefresh.js index 7c24482448b..939000d557c 100644 --- a/packages/rspack-dev-client/src/reactRefresh.js +++ b/packages/rspack-dev-client/src/reactRefresh.js @@ -1,23 +1 @@ -// Thanks https://github.com/pmmmwh/react-refresh-webpack-plugin -const RefreshUtils = require("@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils"); -const RefreshRuntime = require("react-refresh/runtime"); - -// Port from https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/loader/utils/getRefreshModuleRuntime.js#L29 -function refresh(moduleId, webpackHot) { - const currentExports = RefreshUtils.getModuleExports(moduleId); - const fn = exports => { - RefreshUtils.executeRuntime(exports, moduleId, webpackHot); - }; - if (typeof Promise !== "undefined" && currentExports instanceof Promise) { - currentExports.then(fn); - } else { - fn(currentExports); - } -} - -module.exports = { - refresh, - register: RefreshRuntime.register, - createSignatureFunctionForTransform: - RefreshRuntime.createSignatureFunctionForTransform -}; +module.exports = require("@rspack/plugin-react-refresh/react-refresh"); diff --git a/packages/rspack-dev-client/src/reactRefreshEntry.js b/packages/rspack-dev-client/src/reactRefreshEntry.js index 26eb38307b3..8f77c1e7d78 100644 --- a/packages/rspack-dev-client/src/reactRefreshEntry.js +++ b/packages/rspack-dev-client/src/reactRefreshEntry.js @@ -1,4 +1 @@ -const RefreshRuntime = require("react-refresh/runtime"); - -RefreshRuntime.injectIntoGlobalHook(self); -self.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform; +require("@rspack/plugin-react-refresh/react-refresh-entry"); diff --git a/packages/rspack-dev-client/src/runtimePathRegexp.js b/packages/rspack-dev-client/src/runtimePathRegexp.js deleted file mode 100644 index c42904619f7..00000000000 --- a/packages/rspack-dev-client/src/runtimePathRegexp.js +++ /dev/null @@ -1,17 +0,0 @@ -const path = require("path"); -const reactRefreshPath = require.resolve("./reactRefresh.js"); -const RefreshUtilsPath = require.resolve( - "@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils", - { - paths: [reactRefreshPath] - } -); -const RefreshRuntimeDirPath = path.dirname( - require.resolve("react-refresh", { - paths: [reactRefreshPath] - }) -); - -exports.runtimePathRegexp = new RegExp( - `${reactRefreshPath}|${RefreshUtilsPath}|^${RefreshRuntimeDirPath}` -); diff --git a/packages/rspack-dev-server/package.json b/packages/rspack-dev-server/package.json index deec53db4fc..047bbdcfd1f 100644 --- a/packages/rspack-dev-server/package.json +++ b/packages/rspack-dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/dev-server", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Development server for rspack", "main": "./dist/index.js", @@ -36,8 +36,8 @@ "puppeteer": "19.4.0" }, "dependencies": { - "@rspack/dev-client": "workspace:*", "@rspack/dev-server": "workspace:*", + "@rspack/plugin-react-refresh": "workspace:*", "chokidar": "3.5.3", "connect-history-api-fallback": "2.0.0", "express": "4.18.1", diff --git a/packages/rspack-dev-server/src/patch.ts b/packages/rspack-dev-server/src/patch.ts new file mode 100644 index 00000000000..b77fb0668aa --- /dev/null +++ b/packages/rspack-dev-server/src/patch.ts @@ -0,0 +1,34 @@ +import WebpackDevServer from "webpack-dev-server"; + +let old: InstanceType["sendStats"] | undefined; + +function restoreDevServerPatch() { + // @ts-expect-error private API + WebpackDevServer.prototype.sendStats = old; +} + +// Patch webpack-dev-server to prevent it from failing to send stats. +// See https://github.com/web-infra-dev/rspack/pull/4028 for details. +function applyDevServerPatch() { + if (old) return restoreDevServerPatch; + + // @ts-expect-error private API + old = WebpackDevServer.prototype.sendStats; + + // @ts-expect-error private API + WebpackDevServer.prototype.sendStats = function sendStats__rspack_patched( + ...args + ) { + let stats = args[1]; + + if (!stats) { + return; + } + + return old.apply(this, args); + }; + + return restoreDevServerPatch; +} + +export { applyDevServerPatch }; diff --git a/packages/rspack-dev-server/src/server.ts b/packages/rspack-dev-server/src/server.ts index 5a5dc96988d..b7731665383 100644 --- a/packages/rspack-dev-server/src/server.ts +++ b/packages/rspack-dev-server/src/server.ts @@ -13,11 +13,13 @@ import type { FSWatcher } from "chokidar"; import rdm from "webpack-dev-middleware"; import type { Server } from "http"; import fs from "fs"; +import ReactRefreshPlugin from "@rspack/plugin-react-refresh"; import WebpackDevServer from "webpack-dev-server"; import type { ResolvedDevServer, DevServer } from "./config"; import { getRspackMemoryAssets } from "./middleware"; -// @ts-expect-error -import { runtimePathRegexp } from "@rspack/dev-client/runtime-path-regexp"; +import { applyDevServerPatch } from "./patch"; + +applyDevServerPatch(); export class RspackDevServer extends WebpackDevServer { /** @@ -75,178 +77,6 @@ export class RspackDevServer extends WebpackDevServer { ); } - addAdditionalEntries(compiler: Compiler) { - const additionalEntries: string[] = []; - // @ts-expect-error - const isWebTarget = WebpackDevServer.isWebTarget(compiler); - // inject runtime first, avoid other additional entry after transfrom depend on it - const clientPath = require.resolve("webpack-dev-server/client/index.js"); - if (this.options.hot) { - if (compiler.options.builtins.react?.refresh) { - const reactRefreshEntryPath = require.resolve( - "@rspack/dev-client/react-refresh-entry" - ); - additionalEntries.push(reactRefreshEntryPath); - } - // #3356 make sure resolve webpack/hot/dev-server from webpack-dev-server - const hotUpdateEntryPath = require.resolve("webpack/hot/dev-server", { - paths: [clientPath] - }); - additionalEntries.push(hotUpdateEntryPath); - } - if (this.options.client && isWebTarget) { - let webSocketURLStr = ""; - - if (this.options.webSocketServer) { - const webSocketURL = this.options.client - .webSocketURL as WebpackDevServer.WebSocketURL; - const webSocketServer = this.options.webSocketServer; - const searchParams = new URLSearchParams(); - - let protocol: string; - - // We are proxying dev server and need to specify custom `hostname` - if (typeof webSocketURL.protocol !== "undefined") { - protocol = webSocketURL.protocol; - } else { - protocol = this.options.server.type === "http" ? "ws:" : "wss:"; - } - - searchParams.set("protocol", protocol); - - if (typeof webSocketURL.username !== "undefined") { - searchParams.set("username", webSocketURL.username); - } - - if (typeof webSocketURL.password !== "undefined") { - searchParams.set("password", webSocketURL.password); - } - - let hostname: string; - - // SockJS is not supported server mode, so `hostname` and `port` can't specified, let's ignore them - // TODO show warning about this - const isSockJSType = webSocketServer.type === "sockjs"; - - // We are proxying dev server and need to specify custom `hostname` - if (typeof webSocketURL.hostname !== "undefined") { - hostname = webSocketURL.hostname; - } - // Web socket server works on custom `hostname`, only for `ws` because `sock-js` is not support custom `hostname` - else if ( - typeof webSocketServer.options.host !== "undefined" && - !isSockJSType - ) { - hostname = webSocketServer.options.host; - } - // The `host` option is specified - else if (typeof this.options.host !== "undefined") { - hostname = this.options.host; - } - // The `port` option is not specified - else { - hostname = "0.0.0.0"; - } - - searchParams.set("hostname", hostname); - - let port: number | string; - - // We are proxying dev server and need to specify custom `port` - if (typeof webSocketURL.port !== "undefined") { - port = webSocketURL.port; - } - // Web socket server works on custom `port`, only for `ws` because `sock-js` is not support custom `port` - else if ( - typeof webSocketServer.options.port !== "undefined" && - !isSockJSType - ) { - port = webSocketServer.options.port; - } - // The `port` option is specified - else if (typeof this.options.port === "number") { - port = this.options.port; - } - // The `port` option is specified using `string` - else if ( - typeof this.options.port === "string" && - this.options.port !== "auto" - ) { - port = Number(this.options.port); - } - // The `port` option is not specified or set to `auto` - else { - port = "0"; - } - - searchParams.set("port", String(port)); - - let pathname = ""; - - // We are proxying dev server and need to specify custom `pathname` - if (typeof webSocketURL.pathname !== "undefined") { - pathname = webSocketURL.pathname; - } - // Web socket server works on custom `path` - else if ( - typeof webSocketServer.options.prefix !== "undefined" || - typeof webSocketServer.options.path !== "undefined" - ) { - pathname = - webSocketServer.options.prefix || webSocketServer.options.path; - } - - searchParams.set("pathname", pathname); - - const client = /** @type {ClientConfiguration} */ this.options.client; - - if (typeof client.logging !== "undefined") { - searchParams.set("logging", client.logging); - } - - if (typeof client.progress !== "undefined") { - searchParams.set("progress", String(client.progress)); - } - - if (typeof client.overlay !== "undefined") { - searchParams.set( - "overlay", - typeof client.overlay === "boolean" - ? String(client.overlay) - : JSON.stringify(client.overlay) - ); - } - - if (typeof client.reconnect !== "undefined") { - searchParams.set( - "reconnect", - typeof client.reconnect === "number" - ? String(client.reconnect) - : "10" - ); - } - - if (typeof this.options.hot !== "undefined") { - searchParams.set("hot", String(this.options.hot)); - } - - if (typeof this.options.liveReload !== "undefined") { - searchParams.set("live-reload", String(this.options.liveReload)); - } - - webSocketURLStr = searchParams.toString(); - } - - additionalEntries.push(`${clientPath}?${webSocketURLStr}`); - } - - for (const additionalEntry of additionalEntries) { - new compiler.webpack.EntryPlugin(compiler.context, additionalEntry, { - name: undefined - }).apply(compiler); - } - } - getClientTransport(): string { // WARNING: we can't use `super.getClientTransport`, // because we doesn't had same directory structure. @@ -329,10 +159,7 @@ export class RspackDevServer extends WebpackDevServer { compiler.options.builtins.react ??= {}; compiler.options.builtins.react.refresh ??= true; compiler.options.builtins.react.development ??= true; - compiler.options.builtins.provide ??= {}; - compiler.options.builtins.provide.$ReactRefreshRuntime$ ??= [ - require.resolve("@rspack/dev-client/react-refresh") - ]; + new ReactRefreshPlugin().apply(compiler); } else if (compiler.options.builtins.react.refresh) { if (mode === "production") { this.logger.warn( @@ -349,19 +176,11 @@ export class RspackDevServer extends WebpackDevServer { if (this.options.webSocketServer) { compilers.forEach(compiler => { + // @ts-expect-error: `addAdditionalEntries` is private function in base class. this.addAdditionalEntries(compiler); - - if (this.options.hot) { - compiler.options.module.rules.push({ - test: runtimePathRegexp, - type: "js" - }); - } - - compiler.options.builtins.provide = { - ...compiler.options.builtins.provide, - __webpack_dev_server_client__: [this.getClientTransport()] - }; + new compiler.webpack.ProvidePlugin({ + __webpack_dev_server_client__: this.getClientTransport() + }).apply(compiler); }); } diff --git a/packages/rspack-dev-server/tests/__snapshots__/normalizeOptions.test.ts.snap b/packages/rspack-dev-server/tests/__snapshots__/normalizeOptions.test.ts.snap index 96ec4e57c97..77c7fc6b02a 100644 --- a/packages/rspack-dev-server/tests/__snapshots__/normalizeOptions.test.ts.snap +++ b/packages/rspack-dev-server/tests/__snapshots__/normalizeOptions.test.ts.snap @@ -6,9 +6,9 @@ exports[`normalize options snapshot additional entires should added 1`] = ` "/./placeholder.js", ], "undefined": [ - "/rspack-dev-client/src/reactRefreshEntry.js", - "/webpack/hot/dev-server.js", + "/rspack-plugin-react-refresh/client/reactRefreshEntry.js", "/webpack-dev-server/client/index.js?protocol=ws%3A&hostname=0.0.0.0&&pathname=%2Fws&logging=info&overlay=true&reconnect=10&hot=true&live-reload=true", + "/webpack/hot/dev-server.js", ], } `; @@ -133,9 +133,9 @@ exports[`normalize options snapshot react-refresh client added when react/refres "/./placeholder.js", ], "undefined": [ - "/rspack-dev-client/src/reactRefreshEntry.js", - "/webpack/hot/dev-server.js", + "/rspack-plugin-react-refresh/client/reactRefreshEntry.js", "/webpack-dev-server/client/index.js?protocol=ws%3A&hostname=0.0.0.0&&pathname=%2Fws&logging=info&overlay=true&reconnect=10&hot=true&live-reload=true", + "/webpack/hot/dev-server.js", ], } `; diff --git a/packages/rspack-dev-server/tests/normalizeOptions.test.ts b/packages/rspack-dev-server/tests/normalizeOptions.test.ts index c72cccfd263..7951141955d 100644 --- a/packages/rspack-dev-server/tests/normalizeOptions.test.ts +++ b/packages/rspack-dev-server/tests/normalizeOptions.test.ts @@ -96,13 +96,7 @@ async function match(config: RspackOptions) { const compiler = createCompiler({ ...config, entry: ENTRY, - stats: "none", - infrastructureLogging: { - level: "info", - stream: { - write: () => {} - } - } + stats: "none" }); const server = new RspackDevServer( compiler.options.devServer ?? {}, @@ -123,12 +117,7 @@ async function matchAdditionEntries( const compiler = createCompiler({ ...config, stats: "none", - entry: ENTRY, - infrastructureLogging: { - stream: { - write: () => {} - } - } + entry: ENTRY }); const server = new RspackDevServer(serverConfig, compiler); diff --git a/packages/rspack-plugin-html/package.json b/packages/rspack-plugin-html/package.json index 0b70f356609..18ed78ce0c4 100644 --- a/packages/rspack-plugin-html/package.json +++ b/packages/rspack-plugin-html/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/plugin-html", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "HTML plugin for rspack", "main": "index.cjs", diff --git a/packages/rspack-plugin-minify/package.json b/packages/rspack-plugin-minify/package.json index 088a31e5657..95af1e1d0b7 100644 --- a/packages/rspack-plugin-minify/package.json +++ b/packages/rspack-plugin-minify/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/plugin-minify", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Minify plugin for rspack", "main": "src/index.js", diff --git a/packages/rspack-plugin-node-polyfill/package.json b/packages/rspack-plugin-node-polyfill/package.json index fce330c32e7..5754f26ca97 100644 --- a/packages/rspack-plugin-node-polyfill/package.json +++ b/packages/rspack-plugin-node-polyfill/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/plugin-node-polyfill", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "description": "Node polyfill plugin for rspack", "main": "src/index.js", diff --git a/packages/rspack-plugin-react-refresh/CHANGELOG.md b/packages/rspack-plugin-react-refresh/CHANGELOG.md new file mode 100644 index 00000000000..6c1499b0e1e --- /dev/null +++ b/packages/rspack-plugin-react-refresh/CHANGELOG.md @@ -0,0 +1 @@ +# @rspack/plugin-react-refresh diff --git a/packages/rspack-plugin-react-refresh/LICENSE b/packages/rspack-plugin-react-refresh/LICENSE new file mode 100644 index 00000000000..46310101ad8 --- /dev/null +++ b/packages/rspack-plugin-react-refresh/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2022-present Bytedance, Inc. and its affiliates. + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/rspack-plugin-react-refresh/README.md b/packages/rspack-plugin-react-refresh/README.md new file mode 100644 index 00000000000..35cdd301eed --- /dev/null +++ b/packages/rspack-plugin-react-refresh/README.md @@ -0,0 +1,16 @@ + + + Rspack Banner + + +# @rspack/plugin-react-refresh + +React refresh plugin for rspack. + +## Documentation + +See [https://rspack.dev](https://rspack.dev) for details. + +## License + +Rspack is [MIT licensed](https://github.com/web-infra-dev/rspack/blob/main/LICENSE). diff --git a/packages/rspack-plugin-react-refresh/client/reactRefresh.js b/packages/rspack-plugin-react-refresh/client/reactRefresh.js new file mode 100644 index 00000000000..7c24482448b --- /dev/null +++ b/packages/rspack-plugin-react-refresh/client/reactRefresh.js @@ -0,0 +1,23 @@ +// Thanks https://github.com/pmmmwh/react-refresh-webpack-plugin +const RefreshUtils = require("@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils"); +const RefreshRuntime = require("react-refresh/runtime"); + +// Port from https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/loader/utils/getRefreshModuleRuntime.js#L29 +function refresh(moduleId, webpackHot) { + const currentExports = RefreshUtils.getModuleExports(moduleId); + const fn = exports => { + RefreshUtils.executeRuntime(exports, moduleId, webpackHot); + }; + if (typeof Promise !== "undefined" && currentExports instanceof Promise) { + currentExports.then(fn); + } else { + fn(currentExports); + } +} + +module.exports = { + refresh, + register: RefreshRuntime.register, + createSignatureFunctionForTransform: + RefreshRuntime.createSignatureFunctionForTransform +}; diff --git a/packages/rspack-plugin-react-refresh/client/reactRefreshEntry.js b/packages/rspack-plugin-react-refresh/client/reactRefreshEntry.js new file mode 100644 index 00000000000..26eb38307b3 --- /dev/null +++ b/packages/rspack-plugin-react-refresh/client/reactRefreshEntry.js @@ -0,0 +1,4 @@ +const RefreshRuntime = require("react-refresh/runtime"); + +RefreshRuntime.injectIntoGlobalHook(self); +self.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform; diff --git a/packages/rspack-plugin-react-refresh/package.json b/packages/rspack-plugin-react-refresh/package.json new file mode 100644 index 00000000000..bde2c1bd0e8 --- /dev/null +++ b/packages/rspack-plugin-react-refresh/package.json @@ -0,0 +1,42 @@ +{ + "name": "@rspack/plugin-react-refresh", + "version": "0.3.2", + "license": "MIT", + "description": "React refresh plugin for rspack", + "main": "src/index.js", + "exports": { + ".": "./src/index.js", + "./react-refresh": "./client/reactRefresh.js", + "./react-refresh-entry": "./client/reactRefreshEntry.js" + }, + "scripts": { + "test": "echo success" + }, + "files": [ + "src" + ], + "publishConfig": { + "access": "public" + }, + "homepage": "https://rspack.dev", + "bugs": "https://github.com/web-infra-dev/rspack/issues", + "repository": { + "type": "git", + "url": "https://github.com/web-infra-dev/rspack", + "directory": "packages/rspack-plugin-react-refresh" + }, + "dependencies": { + "@pmmmwh/react-refresh-webpack-plugin": "0.5.10" + }, + "devDependencies": { + "react-refresh": "0.14.0" + }, + "peerDependencies": { + "react-refresh": ">=0.10.0 <1.0.0" + }, + "peerDependenciesMeta": { + "react-refresh": { + "optional": true + } + } +} diff --git a/packages/rspack-plugin-react-refresh/src/index.js b/packages/rspack-plugin-react-refresh/src/index.js new file mode 100644 index 00000000000..92b057de0ed --- /dev/null +++ b/packages/rspack-plugin-react-refresh/src/index.js @@ -0,0 +1,35 @@ +const path = require("path"); +const reactRefreshPath = require.resolve("../client/reactRefresh.js"); +const reactRefreshEntryPath = require.resolve("../client/reactRefreshEntry.js"); +const refreshUtilsPath = require.resolve( + "@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils", + { + paths: [reactRefreshPath] + } +); +const refreshRuntimeDirPath = path.dirname( + require.resolve("react-refresh", { + paths: [reactRefreshPath] + }) +); +const runtimePaths = [ + reactRefreshPath, + refreshUtilsPath, + refreshRuntimeDirPath +]; + +module.exports = class ReactRefreshRspackPlugin { + apply(compiler) { + new compiler.webpack.EntryPlugin(compiler.context, reactRefreshEntryPath, { + name: undefined + }).apply(compiler); + new compiler.webpack.ProvidePlugin({ + $ReactRefreshRuntime$: reactRefreshPath + }).apply(compiler); + + compiler.options.module.rules.push({ + include: runtimePaths, + type: "js" + }); + } +}; diff --git a/packages/rspack/package.json b/packages/rspack/package.json index 62d70207550..71fc60fd230 100644 --- a/packages/rspack/package.json +++ b/packages/rspack/package.json @@ -1,6 +1,6 @@ { "name": "@rspack/core", - "version": "0.3.1", + "version": "0.3.2", "webpackVersion": "5.75.0", "license": "MIT", "description": "A Fast Rust-based Web Bundler", @@ -14,10 +14,8 @@ }, "scripts": { "build": "tsc -b ./tsconfig.build.json", - "prepare": "pnpm precompile-schema", "dev": "tsc -w", - "test": "node --expose-gc --max-old-space-size=8192 --experimental-vm-modules ../../node_modules/jest-cli/bin/jest --runInBand --logHeapUsage", - "precompile-schema": "node ./scripts/precompile-schema.js" + "test": "cross-env NO_COLOR=1 node --expose-gc --max-old-space-size=8192 --experimental-vm-modules ../../node_modules/jest-cli/bin/jest --runInBand --logHeapUsage" }, "files": [ "dist" @@ -36,10 +34,10 @@ "@types/watchpack": "^2.4.0", "@types/webpack-sources": "3.2.0", "@types/ws": "8.5.3", - "ajv": "^8.12.0", "babel-loader": "^9.1.0", "babel-plugin-import": "^1.13.5", "copy-webpack-plugin": "5", + "cross-env": "^7.0.3", "file-loader": "^6.2.0", "jest-serializer-path": "^0.1.15", "less": "4.1.3", @@ -56,7 +54,6 @@ }, "dependencies": { "@rspack/binding": "workspace:*", - "@rspack/dev-client": "workspace:*", "@swc/helpers": "0.5.1", "browserslist": "^4.21.3", "compare-versions": "6.0.0-rc.1", diff --git a/packages/rspack/scripts/precompile-schema.js b/packages/rspack/scripts/precompile-schema.js deleted file mode 100644 index 2e4417b3406..00000000000 --- a/packages/rspack/scripts/precompile-schema.js +++ /dev/null @@ -1,99 +0,0 @@ -const fs = require("fs"); -const { default: Ajv, _, Name } = require("ajv"); -const path = require("path"); -const standaloneCode = require("ajv/dist/standalone").default; -const terser = require("terser"); - -const configDir = path.resolve(__dirname, "../src/config"); -const configSchema = path.resolve(configDir, "./schema.js"); -const configCheck = path.resolve(configDir, "./schema.check.js"); - -const ajv = new Ajv({ - code: { source: true, optimize: true }, - strictNumbers: false, - logger: false -}); - -ajv.addKeyword({ - keyword: "instanceof", - schemaType: "string", - code(ctx) { - const { data, schema } = ctx; - ctx.fail(_`!(${data} instanceof ${new Name(schema)})`); - } -}); - -ajv.addKeyword({ - keyword: "absolutePath", - type: "string", - schemaType: "boolean", - - code(ctx) { - const { data, schema } = ctx; - ctx.fail( - _`${data}.includes("!") || (absolutePathRegExp.test(${data}) !== ${schema})` - ); - } -}); - -ajv.removeKeyword("minLength"); -ajv.addKeyword({ - keyword: "minLength", - type: "string", - schemaType: "number", - - code(ctx) { - const { data, schema } = ctx; - if (schema !== 1) - throw new Error("Schema precompilation only supports minLength: 1"); - ctx.fail(_`${data}.length < 1`); - } -}); - -ajv.removeKeyword("enum"); -ajv.addKeyword({ - keyword: "enum", - schemaType: "array", - $data: true, - - code(ctx) { - const { data, schema } = ctx; - for (const item of schema) { - if (typeof item === "object" && item !== null) { - throw new Error( - `Schema precompilation only supports primitive values in enum: ${JSON.stringify( - item, - null, - 2 - )}` - ); - } - } - ctx.fail( - schema.map(x => _`${data} !== ${x}`).reduce((a, b) => _`${a} && ${b}`) - ); - } -}); - -const validate = ajv.compile(require(configSchema)); -const code = standaloneCode(ajv, validate); -const generated = - "/** This file was automatically generated, Run `pnpm precompile-schema` to update */\n" + - code; -fs.writeFileSync(configCheck, generated); - -// terser -// .minify(code, { -// compress: { -// passes: 3 -// }, -// mangle: true, -// ecma: 2015, -// toplevel: true -// }) -// .then(minified => { -// const code = -// "/** This file was automatically generated, Run `pnpm precompile-schema` to update */\n" + -// minified.code; -// fs.promises.writeFile(configCheck, code); -// }); diff --git a/packages/rspack/src/Compilation.ts b/packages/rspack/src/Compilation.ts index ad490f96f0e..14e761a0d44 100644 --- a/packages/rspack/src/Compilation.ts +++ b/packages/rspack/src/Compilation.ts @@ -304,6 +304,8 @@ export class Compilation { options.modulesSpace = options.modulesSpace || (context.forToString ? 15 : Infinity); + options.ids = optionOrLocalFallback(options.ids, !context.forToString); + return options; } diff --git a/packages/rspack/src/Compiler.ts b/packages/rspack/src/Compiler.ts index 6aef519cba8..06eab2a2c1e 100644 --- a/packages/rspack/src/Compiler.ts +++ b/packages/rspack/src/Compiler.ts @@ -834,7 +834,7 @@ class Compiler { (loaderContext: any) => { loaderContext._module = { factoryMeta: { - sideEffectFree: !resolveData.factoryMeta.sideEffects + sideEffectFree: !!resolveData.factoryMeta.sideEffectFree } }; } diff --git a/packages/rspack/src/Stats.ts b/packages/rspack/src/Stats.ts index 63eafc8b875..c3a0a9885b1 100644 --- a/packages/rspack/src/Stats.ts +++ b/packages/rspack/src/Stats.ts @@ -41,9 +41,8 @@ export class Stats { const statsFactory = this.compilation.createStatsFactory(options); // FIXME: This is a really ugly workaround for avoid panic for accessing previous compilation. - // webpack-dev-server and Modern.js dev server will detect whether the returned stats is available. + // Modern.js dev server will detect whether the returned stats is available. // So this does not do harm to these frameworks. - // webpack-dev-server: https://github.com/webpack/webpack-dev-server/blob/540c43852ea33f9cb18820e1cef05d5ddb86cc3e/lib/Server.js#L3222 // Modern.js: https://github.com/web-infra-dev/modern.js/blob/63f916f882f7d16096949e264e119218c0ab8d7d/packages/server/server/src/dev-tools/dev-middleware/socketServer.ts#L172 let stats: StatsCompilation | null = null; try { @@ -69,9 +68,8 @@ export class Stats { const statsPrinter = this.compilation.createStatsPrinter(options); // FIXME: This is a really ugly workaround for avoid panic for accessing previous compilation. - // webpack-dev-server and Modern.js dev server will detect whether the returned stats is available. + // Modern.js dev server will detect whether the returned stats is available. // So this does not do harm to these frameworks. - // webpack-dev-server: https://github.com/webpack/webpack-dev-server/blob/540c43852ea33f9cb18820e1cef05d5ddb86cc3e/lib/Server.js#L3222 // Modern.js: https://github.com/web-infra-dev/modern.js/blob/63f916f882f7d16096949e264e119218c0ab8d7d/packages/server/server/src/dev-tools/dev-middleware/socketServer.ts#L172 let stats: StatsCompilation | null = null; try { diff --git a/packages/rspack/src/builtin-loader/index.ts b/packages/rspack/src/builtin-loader/index.ts new file mode 100644 index 00000000000..7bc26342b0d --- /dev/null +++ b/packages/rspack/src/builtin-loader/index.ts @@ -0,0 +1 @@ +export * from "./swc"; diff --git a/packages/rspack/src/builtin-loader/swc/emotion.ts b/packages/rspack/src/builtin-loader/swc/emotion.ts new file mode 100644 index 00000000000..09e7edf28ea --- /dev/null +++ b/packages/rspack/src/builtin-loader/swc/emotion.ts @@ -0,0 +1,46 @@ +type EmotionConfigImportMap = { + [packageName: string]: { + [exportName: string]: { + canonicalImport?: [string, string]; + }; + }; +}; + +type EmotionConfig = { + sourceMap?: boolean; + autoLabel?: "never" | "dev-only" | "always"; + labelFormat?: string; + importMap?: EmotionConfigImportMap; +}; + +type EmotionOptions = boolean | EmotionConfig | undefined; + +function resolveEmotion( + emotion: EmotionOptions, + isProduction: boolean +): EmotionConfig | undefined { + if (!emotion) { + return undefined; + } + + if (emotion === true) { + emotion = {}; + } + + const autoLabel = emotion?.autoLabel ?? "dev-only"; + + const emotionConfig: EmotionConfig = { + enabled: true, + // @ts-expect-error autoLabel is string for JavaScript interface, however is boolean for Rust interface + autoLabel: + autoLabel === "dev-only" ? !isProduction : autoLabel === "always", + importMap: emotion?.importMap, + labelFormat: emotion?.labelFormat ?? "[local]", + sourcemap: isProduction ? false : emotion?.sourceMap ?? true + }; + + return emotionConfig; +} + +export { resolveEmotion }; +export type { EmotionOptions }; diff --git a/packages/rspack/src/builtin-loader/swc/index.ts b/packages/rspack/src/builtin-loader/swc/index.ts new file mode 100644 index 00000000000..eaa67b59154 --- /dev/null +++ b/packages/rspack/src/builtin-loader/swc/index.ts @@ -0,0 +1,11 @@ +export { resolveEmotion } from "./emotion"; +export type { EmotionOptions } from "./emotion"; + +export { resolveReact } from "./react"; +export type { ReactOptions } from "./react"; + +export { resolveRelay } from "./relay"; +export type { RelayOptions } from "./relay"; + +export { resolvePluginImport } from "./pluginImport"; +export type { PluginImportOptions } from "./pluginImport"; diff --git a/packages/rspack/src/builtin-loader/swc/pluginImport.ts b/packages/rspack/src/builtin-loader/swc/pluginImport.ts new file mode 100644 index 00000000000..7050b3d0992 --- /dev/null +++ b/packages/rspack/src/builtin-loader/swc/pluginImport.ts @@ -0,0 +1,48 @@ +import { RawPluginImportConfig } from "@rspack/binding"; + +type PluginImportConfig = { + libraryName: string; + libraryDirectory?: string; + customName?: string; + customStyleName?: string; + style?: string | boolean; + styleLibraryDirectory?: string; + camelToDashComponentName?: boolean; + transformToDefaultImport?: boolean; + ignoreEsComponent?: Array; + ignoreStyleComponent?: Array; +}; + +type PluginImportOptions = PluginImportConfig[] | undefined; + +function resolvePluginImport( + pluginImport: PluginImportOptions +): RawPluginImportConfig[] | undefined { + if (!pluginImport) { + return undefined; + } + + return pluginImport.map(config => { + const rawConfig: RawPluginImportConfig = { + ...config, + style: {} // As babel-plugin-import style config is very flexible, we convert it to a more specific structure + }; + + if (typeof config.style === "boolean") { + rawConfig.style!.bool = config.style; + } else if (typeof config.style === "string") { + const isTpl = config.style.includes("{{"); + rawConfig.style![isTpl ? "custom" : "css"] = config.style; + } + + // This option will overrides the behavior of style + if (config.styleLibraryDirectory) { + rawConfig.style = { styleLibraryDirectory: config.styleLibraryDirectory }; + } + + return rawConfig; + }); +} + +export { resolvePluginImport }; +export type { PluginImportOptions }; diff --git a/packages/rspack/src/builtin-loader/swc/react.ts b/packages/rspack/src/builtin-loader/swc/react.ts new file mode 100644 index 00000000000..c2fd2d176ba --- /dev/null +++ b/packages/rspack/src/builtin-loader/swc/react.ts @@ -0,0 +1,10 @@ +import { RawReactOptions } from "@rspack/binding"; + +function resolveReact(react: ReactOptions): RawReactOptions { + return react ?? {}; +} + +type ReactOptions = RawReactOptions | undefined; + +export { resolveReact }; +export type { ReactOptions }; diff --git a/packages/rspack/src/builtin-loader/swc/relay.ts b/packages/rspack/src/builtin-loader/swc/relay.ts new file mode 100644 index 00000000000..696176d4c79 --- /dev/null +++ b/packages/rspack/src/builtin-loader/swc/relay.ts @@ -0,0 +1,60 @@ +import path from "node:path"; + +import { RawRelayConfig } from "@rspack/binding"; + +type RelayOptions = boolean | RawRelayConfig | undefined; + +function getRelayConfigFromProject( + rootDir: string +): RawRelayConfig | undefined { + for (const configName of [ + "relay.config.json", + "relay.config.js", + "package.json" + ]) { + const configPath = path.join(rootDir, configName); + try { + let config = require(configPath) as + | Partial + | { relay?: Partial } + | undefined; + + let finalConfig: Partial | undefined; + if (configName === "package.json") { + finalConfig = (config as { relay?: Partial })?.relay; + } else { + finalConfig = config as Partial | undefined; + } + + if (finalConfig) { + return { + language: finalConfig.language!, + artifactDirectory: finalConfig.artifactDirectory + }; + } + } catch (_) {} + } +} + +function resolveRelay( + relay: RelayOptions, + rootDir: string +): RawRelayConfig | undefined { + if (!relay) { + return undefined; + } + + // Search relay config based on + if (relay === true) { + return ( + getRelayConfigFromProject(rootDir) || { + language: "javascript" + } + ); + } else { + return relay; + } +} + +export { resolveRelay }; +export type { RelayOptions }; diff --git a/packages/rspack/src/builtin-plugin/BannerPlugin.ts b/packages/rspack/src/builtin-plugin/BannerPlugin.ts index 7423d02eaea..efe3deb332d 100644 --- a/packages/rspack/src/builtin-plugin/BannerPlugin.ts +++ b/packages/rspack/src/builtin-plugin/BannerPlugin.ts @@ -1,25 +1,49 @@ +import { z } from "zod"; import { + JsChunk, RawBannerCondition, RawBannerConditions, - RawBannerConfig + RawBannerConfig, + RawBannerContent } from "@rspack/binding"; import { BuiltinPluginKind, create } from "./base"; -type BannerCondition = string | RegExp; -type BannerConditions = BannerCondition | BannerCondition[]; -export type BannerPluginOptions = - | string - | { - banner: string; - entryOnly?: boolean; - footer?: boolean; - raw?: boolean; - test?: BannerConditions; - exclude?: BannerConditions; - include?: BannerConditions; - }; +const rule = z.string().or(z.instanceof(RegExp)); +export type Rule = z.infer; -function getBannerCondition(condition: BannerCondition): RawBannerCondition { +const rules = rule.or(rule.array()); +export type Rules = z.infer; + +const bannerFunction = z + .function() + .args( + z.object({ + hash: z.string(), + chunk: z.custom(), + filename: z.string() + }) + ) + .returns(z.string()); +export type BannerFunction = z.infer; + +const bannerContent = z.string().or(bannerFunction); +export type BannerContent = z.infer; + +const bannerPluginOptions = z.strictObject({ + banner: bannerContent, + entryOnly: z.boolean().optional(), + exclude: rules.optional(), + include: rules.optional(), + raw: z.boolean().optional(), + footer: z.boolean().optional(), + test: rules.optional() +}); +export type BannerPluginOptions = z.infer; + +const bannerPluginArgument = bannerContent.or(bannerPluginOptions); +export type BannerPluginArgument = z.infer; + +function getRawBannerRule(condition: Rule): RawBannerCondition { if (typeof condition === "string") { return { type: "string", @@ -35,35 +59,57 @@ function getBannerCondition(condition: BannerCondition): RawBannerCondition { throw new Error("unreachable: condition should be one of string, RegExp"); } -function getBannerConditions( - condition?: BannerConditions -): RawBannerConditions | undefined { +function getRawBannerRules(condition?: Rules): RawBannerConditions | undefined { if (!condition) return undefined; if (Array.isArray(condition)) { return { type: "array", - arrayMatcher: condition.map(i => getBannerCondition(i)) + arrayMatcher: condition.map(i => getRawBannerRule(i)) }; } - return getBannerCondition(condition); + return getRawBannerRule(condition); +} + +function getRawBannerContent(content: BannerContent): RawBannerContent { + if (typeof content === "string") { + return { + type: "string", + stringPayload: content + }; + } + if (typeof content === "function") { + return { + type: "function", + fnPayload: content + }; + } + throw new Error("BannerContent should be a string or function"); } export const BannerPlugin = create( BuiltinPluginKind.Banner, - (bannerConfig: BannerPluginOptions): RawBannerConfig => { - if (typeof bannerConfig === "string") { + (args: BannerPluginArgument): RawBannerConfig => { + if (typeof args === "string") { + return { + banner: getRawBannerContent(args) + }; + } + if (typeof args === "function") { return { - banner: bannerConfig + banner: getRawBannerContent(args) }; } return { - ...bannerConfig, - test: getBannerConditions(bannerConfig.test), - include: getBannerConditions(bannerConfig.include), - exclude: getBannerConditions(bannerConfig.exclude) + banner: getRawBannerContent(args.banner), + entryOnly: args.entryOnly, + footer: args.footer, + raw: args.raw, + test: getRawBannerRules(args.test), + include: getRawBannerRules(args.include), + exclude: getRawBannerRules(args.exclude) }; } ); diff --git a/packages/rspack/src/builtin-plugin/ExternalsPlugin.ts b/packages/rspack/src/builtin-plugin/ExternalsPlugin.ts index d31c29775ac..e40fdd03dd6 100644 --- a/packages/rspack/src/builtin-plugin/ExternalsPlugin.ts +++ b/packages/rspack/src/builtin-plugin/ExternalsPlugin.ts @@ -21,9 +21,11 @@ export const ExternalsPlugin = create( function getRawExternalItem(item: ExternalItem): RawExternalItem { if (typeof item === "string") { return { type: "string", stringPayload: item }; - } else if (item instanceof RegExp) { + } + if (item instanceof RegExp) { return { type: "regexp", regexpPayload: item.source }; - } else if (typeof item === "function") { + } + if (typeof item === "function") { return { type: "function", fnPayload: async ctx => { @@ -34,7 +36,7 @@ function getRawExternalItem(item: ExternalItem): RawExternalItem { result: getRawExternalItemValueFormFnResult(result), external_type: type }); - }); + }) as Promise; if (promise && promise.then) { promise.then( result => diff --git a/packages/rspack/src/builtin-plugin/HtmlPlugin.ts b/packages/rspack/src/builtin-plugin/HtmlPlugin.ts index cd83422ae34..0a4f4471b68 100644 --- a/packages/rspack/src/builtin-plugin/HtmlPlugin.ts +++ b/packages/rspack/src/builtin-plugin/HtmlPlugin.ts @@ -1,12 +1,29 @@ +import { z } from "zod"; import { RawHtmlPluginConfig } from "@rspack/binding"; import { BuiltinPluginKind, create } from "./base"; +import { validate } from "../util/validate"; -export type HtmlPluginOptions = Omit & { - meta?: Record>; -}; +const htmlPluginOptions = z.strictObject({ + filename: z.string().optional(), + template: z.string().optional(), + templateContent: z.string().optional(), + templateParameters: z.record(z.string()).optional(), + inject: z.enum(["head", "body"]).optional(), + publicPath: z.string().optional(), + scriptLoading: z.enum(["blocking", "defer", "module"]).optional(), + chunks: z.string().array().optional(), + excludedChunks: z.string().array().optional(), + sri: z.enum(["sha256", "sha384", "sha512"]).optional(), + minify: z.boolean().optional(), + title: z.string().optional(), + favicon: z.string().optional(), + meta: z.record(z.string().or(z.record(z.string()))).optional() +}); +export type HtmlPluginOptions = z.infer; export const HtmlPlugin = create( BuiltinPluginKind.Html, (c: HtmlPluginOptions): RawHtmlPluginConfig => { + validate(c, htmlPluginOptions); const meta: Record> = {}; for (const key in c.meta) { const value = c.meta[key]; diff --git a/packages/rspack/src/builtin-plugin/ProgressPlugin.ts b/packages/rspack/src/builtin-plugin/ProgressPlugin.ts index ff8463feb65..49b2d8612cd 100644 --- a/packages/rspack/src/builtin-plugin/ProgressPlugin.ts +++ b/packages/rspack/src/builtin-plugin/ProgressPlugin.ts @@ -1,8 +1,8 @@ import { RawProgressPluginConfig } from "@rspack/binding"; import { BuiltinPluginKind, create } from "./base"; -export type ProgressPluginOptions = RawProgressPluginConfig | undefined; +export type ProgressPluginArgument = RawProgressPluginConfig | undefined; export const ProgressPlugin = create( BuiltinPluginKind.Progress, - (progress: ProgressPluginOptions = {}): RawProgressPluginConfig => progress + (progress: ProgressPluginArgument = {}): RawProgressPluginConfig => progress ); diff --git a/packages/rspack/src/builtin-plugin/SwcJsMinimizerPlugin.ts b/packages/rspack/src/builtin-plugin/SwcJsMinimizerPlugin.ts index 53505f668af..f4d5459882e 100644 --- a/packages/rspack/src/builtin-plugin/SwcJsMinimizerPlugin.ts +++ b/packages/rspack/src/builtin-plugin/SwcJsMinimizerPlugin.ts @@ -10,6 +10,8 @@ type MinifyConditions = MinifyCondition | MinifyCondition[]; export type SwcJsMinimizerPluginOptions = { passes?: number; dropConsole?: boolean; + keepClassNames?: boolean; + keepFnNames?: boolean; pureFuncs?: Array; extractComments?: boolean | RegExp; comments?: false | "all" | "some"; @@ -58,6 +60,8 @@ export const SwcJsMinimizerPlugin = create( return { passes: options?.passes ?? 1, dropConsole: options?.dropConsole ?? false, + keepClassNames: options?.keepClassNames ?? false, + keepFnNames: options?.keepFnNames ?? false, pureFuncs: options?.pureFuncs ?? [], comments: options?.comments ? options.comments : "false", asciiOnly: options?.asciiOnly ?? false, diff --git a/packages/rspack/src/builtin-plugin/base.ts b/packages/rspack/src/builtin-plugin/base.ts index a02235fc74f..cef64202d50 100644 --- a/packages/rspack/src/builtin-plugin/base.ts +++ b/packages/rspack/src/builtin-plugin/base.ts @@ -1,5 +1,5 @@ import * as binding from "@rspack/binding"; -import { Compiler } from ".."; +import { Compiler, RspackPluginInstance } from ".."; // TODO: workaround for https://github.com/napi-rs/napi-rs/pull/1690 export enum BuiltinPluginKind { @@ -18,7 +18,7 @@ export enum BuiltinPluginKind { HttpExternals = "HttpExternals" } -export abstract class RspackBuiltinPlugin { +export abstract class RspackBuiltinPlugin implements RspackPluginInstance { abstract raw(): binding.BuiltinPlugin; apply(compiler: Compiler) { compiler.__internal__registerBuiltinPlugin(this); diff --git a/packages/rspack/src/builtin-plugin/index.ts b/packages/rspack/src/builtin-plugin/index.ts index bfafacb4e71..2d3a43d7a90 100644 --- a/packages/rspack/src/builtin-plugin/index.ts +++ b/packages/rspack/src/builtin-plugin/index.ts @@ -20,9 +20,6 @@ import { RawDecoratorOptions, RawPresetEnv, RawProgressPluginConfig, - RawReactOptions, - RawRelayConfig, - RawPluginImportConfig, RawBuiltins, RawCssModulesConfig } from "@rspack/binding"; @@ -44,7 +41,16 @@ import { RspackBuiltinPlugin } from "."; import { loadConfig } from "browserslist"; -import path from "path"; +import { + EmotionOptions, + PluginImportOptions, + ReactOptions, + RelayOptions, + resolveEmotion, + resolvePluginImport, + resolveReact, + resolveRelay +} from "../builtin-loader/swc"; type BuiltinsCssConfig = { modules?: Partial; @@ -78,8 +84,6 @@ type PluginImportConfig = { ignoreStyleComponent?: Array; }; -type RelayConfig = boolean | RawRelayConfig; - function resolveTreeShaking( treeShaking: Builtins["treeShaking"], production: boolean @@ -125,132 +129,24 @@ function resolveDecorator( ); } -function resolveEmotion( - emotion: Builtins["emotion"], - isProduction: boolean -): string | undefined { - if (!emotion) { - return undefined; - } - - if (emotion === true) { - emotion = {}; - } - - const autoLabel = emotion?.autoLabel ?? "dev-only"; - - const emotionConfig: Builtins["emotion"] = { - enabled: true, - // @ts-expect-error autoLabel is string for JavaScript interface, however is boolean for Rust interface - autoLabel: - autoLabel === "dev-only" ? !isProduction : autoLabel === "always", - importMap: emotion?.importMap, - labelFormat: emotion?.labelFormat ?? "[local]", - sourcemap: isProduction ? false : emotion?.sourceMap ?? true - }; - - return JSON.stringify(emotionConfig); -} - -function resolvePluginImport( - pluginImport?: PluginImportConfig[] -): RawPluginImportConfig[] | undefined { - if (!pluginImport) { - return undefined; - } - - return pluginImport.map(config => { - const rawConfig: RawPluginImportConfig = { - ...config, - style: {} // As babel-plugin-import style config is very flexible, we convert it to a more specific structure - }; - - if (typeof config.style === "boolean") { - rawConfig.style!.bool = config.style; - } else if (typeof config.style === "string") { - const isTpl = config.style.includes("{{"); - rawConfig.style![isTpl ? "custom" : "css"] = config.style; - } - - // This option will overrides the behavior of style - if (config.styleLibraryDirectory) { - rawConfig.style = { styleLibraryDirectory: config.styleLibraryDirectory }; - } - - return rawConfig; - }); -} - -function resolveRelay( - relay: RelayConfig, - rootDir: string -): RawRelayConfig | undefined { - if (!relay) { - return undefined; - } - - // Search relay config based on - if (relay === true) { - return ( - getRelayConfigFromProject(rootDir) || { - language: "javascript" - } - ); - } else { - return relay; - } -} - -function getRelayConfigFromProject( - rootDir: string -): RawRelayConfig | undefined { - for (const configName of [ - "relay.config.json", - "relay.config.js", - "package.json" - ]) { - const configPath = path.join(rootDir, configName); - try { - let config = require(configPath) as - | Partial - | { relay?: Partial } - | undefined; - - let finalConfig: Partial | undefined; - if (configName === "package.json") { - finalConfig = (config as { relay?: Partial })?.relay; - } else { - finalConfig = config as Partial | undefined; - } - - if (finalConfig) { - return { - language: finalConfig.language!, - artifactDirectory: finalConfig.artifactDirectory - }; - } - } catch (_) {} - } -} - export interface Builtins { css?: BuiltinsCssConfig; treeShaking?: boolean | "module"; progress?: boolean | RawProgressPluginConfig; - react?: RawReactOptions; noEmitAssets?: boolean; define?: Record; provide?: Record; html?: Array; decorator?: boolean | Partial; minifyOptions?: SwcJsMinimizerPluginOptions; - emotion?: boolean | EmotionConfig; presetEnv?: Partial; devFriendlySplitChunks?: boolean; copy?: CopyPluginOptions; banner?: BannerPluginOptions | BannerPluginOptions[]; - pluginImport?: PluginImportConfig[]; - relay?: boolean | RawRelayConfig; + react?: ReactOptions; + pluginImport?: PluginImportOptions; + emotion?: EmotionOptions; + relay?: RelayOptions; } export function deprecated_resolveBuiltins( @@ -377,15 +273,15 @@ export function deprecated_resolveBuiltins( } : undefined, treeShaking: resolveTreeShaking(builtins.treeShaking, production), - react: builtins.react ?? {}, noEmitAssets: noEmitAssets, presetEnv: resolvePresetEnv(builtins.presetEnv, contextPath), decorator: resolveDecorator(builtins.decorator), - emotion: resolveEmotion(builtins.emotion, production), devFriendlySplitChunks: builtins.devFriendlySplitChunks ?? false, + react: resolveReact(builtins.react), pluginImport: resolvePluginImport(builtins.pluginImport), - relay: builtins.relay - ? resolveRelay(builtins.relay, contextPath) - : undefined + emotion: builtins.emotion + ? JSON.stringify(resolveEmotion(builtins.emotion, production)) + : undefined, + relay: resolveRelay(builtins.relay, contextPath) }; } diff --git a/packages/rspack/src/config/adapter.ts b/packages/rspack/src/config/adapter.ts index 3433715b2b6..fa2e7ad9cb8 100644 --- a/packages/rspack/src/config/adapter.ts +++ b/packages/rspack/src/config/adapter.ts @@ -14,12 +14,14 @@ import type { RawAssetResourceGeneratorOptions, RawIncrementalRebuild, RawModuleRuleUses, - RawFuncUseCtx + RawFuncUseCtx, + RawRspackFuture } from "@rspack/binding"; import assert from "assert"; import { Compiler } from "../Compiler"; import { normalizeStatsPreset } from "../Stats"; import { isNil } from "../util"; +import { parseResource } from "../util/identifier"; import { ComposeJsUseOptions, LoaderContext, @@ -27,14 +29,10 @@ import { } from "./adapterRuleUse"; import { CrossOriginLoading, - ExternalsPresets, LibraryOptions, - ModuleOptionsNormalized, Node, Optimization, - OutputNormalized, Resolve, - RspackOptionsNormalized, RuleSetCondition, RuleSetLogicalConditions, RuleSetRule, @@ -49,11 +47,18 @@ import { AssetParserOptions, ParserOptionsByModuleType, GeneratorOptionsByModuleType, + IncrementalRebuildOptions, + OptimizationSplitChunksOptions, + RspackFutureOptions +} from "./zod"; +import { ExperimentsNormalized, - IncrementalRebuildOptions -} from "./types"; -import { SplitChunksConfig } from "./zod/optimization/split-chunks"; -import { parseResource } from "../util/identifier"; + ModuleOptionsNormalized, + OutputNormalized, + RspackOptionsNormalized +} from "./normalization"; + +export type { LoaderContext }; export const getRawOptions = ( options: RspackOptionsNormalized, @@ -69,8 +74,9 @@ export const getRawOptions = ( "context, devtool, cache should not be nil after defaults" ); const devtool = options.devtool === false ? "" : options.devtool; + const mode = options.mode; return { - mode: options.mode, + mode, target: getRawTarget(options.target), context: options.context, output: getRawOutput(options.output), @@ -79,6 +85,7 @@ export const getRawOptions = ( module: getRawModule(options.module, { compiler, devtool, + mode, context: options.context }), devtool, @@ -646,7 +653,7 @@ function getRawOptimization( } function toRawSplitChunksOptions( - sc?: SplitChunksConfig + sc?: OptimizationSplitChunksOptions ): RawOptions["optimization"]["splitChunks"] | undefined { if (!sc) { return; @@ -725,7 +732,16 @@ function getRawExperiments( asyncWebAssembly, newSplitChunks, css, - rspackFuture + rspackFuture: getRawRspackFutureOptions(rspackFuture) + }; +} + +function getRawRspackFutureOptions( + future: RspackFutureOptions +): RawRspackFuture { + assert(!isNil(future.newResolver)); + return { + newResolver: future.newResolver }; } diff --git a/packages/rspack/src/config/adapterRuleUse.ts b/packages/rspack/src/config/adapterRuleUse.ts index a7297eee0c7..682fc1428b2 100644 --- a/packages/rspack/src/config/adapterRuleUse.ts +++ b/packages/rspack/src/config/adapterRuleUse.ts @@ -4,7 +4,6 @@ import type { RawModuleRuleUse, RawOptions } from "@rspack/binding"; -import assert from "assert"; import { ResolveRequest } from "enhanced-resolve"; import { Compiler } from "../Compiler"; @@ -16,15 +15,22 @@ import { RuleSetUse, RuleSetUseItem, RuleSetLoaderWithOptions -} from "./types"; +} from "./zod"; import { parsePathQueryFragment } from "../loader-runner"; import { isNil } from "../util"; +import { + resolveEmotion, + resolvePluginImport, + resolveReact, + resolveRelay +} from "../builtin-loader"; const BUILTIN_LOADER_PREFIX = "builtin:"; export interface ComposeJsUseOptions { devtool: RawOptions["devtool"]; context: RawOptions["context"]; + mode: RawOptions["mode"]; compiler: Compiler; } @@ -220,6 +226,59 @@ export function createRawModuleRuleUses( return createRawModuleRuleUsesImpl(allUses, path, options); } +type GetLoaderOptions = ( + o: RuleSetLoaderWithOptions["options"], + options: ComposeJsUseOptions +) => RuleSetLoaderWithOptions["options"]; + +const getSassLoaderOptions: GetLoaderOptions = o => { + (o ??= {} as any).__exePath = require.resolve( + `sass-embedded-${process.platform}-${ + process.arch + }/dart-sass-embedded/dart-sass-embedded${ + process.platform === "win32" ? ".bat" : "" + }` + ); + return o; +}; + +const getSwcLoaderOptions: GetLoaderOptions = (o, options) => { + if (o && typeof o === "object" && o.rspackExperiments) { + let expr = o.rspackExperiments; + const contextPath = options.context!; + const production = options.mode === "production" || !options.mode; + if (expr.emotion) { + expr.emotion = resolveEmotion(expr.emotion, production); + } + if (expr.relay) { + expr.relay = resolveRelay(expr.relay, contextPath); + } + if (expr.import || expr.pluginImport) { + expr.import = resolvePluginImport(expr.import || expr.pluginImport); + } + if (expr.react) { + expr.react = resolveReact(expr.react); + } + } + return o; +}; + +function getBuiltinLoaderOptions( + identifier: string, + o: RuleSetLoaderWithOptions["options"], + options: ComposeJsUseOptions +): RuleSetLoaderWithOptions["options"] { + if (identifier.startsWith(`${BUILTIN_LOADER_PREFIX}sass-loader`)) { + return getSassLoaderOptions(o, options); + } + + if (identifier.startsWith(`${BUILTIN_LOADER_PREFIX}swc-loader`)) { + return getSwcLoaderOptions(o, options); + } + + return o; +} + function createRawModuleRuleUsesImpl( uses: RuleSetLoaderWithOptions[], path: string, @@ -232,16 +291,7 @@ function createRawModuleRuleUsesImpl( return uses.map((use, index) => { let o; if (use.loader.startsWith(BUILTIN_LOADER_PREFIX)) { - o = use.options; - if (use.loader === `${BUILTIN_LOADER_PREFIX}sass-loader`) { - (o ??= {} as any).__exePath = require.resolve( - `sass-embedded-${process.platform}-${ - process.arch - }/dart-sass-embedded/dart-sass-embedded${ - process.platform === "win32" ? ".bat" : "" - }` - ); - } + o = getBuiltinLoaderOptions(use.loader, use.options, options); o = isNil(o) ? undefined : typeof o === "string" ? o : JSON.stringify(o); } diff --git a/packages/rspack/src/config/defaults.ts b/packages/rspack/src/config/defaults.ts index 33684e8142d..d24bb093faa 100644 --- a/packages/rspack/src/config/defaults.ts +++ b/packages/rspack/src/config/defaults.ts @@ -13,39 +13,38 @@ import fs from "fs"; import path from "path"; import { isNil } from "../util"; import { cleverMerge } from "../util/cleverMerge"; -import { deprecated_resolveBuiltins } from "../builtin-plugin"; import { getDefaultTarget, getTargetProperties, getTargetsProperties } from "./target"; import type { - AvailableTarget, + Target, Context, - Entry, - EntryDescription, - EntryDescriptionNormalized, - EntryNormalized, - ExperimentsNormalized, ExternalsPresets, InfrastructureLogging, Mode, ModuleOptions, Node, Optimization, - OutputNormalized, ResolveOptions, - RspackOptionsNormalized, RuleSetRules, SnapshotOptions -} from "./types"; +} from "./zod"; +import { + EntryDescriptionNormalized, + EntryNormalized, + ExperimentsNormalized, + OutputNormalized, + RspackOptionsNormalized +} from "./normalization"; export const applyRspackOptionsDefaults = ( options: RspackOptionsNormalized ) => { F(options, "context", () => process.cwd()); F(options, "target", () => { - return getDefaultTarget(options.context!) as AvailableTarget; + return getDefaultTarget(options.context!); }); const { mode, target } = options; @@ -153,18 +152,16 @@ const applyExperimentsDefaults = ( experiments: ExperimentsNormalized, { cache }: { cache: boolean } ) => { - D(experiments, "incrementalRebuild", {}); D(experiments, "lazyCompilation", false); D(experiments, "asyncWebAssembly", false); D(experiments, "newSplitChunks", true); D(experiments, "css", true); // we not align with webpack about the default value for better DX - D(experiments, "rspackFuture", {}); + D(experiments, "incrementalRebuild", {}); if (typeof experiments.incrementalRebuild === "object") { D(experiments.incrementalRebuild, "make", true); D(experiments.incrementalRebuild, "emitAsset", true); } - if ( cache === false && experiments.incrementalRebuild && @@ -173,6 +170,11 @@ const applyExperimentsDefaults = ( experiments.incrementalRebuild.make = false; // TODO: use logger to warn user enable cache for incrementalRebuild.make } + + D(experiments, "rspackFuture", {}); + if (typeof experiments.rspackFuture === "object") { + D(experiments.rspackFuture, "newResolver", false); + } }; const applySnapshotDefaults = ( diff --git a/packages/rspack/src/config/index.ts b/packages/rspack/src/config/index.ts index 3cbb724a67d..9c9dac3ac3c 100644 --- a/packages/rspack/src/config/index.ts +++ b/packages/rspack/src/config/index.ts @@ -1,34 +1,4 @@ -import { configSchema } from "./zod"; -import { fromZodError } from "zod-validation-error"; - export * from "./normalization"; -export * from "./types"; +export * from "./zod"; export * from "./defaults"; export * from "./adapter"; - -export function validateConfig(opts: any) { - const res = configSchema().safeParse(opts); - if (!res.success) { - const strategy = process.env.RSPACK_CONFIG_VALIDATE ?? "strict"; - if (strategy === "loose-silent") return; - const issueSeparator = "$issue$"; - const prefixSeparator = "$prefix$"; - const validationErr = fromZodError(res.error, { - prefix: "Configuration error", - prefixSeparator, - issueSeparator - }); - // The output validationErr.message looks like - // `Configuration error$prefix$xxxx error$issue$yyy error$issue$zzz error` - const [prefix, reason] = validationErr.message.split(prefixSeparator); - const reasonItem = reason.split(issueSeparator); - const friendlyErr = new Error( - `${prefix}:\n${reasonItem.map(item => `- ${item}`).join("\n")}` - ); - if (strategy === "loose") { - console.error(friendlyErr.message); - } else { - throw friendlyErr; - } - } -} diff --git a/packages/rspack/src/config/normalization.ts b/packages/rspack/src/config/normalization.ts index 49f7dc6d93d..c90ce038e8a 100644 --- a/packages/rspack/src/config/normalization.ts +++ b/packages/rspack/src/config/normalization.ts @@ -8,17 +8,73 @@ * https://github.com/webpack/webpack/blob/main/LICENSE */ +import { Compilation } from ".."; import { deprecatedWarn } from "../util"; import type { + Context, + Dependencies, + Node, + DevTool, EntryStatic, - EntryStaticNormalized, + Externals, + ExternalsPresets, + ExternalsType, + InfrastructureLogging, LibraryOptions, + Mode, + Name, OptimizationRuntimeChunk, - OptimizationRuntimeChunkNormalized, + Resolve, RspackOptions, - RspackOptionsNormalized -} from "./types"; -import { OptimizationRuntimeChunkConfig } from "./zod/optimization"; + Target, + SnapshotOptions, + CacheOptions, + StatsValue, + Optimization, + Plugins, + Watch, + WatchOptions, + DevServer, + Profile, + Builtins, + EntryRuntime, + ChunkLoading, + PublicPath, + EntryFilename, + Path, + Clean, + Filename, + ChunkFilename, + CrossOriginLoading, + CssFilename, + CssChunkFilename, + HotUpdateMainFilename, + HotUpdateChunkFilename, + AssetModuleFilename, + UniqueName, + ChunkLoadingGlobal, + EnabledLibraryTypes, + OutputModule, + StrictModuleErrorHandling, + GlobalObject, + ImportFunctionName, + Iife, + WasmLoading, + EnabledWasmLoadingTypes, + WebassemblyModuleFilename, + TrustedTypes, + SourceMapFilename, + HashDigest, + HashDigestLength, + HashFunction, + HashSalt, + WorkerPublicPath, + RuleSetRules, + ParserOptionsByModuleType, + GeneratorOptionsByModuleType, + IncrementalRebuildOptions, + RspackFutureOptions +} from "./zod"; export const getNormalizedRspackOptions = ( config: RspackOptions @@ -28,9 +84,9 @@ export const getNormalizedRspackOptions = ( config.ignoreWarnings !== undefined ? config.ignoreWarnings.map(ignore => { if (typeof ignore === "function") { - return ignore as (...args: any[]) => boolean; + return ignore; } else { - return warning => { + return (warning: Error) => { return ignore.test(warning.message); }; } @@ -291,7 +347,7 @@ const getNormalizedEntryStatic = (entry: EntryStatic) => { }; const getNormalizedOptimizationRuntimeChunk = ( - runtimeChunk?: OptimizationRuntimeChunkConfig + runtimeChunk?: OptimizationRuntimeChunk ): OptimizationRuntimeChunkNormalized | undefined => { if (runtimeChunk === undefined) return undefined; if (runtimeChunk === false) return false; @@ -356,3 +412,116 @@ const keyedNestedConfig = ( } return result; }; + +export type EntryNormalized = EntryStaticNormalized; +export interface EntryStaticNormalized { + [k: string]: EntryDescriptionNormalized; +} +export interface EntryDescriptionNormalized { + import?: string[]; + runtime?: EntryRuntime; + chunkLoading?: ChunkLoading; + asyncChunks?: boolean; + publicPath?: PublicPath; + baseUri?: string; + filename?: EntryFilename; +} + +export interface OutputNormalized { + path?: Path; + clean?: Clean; + publicPath?: PublicPath; + filename?: Filename; + chunkFilename?: ChunkFilename; + crossOriginLoading?: CrossOriginLoading; + cssFilename?: CssFilename; + cssChunkFilename?: CssChunkFilename; + hotUpdateMainFilename?: HotUpdateMainFilename; + hotUpdateChunkFilename?: HotUpdateChunkFilename; + assetModuleFilename?: AssetModuleFilename; + uniqueName?: UniqueName; + chunkLoadingGlobal?: ChunkLoadingGlobal; + enabledLibraryTypes?: EnabledLibraryTypes; + library?: LibraryOptions; + module?: OutputModule; + strictModuleErrorHandling?: StrictModuleErrorHandling; + globalObject?: GlobalObject; + importFunctionName?: ImportFunctionName; + iife?: Iife; + wasmLoading?: WasmLoading; + enabledWasmLoadingTypes?: EnabledWasmLoadingTypes; + webassemblyModuleFilename?: WebassemblyModuleFilename; + chunkFormat?: string | false; + chunkLoading?: string | false; + enabledChunkLoadingTypes?: string[]; + trustedTypes?: TrustedTypes; + sourceMapFilename?: SourceMapFilename; + hashDigest?: HashDigest; + hashDigestLength?: HashDigestLength; + hashFunction?: HashFunction; + hashSalt?: HashSalt; + asyncChunks?: boolean; + workerChunkLoading?: ChunkLoading; + workerWasmLoading?: WasmLoading; + workerPublicPath?: WorkerPublicPath; +} + +export interface ModuleOptionsNormalized { + defaultRules?: RuleSetRules; + rules: RuleSetRules; + parser: ParserOptionsByModuleType; + generator: GeneratorOptionsByModuleType; +} + +export interface ExperimentsNormalized { + lazyCompilation?: boolean; + incrementalRebuild?: false | IncrementalRebuildOptions; + asyncWebAssembly?: boolean; + outputModule?: boolean; + newSplitChunks?: boolean; + css?: boolean; + futureDefaults?: boolean; + rspackFuture?: RspackFutureOptions; +} + +export type IgnoreWarningsNormalized = (( + warning: Error, + compilation: Compilation +) => boolean)[]; + +export type OptimizationRuntimeChunkNormalized = + | false + | { + name: (...args: any[]) => string | undefined; + }; + +export interface RspackOptionsNormalized { + name?: Name; + dependencies?: Dependencies; + context?: Context; + mode?: Mode; + entry: EntryNormalized; + output: OutputNormalized; + resolve: Resolve; + resolveLoader: Resolve; + module: ModuleOptionsNormalized; + target?: Target; + externals?: Externals; + externalsType?: ExternalsType; + externalsPresets: ExternalsPresets; + infrastructureLogging: InfrastructureLogging; + devtool?: DevTool; + node: Node; + snapshot: SnapshotOptions; + cache?: CacheOptions; + stats: StatsValue; + optimization: Optimization; + plugins: Plugins; + experiments: ExperimentsNormalized; + watch?: Watch; + watchOptions: WatchOptions; + devServer?: DevServer; + ignoreWarnings?: IgnoreWarningsNormalized; + profile?: Profile; + builtins: Builtins; +} diff --git a/packages/rspack/src/config/schema.js b/packages/rspack/src/config/schema.js deleted file mode 100644 index f8d2b0a8c7b..00000000000 --- a/packages/rspack/src/config/schema.js +++ /dev/null @@ -1,2335 +0,0 @@ -module.exports = { - definitions: { - AssetModuleFilename: { - description: - "The filename of asset modules as relative path inside the 'output.path' directory.", - anyOf: [ - { - type: "string" - } - ] - }, - AssetParserDataUrlOptions: { - description: "Options object for DataUrl condition.", - type: "object", - additionalProperties: false, - properties: { - maxSize: { - description: - "Maximum size of asset that should be inline as modules. Default: 8kb.", - type: "number" - } - } - }, - AssetParserOptions: { - description: "Parser options for asset modules.", - type: "object", - additionalProperties: false, - properties: { - dataUrlCondition: { - description: "The condition for inlining the asset as DataUrl.", - anyOf: [ - { - $ref: "#/definitions/AssetParserDataUrlOptions" - } - ] - } - } - }, - AuxiliaryComment: { - description: "Add a comment in the UMD wrapper.", - anyOf: [ - { - description: "Append the same comment above each import style.", - type: "string" - }, - { - $ref: "#/definitions/LibraryCustomUmdCommentObject" - } - ] - }, - CacheOptions: { - description: - "Cache generated modules and chunks to improve performance for multiple incremental builds.", - type: "boolean" - }, - ChunkFilename: { - description: - "Specifies the filename template of output files of non-initial chunks on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - oneOf: [ - { - $ref: "#/definitions/FilenameTemplate" - } - ] - }, - ChunkFormat: { - description: - "The format of chunks (formats included by default are 'array-push' (web/WebWorker), 'commonjs' (node.js), 'module' (ESM), but others might be added by plugins).", - anyOf: [ - { - enum: ["array-push", "commonjs", "module", false] - }, - { - type: "string" - } - ] - }, - ChunkLoading: { - description: - "The method of loading chunks (methods included by default are 'jsonp' (web), 'import' (ESM), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).", - anyOf: [ - { - enum: [false] - }, - { - $ref: "#/definitions/ChunkLoadingType" - } - ] - }, - ChunkLoadingType: { - description: - "The method of loading chunks (methods included by default are 'jsonp' (web), 'import' (ESM), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).", - anyOf: [ - { - enum: ["jsonp", "import-scripts", "require", "async-node", "import"] - }, - { - type: "string" - } - ] - }, - CrossOriginLoading: { - description: "This option enables cross-origin loading of chunks.", - enum: [false, "anonymous", "use-credentials"] - }, - Context: { - description: - "The base directory (absolute path!) for resolving the `entry` option. If `output.pathinfo` is set, the included pathinfo is shortened to this directory.", - type: "string" - }, - CssChunkFilename: { - description: - "Specifies the filename template of non-initial output css files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - oneOf: [ - { - $ref: "#/definitions/FilenameTemplate" - } - ] - }, - CssFilename: { - description: - "Specifies the filename template of output css files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - oneOf: [ - { - $ref: "#/definitions/FilenameTemplate" - } - ] - }, - HotUpdateChunkFilename: { - description: - "The filename of the Hot Update Chunks. They are inside the output.path directory.", - type: "string", - absolutePath: false - }, - HotUpdateMainFilename: { - description: - "The filename of the Hot Update Main File. It is inside the 'output.path' directory.", - type: "string", - absolutePath: false - }, - HashDigest: { - description: "Digest type used for the hash.", - type: "string" - }, - HashDigestLength: { - description: "Number of chars which are used for the hash.", - type: "number", - minimum: 1 - }, - HashFunction: { - description: - "Algorithm used for generation the hash (see node.js crypto package).", - anyOf: [ - { - type: "string", - minLength: 1 - }, - { - instanceof: "Function" - } - ] - }, - HashSalt: { - description: "Any string which is added to the hash to salt it.", - type: "string", - minLength: 1 - }, - WebassemblyModuleFilename: { - description: - "The filename of WebAssembly modules as relative path inside the 'output.path' directory.", - type: "string" - }, - EnabledWasmLoadingTypes: { - description: - "List of wasm loading types enabled for use by entry points.", - type: "array", - items: { - $ref: "#/definitions/WasmLoadingType" - } - }, - EnabledChunkLoadingTypes: { - description: - "List of chunk loading types enabled for use by entry points.", - type: "array", - items: { - $ref: "#/definitions/ChunkLoadingType" - } - }, - WasmLoading: { - description: - "The method of loading WebAssembly Modules (methods included by default are 'fetch' (web/WebWorker), 'async-node' (node.js), but others might be added by plugins).", - anyOf: [ - { - enum: [false] - }, - { - $ref: "#/definitions/WasmLoadingType" - } - ] - }, - WasmLoadingType: { - description: - "The method of loading WebAssembly Modules (methods included by default are 'fetch' (web/WebWorker), 'async-node' (node.js), but others might be added by plugins).", - anyOf: [ - { - enum: ["fetch-streaming", "fetch", "async-node"] - }, - { - type: "string" - } - ] - }, - Dependencies: { - description: "References to other configurations to depend on.", - type: "array", - items: { - description: "References to another configuration to depend on.", - type: "string" - } - }, - DevServer: { - description: "Options for the rspack-dev-server.", - type: "object" - }, - DevTool: { - description: - "A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map).", - anyOf: [ - { - enum: [false] - }, - { - type: "string", - pattern: - "^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$" - } - ] - }, - EnabledLibraryTypes: { - description: "List of library types enabled for use by entry points.", - type: "array", - items: { - $ref: "#/definitions/LibraryType" - } - }, - Entry: { - description: "The entry point(s) of the compilation.", - anyOf: [ - { - $ref: "#/definitions/EntryStatic" - } - ] - }, - EntryDescription: { - description: "An object with entry point description.", - type: "object", - additionalProperties: false, - properties: { - import: { - $ref: "#/definitions/EntryItem" - }, - runtime: { - $ref: "#/definitions/EntryRuntime" - }, - wasmLoading: { - $ref: "#/definitions/WasmLoading" - } - }, - required: ["import"] - }, - EntryFilename: { - description: - "Specifies the filename of the output file on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - oneOf: [ - { - $ref: "#/definitions/FilenameTemplate" - } - ] - }, - EntryItem: { - description: "Module(s) that are loaded upon startup.", - anyOf: [ - { - description: - "All modules are loaded upon startup. The last one is exported.", - type: "array", - items: { - description: - "A module that is loaded upon startup. Only the last one is exported.", - type: "string", - minLength: 1 - }, - minItems: 1, - uniqueItems: true - }, - { - description: - "The string is resolved to a module which is loaded upon startup.", - type: "string", - minLength: 1 - } - ] - }, - EntryObject: { - description: - "Multiple entry bundles are created. The key is the entry name. The value can be a string, an array or an entry description object.", - type: "object", - additionalProperties: { - description: "An entry point with name.", - anyOf: [ - { - $ref: "#/definitions/EntryItem" - }, - { - $ref: "#/definitions/EntryDescription" - } - ] - } - }, - EntryRuntime: { - description: - "The name of the runtime chunk. If set a runtime chunk with this name is created or an existing entrypoint is used as runtime.", - anyOf: [ - { - enum: [false] - }, - { - type: "string", - minLength: 1 - } - ] - }, - EntryStatic: { - description: "A static entry description.", - anyOf: [ - { - $ref: "#/definitions/EntryObject" - }, - { - $ref: "#/definitions/EntryUnnamed" - } - ] - }, - EntryUnnamed: { - description: "An entry point without name.", - oneOf: [ - { - $ref: "#/definitions/EntryItem" - } - ] - }, - Experiments: { - description: - "Enables/Disables experiments (experimental features with relax SemVer compatibility).", - type: "object", - additionalProperties: false, - properties: { - asyncWebAssembly: { - description: "Support WebAssembly as asynchronous EcmaScript Module.", - type: "boolean" - }, - incrementalRebuild: { - description: "Rebuild incrementally", - anyOf: [ - { - type: "boolean" - }, - { - type: "object", - properties: { - make: { - description: "Make stage enable incremental rebuild", - type: "boolean" - }, - emitAsset: { - description: "Emit asset enable incremental rebuild", - type: "boolean" - } - } - } - ] - }, - lazyCompilation: { - description: - "Compile entrypoints and import()s only when they are accessed.", - anyOf: [ - { - type: "boolean" - } - ] - }, - outputModule: { - description: "Allow output javascript files as module source type.", - type: "boolean" - }, - newSplitChunks: { - description: "Enable new SplitChunksPlugin", - type: "boolean" - }, - css: { - description: "Enable native css support.", - type: "boolean" - }, - rspackFuture: { - description: - "Enable default behavior in the future version of Rspack", - type: "object", - additionalProperties: false, - properties: {} - } - } - }, - ExternalItem: { - description: - "Specify dependency that shouldn't be resolved by rspack, but should become dependencies of the resulting bundle. The kind of the dependency depends on `output.libraryTarget`.", - anyOf: [ - { - description: "Every matched dependency becomes external.", - instanceof: "RegExp" - }, - { - description: - "An exact matched dependency becomes external. The same string is used as external dependency.", - type: "string" - }, - { - description: - "If an dependency matches exactly a property of the object, the property value is used as dependency.", - type: "object", - additionalProperties: { - $ref: "#/definitions/ExternalItemValue" - } - }, - { - description: - "The function is called on each dependency (`function(context, request, callback(err, result))`).", - instanceof: "Function" - } - ] - }, - ExternalItemValue: { - description: "The dependency used for the external.", - anyOf: [ - { - type: "array", - items: { - description: "A part of the target of the external.", - type: "string", - minLength: 1 - } - }, - { - description: "The target of the external.", - type: "string" - }, - { - description: - "`true`: The dependency name is used as target of the external.", - type: "boolean" - } - ] - }, - Externals: { - description: - "Specify dependencies that shouldn't be resolved by rspack, but should become dependencies of the resulting bundle. The kind of the dependency depends on `output.libraryTarget`.", - anyOf: [ - { - type: "array", - items: { - $ref: "#/definitions/ExternalItem" - } - }, - { - $ref: "#/definitions/ExternalItem" - } - ] - }, - ExternalsPresets: { - description: "Enable presets of externals for specific targets.", - type: "object", - additionalProperties: false, - properties: { - node: { - description: - "Treat node.js built-in modules like fs, path or vm as external and load them via require() when used.", - type: "boolean" - }, - web: { - description: - "Treat references to 'http(s)://...' and 'std:...' as external and load them via import when used (Note that this changes execution order as externals are executed before any other code in the chunk).", - type: "boolean" - }, - electron: { - description: - "Treat common electron built-in modules in main and preload context like 'electron', 'ipc' or 'shell' as external and load them via require() when used.", - type: "boolean" - }, - electronMain: { - description: - "Treat electron built-in modules in the main context like 'app', 'ipc-main' or 'shell' as external and load them via require() when used.", - type: "boolean" - }, - electronPreload: { - description: - "Treat electron built-in modules in the preload context like 'web-frame', 'ipc-renderer' or 'shell' as external and load them via require() when used.", - type: "boolean" - }, - electronRenderer: { - description: - "Treat electron built-in modules in the renderer context like 'web-frame', 'ipc-renderer' or 'shell' as external and load them via require() when used.", - type: "boolean" - } - } - }, - ExternalsType: { - description: - "Specifies the default type of externals ('amd*', 'umd*', 'system' and 'jsonp' depend on output.libraryTarget set to the same value).", - enum: [ - "var", - "module", - "assign", - "this", - "window", - "self", - "global", - "commonjs", - "commonjs2", - "commonjs-module", - "commonjs-static", - "amd", - "amd-require", - "umd", - "umd2", - "jsonp", - "system", - "promise", - "import", - "script", - "node-commonjs" - ] - }, - Filename: { - description: - "Specifies the filename of output files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - oneOf: [ - { - $ref: "#/definitions/FilenameTemplate" - } - ] - }, - SourceMapFilename: { - description: - "Specifies the filename of output files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - oneOf: [ - { - $ref: "#/definitions/FilenameTemplate" - } - ] - }, - FilenameTemplate: { - description: - "Specifies the filename template of output files on disk. You must **not** specify an absolute path here, but the path may contain folders separated by '/'! The specified path is joined with the value of the 'output.path' option to determine the location on disk.", - anyOf: [ - { - type: "string", - minLength: 1 - }, - { - instanceof: "Function" - } - ] - }, - FilterItemTypes: { - description: "Filtering value, regexp or function.", - anyOf: [ - { - instanceof: "RegExp" - }, - { - type: "string" - }, - { - instanceof: "Function" - } - ] - }, - FilterTypes: { - description: "Filtering values.", - anyOf: [ - { - type: "array", - items: { - description: "Rule to filter.", - oneOf: [ - { - $ref: "#/definitions/FilterItemTypes" - } - ] - } - }, - { - $ref: "#/definitions/FilterItemTypes" - } - ] - }, - GlobalObject: { - description: - "An expression which is used to address the global object/scope in runtime code.", - type: "string", - minLength: 1 - }, - ImportFunctionName: { - description: - "The name of the native import() function (can be exchanged for a polyfill).", - type: "string" - }, - InfrastructureLogging: { - description: "Options for infrastructure level logging.", - type: "object", - additionalProperties: false, - properties: { - appendOnly: { - description: - "Only appends lines to the output. Avoids updating existing output e. g. for status messages. This option is only used when no custom console is provided.", - type: "boolean" - }, - colors: { - description: - "Enables/Disables colorful output. This option is only used when no custom console is provided.", - type: "boolean" - }, - console: { - description: "Custom console used for logging." - }, - debug: { - description: "Enable debug logging for specific loggers.", - anyOf: [ - { - description: "Enable/Disable debug logging for all loggers.", - type: "boolean" - }, - { - $ref: "#/definitions/FilterTypes" - } - ] - }, - level: { - description: "Log level.", - enum: ["none", "error", "warn", "info", "log", "verbose"] - }, - stream: { - description: - "Stream used for logging output. Defaults to process.stderr. This option is only used when no custom console is provided." - } - } - }, - Library: { - description: - "Make the output files a library, exporting the exports of the entry point.", - anyOf: [ - { - $ref: "#/definitions/LibraryName" - }, - { - $ref: "#/definitions/LibraryOptions" - } - ] - }, - LibraryCustomUmdCommentObject: { - description: - "Set explicit comments for `commonjs`, `commonjs2`, `amd`, and `root`.", - type: "object", - additionalProperties: false, - properties: { - amd: { - description: "Set comment for `amd` section in UMD.", - type: "string" - }, - commonjs: { - description: "Set comment for `commonjs` (exports) section in UMD.", - type: "string" - }, - commonjs2: { - description: - "Set comment for `commonjs2` (module.exports) section in UMD.", - type: "string" - }, - root: { - description: - "Set comment for `root` (global variable) section in UMD.", - type: "string" - } - } - }, - LibraryCustomUmdObject: { - description: - "Description object for all UMD variants of the library name.", - type: "object", - additionalProperties: false, - properties: { - amd: { - description: "Name of the exposed AMD library in the UMD.", - type: "string", - minLength: 1 - }, - commonjs: { - description: "Name of the exposed commonjs export in the UMD.", - type: "string", - minLength: 1 - }, - root: { - description: - "Name of the property exposed globally by a UMD library.", - anyOf: [ - { - type: "array", - items: { - description: - "Part of the name of the property exposed globally by a UMD library.", - type: "string", - minLength: 1 - } - }, - { - type: "string", - minLength: 1 - } - ] - } - } - }, - LibraryExport: { - description: "Specify which export should be exposed as library.", - anyOf: [ - { - type: "array", - items: { - description: - "Part of the export that should be exposed as library.", - type: "string", - minLength: 1 - } - }, - { - type: "string", - minLength: 1 - } - ] - }, - LibraryName: { - description: - "The name of the library (some types allow unnamed libraries too).", - anyOf: [ - { - type: "array", - items: { - description: "A part of the library name.", - type: "string", - minLength: 1 - }, - minItems: 1 - }, - { - type: "string", - minLength: 1 - }, - { - $ref: "#/definitions/LibraryCustomUmdObject" - } - ] - }, - LibraryOptions: { - description: "Options for library.", - type: "object", - additionalProperties: false, - properties: { - auxiliaryComment: { - $ref: "#/definitions/AuxiliaryComment" - }, - export: { - $ref: "#/definitions/LibraryExport" - }, - name: { - $ref: "#/definitions/LibraryName" - }, - type: { - $ref: "#/definitions/LibraryType" - }, - umdNamedDefine: { - $ref: "#/definitions/UmdNamedDefine" - } - }, - required: ["type"] - }, - LibraryType: { - description: - "Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'commonjs-static', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).", - anyOf: [ - { - enum: [ - "var", - "module", - "assign", - "assign-properties", - "this", - "window", - "self", - "global", - "commonjs", - "commonjs2", - "commonjs-module", - "commonjs-static", - "amd", - "amd-require", - "umd", - "umd2", - "jsonp", - "system" - ] - }, - { - type: "string" - } - ] - }, - Mode: { - description: "Enable production optimizations or development hints.", - enum: ["development", "production", "none"] - }, - IgnoreWarnings: { - description: "ignore warnings based on pattern", - type: "array", - items: { - anyOf: [ - { - instanceof: "RegExp" - }, - { - instanceof: "Function" - } - ] - } - }, - ModuleOptions: { - description: - "Options affecting the normal modules (`NormalModuleFactory`).", - type: "object", - additionalProperties: false, - properties: { - defaultRules: { - description: "An array of rules applied by default for modules.", - oneOf: [ - { - $ref: "#/definitions/RuleSetRules" - } - ] - }, - parser: { - $ref: "#/definitions/ParserOptionsByModuleType" - }, - rules: { - description: "An array of rules applied for modules.", - oneOf: [ - { - $ref: "#/definitions/RuleSetRules" - } - ] - } - } - }, - Name: { - description: - "Name of the configuration. Used when loading multiple configurations.", - type: "string" - }, - Node: { - description: "Include polyfills or mocks for various node stuff.", - anyOf: [ - { - enum: [false] - }, - { - $ref: "#/definitions/NodeOptions" - } - ] - }, - NodeOptions: { - description: "Options object for node compatibility features.", - type: "object", - additionalProperties: false, - properties: { - __dirname: { - description: "Include a polyfill for the '__dirname' variable.", - enum: [false, true, "warn-mock", "mock", "eval-only"] - }, - __filename: { - description: "Include a polyfill for the '__filename' variable.", - enum: [false, true, "warn-mock", "mock", "eval-only"] - }, - global: { - description: "Include a polyfill for the 'global' variable.", - enum: [false, true, "warn"] - } - } - }, - Optimization: { - description: "Enables/Disables integrated optimizations.", - type: "object", - additionalProperties: false, - properties: { - chunkIds: { - description: - "Define the algorithm to choose chunk ids (named: readable ids for better debugging, deterministic: numeric hash ids for better long term caching, size: numeric ids focused on minimal initial download size, total-size: numeric ids focused on minimal total download size, false: no algorithm used, as custom one can be provided via plugin).", - enum: ["named", "deterministic"] - }, - minimize: { - description: - "Enable minimizing the output. Uses optimization.minimizer.", - type: "boolean" - }, - minimizer: { - description: "Minimizer(s) to use for minimizing the output.", - type: "array", - items: { - description: "Plugin of type object or instanceof Function.", - anyOf: [ - { - enum: ["..."] - }, - { - $ref: "#/definitions/RspackPluginInstance" - }, - { - $ref: "#/definitions/RspackPluginFunction" - } - ] - } - }, - moduleIds: { - description: - "Define the algorithm to choose module ids (natural: numeric ids in order of usage, named: readable ids for better debugging, hashed: (deprecated) short hashes as ids for better long term caching, deterministic: numeric hash ids for better long term caching, size: numeric ids focused on minimal initial download size, false: no algorithm used, as custom one can be provided via plugin).", - enum: ["named", "deterministic"] - }, - removeAvailableModules: { - description: - "Removes modules from chunks when these modules are already included in all parents.", - type: "boolean" - }, - removeEmptyChunks: { - description: "Remove chunks which are empty.", - type: "boolean" - }, - runtimeChunk: { - $ref: "#/definitions/OptimizationRuntimeChunk" - }, - sideEffects: { - description: - "Skip over modules which contain no side effects when exports are not used (false: disabled, 'flag': only use manually placed side effects flag, true: also analyse source code for side effects).", - anyOf: [ - { - enum: ["flag"] - }, - { - type: "boolean" - } - ] - }, - splitChunks: { - description: - "Optimize duplication and caching by splitting chunks by shared modules and cache group.", - anyOf: [ - { - enum: [false] - }, - { - $ref: "#/definitions/OptimizationSplitChunksOptions" - } - ] - }, - realContentHash: { - description: - "Use real [contenthash] based on final content of the assets.", - type: "boolean" - } - } - }, - OptimizationRuntimeChunk: { - description: - "Create an additional chunk which contains only the rspack runtime and chunk hash maps.", - anyOf: [ - { - enum: ["single", "multiple"] - }, - { - type: "boolean" - }, - { - type: "object", - additionalProperties: false, - properties: { - name: { - description: "The name or name factory for the runtime chunks.", - anyOf: [ - { - type: "string" - }, - { - instanceof: "Function" - } - ] - } - } - } - ] - }, - OptimizationSplitChunksCacheGroup: { - description: - "Options object for describing behavior of a cache group selecting modules that should be cached together.", - type: "object", - additionalProperties: false, - properties: { - chunks: { - description: - 'Select chunks for determining cache group content (defaults to "initial", "initial" and "all" requires adding these chunks to the HTML).', - anyOf: [ - { - enum: ["initial", "async", "all"] - }, - { - instanceof: "Function" - } - ] - }, - minChunks: { - description: - "Minimum number of times a module has to be duplicated until it's considered for splitting.", - type: "number", - minimum: 1 - }, - name: { - description: - "Give chunks for this cache group a name (chunks with equal name are merged).", - anyOf: [ - { - enum: [false] - }, - { - type: "string" - }, - { - instanceof: "Function" - } - ] - }, - priority: { - description: "Priority of this cache group.", - type: "number" - }, - reuseExistingChunk: { - description: - "Try to reuse existing chunk (with name) when it has matching modules.", - type: "boolean" - }, - enforce: { - description: - "ignore splitChunks.minSize, splitChunks.minChunks, splitChunks.maxAsyncRequests and splitChunks.maxInitialRequests options and always create chunks for this cache group.", - type: "boolean" - }, - hidePathInfo: { - type: "boolean" - }, - maxSize: { - type: "number" - }, - test: { - description: "Assign modules to a cache group by module name.", - anyOf: [ - { - instanceof: "RegExp" - } - ] - }, - minSize: { - description: "Minimal size for the created chunks.", - oneOf: [ - { - $ref: "#/definitions/OptimizationSplitChunksSizes" - } - ] - } - } - }, - OptimizationSplitChunksOptions: { - description: "Options object for splitting chunks into smaller chunks.", - type: "object", - additionalProperties: false, - properties: { - fallbackCacheGroup: { - type: "object", - properties: { - maxSize: { - type: "number" - }, - maxInitialSize: { - type: "number" - }, - maxAsyncSize: { - type: "number" - }, - minSize: { - type: "number" - } - } - }, - hidePathInfo: { - type: "boolean" - }, - name: { - description: "The name or name for chunks.", - anyOf: [ - { - type: "string" - } - ] - }, - cacheGroups: { - description: - "Assign modules to a cache group (modules from different cache groups are tried to keep in separate chunks, default categories: 'default', 'defaultVendors').", - type: "object", - additionalProperties: { - description: "Configuration for a cache group.", - anyOf: [ - { - $ref: "#/definitions/OptimizationSplitChunksCacheGroup" - } - ] - } - }, - chunks: { - description: - 'Select chunks for determining shared modules (defaults to "async", "initial" and "all" requires adding these chunks to the HTML).', - anyOf: [ - { - enum: ["initial", "async", "all"] - } - ] - }, - enforceSizeThreshold: { - description: - "Size threshold at which splitting is enforced and other restrictions (minRemainingSize, maxAsyncRequests, maxInitialRequests) are ignored.", - oneOf: [ - { - $ref: "#/definitions/OptimizationSplitChunksSizes" - } - ] - }, - maxAsyncRequests: { - description: - "Maximum number of requests which are accepted for on-demand loading.", - type: "number", - minimum: 1 - }, - maxInitialRequests: { - description: - "Maximum number of initial chunks which are accepted for an entry point.", - type: "number", - minimum: 1 - }, - minChunks: { - description: - "Minimum number of times a module has to be duplicated until it's considered for splitting.", - type: "number", - minimum: 1 - }, - minRemainingSize: { - description: - "Minimal size for the chunks the stay after moving the modules to a new chunk.", - oneOf: [ - { - $ref: "#/definitions/OptimizationSplitChunksSizes" - } - ] - }, - minSize: { - description: "Minimal size for the created chunks.", - oneOf: [ - { - $ref: "#/definitions/OptimizationSplitChunksSizes" - } - ] - }, - maxSize: { - type: "number" - }, - maxInitialSize: { - type: "number" - }, - maxAsyncSize: { - type: "number" - }, - reuseExistingChunk: { - description: - "If the current chunk contains modules already split out from the main bundle, it will be reused instead of a new one being generated. This can affect the resulting file name of the chunk.", - type: "boolean" - } - } - }, - OptimizationSplitChunksSizes: { - description: "Size description for limits.", - anyOf: [ - { - description: "Size of the javascript part of the chunk.", - type: "number", - minimum: 0 - } - ] - }, - Iife: { - description: - "Wrap javascript code into IIFE's to avoid leaking into global scope.", - type: "boolean" - }, - Clean: { - description: "Clears the output build directory", - type: "boolean" - }, - Output: { - description: - "Options affecting the output of the compilation. `output` options tell rspack how to write the compiled files to disk.", - type: "object", - additionalProperties: false, - properties: { - iife: { - $ref: "#/definitions/Iife" - }, - clean: { - $ref: "#/definitions/Clean" - }, - assetModuleFilename: { - $ref: "#/definitions/AssetModuleFilename" - }, - auxiliaryComment: { - oneOf: [ - { - $ref: "#/definitions/AuxiliaryComment" - } - ] - }, - chunkFormat: { - $ref: "#/definitions/ChunkFormat" - }, - chunkLoading: { - $ref: "#/definitions/ChunkLoading" - }, - enabledChunkLoadingTypes: { - $ref: "#/definitions/EnabledChunkLoadingTypes" - }, - chunkFilename: { - $ref: "#/definitions/ChunkFilename" - }, - crossOriginLoading: { - $ref: "#/definitions/CrossOriginLoading" - }, - cssChunkFilename: { - $ref: "#/definitions/CssChunkFilename" - }, - cssFilename: { - $ref: "#/definitions/CssFilename" - }, - hotUpdateChunkFilename: { - $ref: "#/definitions/HotUpdateChunkFilename" - }, - hotUpdateMainFilename: { - $ref: "#/definitions/HotUpdateMainFilename" - }, - enabledWasmLoadingTypes: { - $ref: "#/definitions/EnabledWasmLoadingTypes" - }, - wasmLoading: { - $ref: "#/definitions/WasmLoading" - }, - webassemblyModuleFilename: { - $ref: "#/definitions/WebassemblyModuleFilename" - }, - enabledLibraryTypes: { - $ref: "#/definitions/EnabledLibraryTypes" - }, - filename: { - $ref: "#/definitions/Filename" - }, - globalObject: { - $ref: "#/definitions/GlobalObject" - }, - importFunctionName: { - $ref: "#/definitions/ImportFunctionName" - }, - library: { - $ref: "#/definitions/Library" - }, - libraryExport: { - oneOf: [ - { - $ref: "#/definitions/LibraryExport" - } - ] - }, - libraryTarget: { - oneOf: [ - { - $ref: "#/definitions/LibraryType" - } - ] - }, - module: { - $ref: "#/definitions/OutputModule" - }, - path: { - $ref: "#/definitions/Path" - }, - publicPath: { - $ref: "#/definitions/PublicPath" - }, - strictModuleErrorHandling: { - $ref: "#/definitions/StrictModuleErrorHandling" - }, - umdNamedDefine: { - oneOf: [ - { - $ref: "#/definitions/UmdNamedDefine" - } - ] - }, - uniqueName: { - $ref: "#/definitions/UniqueName" - }, - chunkLoadingGlobal: { - $ref: "#/definitions/ChunkLoadingGlobal" - }, - trustedTypes: { - description: - "Use a Trusted Types policy to create urls for chunks. 'output.uniqueName' is used a default policy name. Passing a string sets a custom policy name.", - anyOf: [ - { - enum: [true] - }, - { - description: - "The name of the Trusted Types policy created by webpack to serve bundle chunks.", - type: "string", - minLength: 1 - }, - { - $ref: "#/definitions/TrustedTypes" - } - ] - }, - sourceMapFilename: { - $ref: "#/definitions/SourceMapFilename" - }, - hashDigest: { - $ref: "#/definitions/HashDigest" - }, - hashDigestLength: { - $ref: "#/definitions/HashDigestLength" - }, - hashFunction: { - $ref: "#/definitions/HashFunction" - }, - hashSalt: { - $ref: "#/definitions/HashSalt" - } - } - }, - OutputModule: { - description: "Output javascript files as module source type.", - type: "boolean" - }, - ParserOptionsByModuleType: { - description: "Specify options for each parser.", - type: "object", - additionalProperties: { - description: "Options for parsing.", - type: "object", - additionalProperties: true - }, - properties: { - asset: { - $ref: "#/definitions/AssetParserOptions" - } - } - }, - Path: { - description: "The output directory as **absolute path** (required).", - type: "string" - }, - Plugins: { - description: "Add additional plugins to the compiler.", - type: "array", - items: { - description: "Plugin of type object or instanceof Function.", - anyOf: [ - { - $ref: "#/definitions/RspackPluginInstance" - }, - { - $ref: "#/definitions/RspackPluginFunction" - } - ] - } - }, - PublicPath: { - description: - "The 'publicPath' specifies the public URL address of the output files when referenced in a browser.", - anyOf: [ - { - enum: ["auto"] - }, - { - $ref: "#/definitions/RawPublicPath" - } - ] - }, - RawPublicPath: { - description: - "The 'publicPath' specifies the public URL address of the output files when referenced in a browser.", - anyOf: [ - { - type: "string" - } - ] - }, - Resolve: { - description: "Options for the resolver.", - oneOf: [ - { - $ref: "#/definitions/ResolveOptions" - } - ] - }, - ResolveAlias: { - description: "Redirect module requests.", - anyOf: [ - { - type: "object", - additionalProperties: { - description: "New request.", - anyOf: [ - { - description: "Multiple alternative requests.", - type: "array", - items: { - description: "One choice of request.", - type: "string", - minLength: 1 - } - }, - { - description: "Ignore request (replace with empty module).", - enum: [false] - }, - { - description: "New request.", - type: "string", - minLength: 1 - } - ] - } - } - ] - }, - ResolveOptions: { - description: "Options object for resolving requests.", - type: "object", - additionalProperties: false, - properties: { - alias: { - $ref: "#/definitions/ResolveAlias" - }, - browserField: { - description: - "Fields in the description file (usually package.json) which are used to redirect requests inside the module.", - type: "boolean" - }, - conditionNames: { - description: "Condition names for exports field entry point.", - type: "array", - items: { - description: "Condition names for exports field entry point.", - type: "string" - } - }, - extensionAlias: { - description: "An object which maps extension to extension aliases.", - type: "object", - additionalProperties: { - description: "Extension alias.", - anyOf: [ - { - description: "Multiple extensions.", - type: "array", - items: { - description: "Aliased extension.", - type: "string", - minLength: 1 - } - }, - { - description: "Aliased extension.", - type: "string", - minLength: 1 - } - ] - } - }, - extensions: { - description: - "Extensions added to the request when trying to find the file.", - type: "array", - items: { - description: - "Extension added to the request when trying to find the file.", - type: "string" - } - }, - fallback: { - description: "Redirect module requests when normal resolving fails.", - oneOf: [ - { - $ref: "#/definitions/ResolveAlias" - } - ] - }, - fullySpecified: { - description: - "Treats the request specified by the user as fully specified, meaning no extensions are added and the mainFiles in directories are not resolved (This doesn't affect requests from mainFields, aliasFields or aliases).", - type: "boolean" - }, - mainFields: { - description: - "Field names from the description file (package.json) which are used to find the default entry point.", - type: "array", - items: { - description: - "Field name from the description file (package.json) which are used to find the default entry point.", - anyOf: [ - { - type: "array", - items: { - description: - "Part of the field path from the description file (package.json) which are used to find the default entry point.", - type: "string", - minLength: 1 - } - }, - { - type: "string", - minLength: 1 - } - ] - } - }, - mainFiles: { - description: - "Filenames used to find the default entry point if there is no description file or main field.", - type: "array", - items: { - description: - "Filename used to find the default entry point if there is no description file or main field.", - type: "string", - minLength: 1 - } - }, - modules: { - description: "Folder names or directory paths where to find modules.", - type: "array", - items: { - description: "Folder name or directory path where to find modules.", - type: "string", - minLength: 1 - } - }, - preferRelative: { - description: - "Prefer to resolve module requests as relative request and fallback to resolving as module.", - type: "boolean" - }, - byDependency: { - description: - 'Extra resolve options per dependency category. Typical categories are "commonjs", "amd", "esm".', - type: "object", - additionalProperties: { - description: "Options object for resolving requests.", - oneOf: [ - { - $ref: "#/definitions/ResolveOptions" - } - ] - } - }, - tsConfigPath: { - description: "Path to tsconfig.json", - type: "string" - }, - exportsFields: { - description: - "Fields in the description file (usually package.json) which are used to redirect requests inside the module.", - type: "array", - items: { - description: - "Field name from the description file (package.json) which are used to find the default entry point.", - type: "string" - } - } - } - }, - RuleSetCondition: { - description: "A condition matcher.", - anyOf: [ - { - instanceof: "RegExp" - }, - { - type: "string" - }, - { - instanceof: "Function" - }, - { - $ref: "#/definitions/RuleSetLogicalConditions" - }, - { - $ref: "#/definitions/RuleSetConditions" - } - ] - }, - RuleSetConditionOrConditions: { - description: "One or multiple rule conditions.", - anyOf: [ - { - $ref: "#/definitions/RuleSetCondition" - }, - { - $ref: "#/definitions/RuleSetConditions" - } - ] - }, - RuleSetConditions: { - description: "A list of rule conditions.", - type: "array", - items: { - description: "A rule condition.", - oneOf: [ - { - $ref: "#/definitions/RuleSetCondition" - } - ] - } - }, - RuleSetLoader: { - description: "A loader request.", - type: "string", - minLength: 1 - }, - RuleSetLoaderOptions: { - description: "Options passed to a loader.", - anyOf: [ - { - type: "string" - }, - { - type: "object" - } - ] - }, - RuleSetLogicalConditions: { - description: "Logic operators used in a condition matcher.", - type: "object", - additionalProperties: false, - properties: { - and: { - description: "Logical AND.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditions" - } - ] - }, - not: { - description: "Logical NOT.", - oneOf: [ - { - $ref: "#/definitions/RuleSetCondition" - } - ] - }, - or: { - description: "Logical OR.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditions" - } - ] - } - } - }, - RuleSetRule: { - description: - "A rule description with conditions and effects for modules.", - type: "object", - additionalProperties: false, - properties: { - enforce: { - description: "Enforce this rule as pre or post step.", - enum: ["pre", "post"] - }, - exclude: { - description: "Shortcut for resource.exclude.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - generator: { - description: "The options for the module generator.", - type: "object" - }, - include: { - description: "Shortcut for resource.include.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - issuer: { - description: - "Match the issuer of the module (The module pointing to this module).", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - dependency: { - description: "Match dependency type.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - descriptionData: { - description: - "Match values of properties in the description file (usually package.json).", - type: "object", - additionalProperties: { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - }, - oneOf: { - description: "Only execute the first matching rule in this array.", - type: "array", - items: { - description: "A rule.", - oneOf: [ - { - $ref: "#/definitions/RuleSetRule" - } - ] - } - }, - parser: { - description: "Options for parsing.", - type: "object", - additionalProperties: true - }, - resolve: { - description: "Options for the resolver.", - type: "object", - oneOf: [ - { - $ref: "#/definitions/ResolveOptions" - } - ] - }, - resource: { - description: "Match the resource path of the module.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - resourceFragment: { - description: "Match the resource fragment of the module.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - resourceQuery: { - description: "Match the resource query of the module.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - mimetype: { - description: "Match module mimetype when load from Data URI.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - loader: { - description: "Shortcut for use.loader.", - oneOf: [ - { - $ref: "#/definitions/RuleSetLoader" - } - ] - }, - options: { - description: "Shortcut for use.options.", - oneOf: [ - { - $ref: "#/definitions/RuleSetLoaderOptions" - } - ] - }, - scheme: { - description: "Match module scheme.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - rules: { - description: - "Match and execute these rules when this rule is matched.", - type: "array", - items: { - description: "A rule.", - oneOf: [ - { - $ref: "#/definitions/RuleSetRule" - } - ] - } - }, - sideEffects: { - description: "Flags a module as with or without side effects.", - type: "boolean" - }, - test: { - description: "Shortcut for resource.test.", - oneOf: [ - { - $ref: "#/definitions/RuleSetConditionOrConditions" - } - ] - }, - type: { - description: "Module type to use for the module.", - type: "string" - }, - use: { - description: "Modifiers applied to the module when rule is matched.", - oneOf: [ - { - $ref: "#/definitions/RuleSetUse" - } - ] - } - } - }, - RuleSetRules: { - description: "A list of rules.", - type: "array", - items: { - description: "A rule.", - anyOf: [ - { - enum: ["..."] - }, - { - $ref: "#/definitions/RuleSetRule" - } - ] - } - }, - RuleSetUse: { - description: "A list of descriptions of loaders applied.", - anyOf: [ - { - type: "array", - items: { - description: "An use item.", - oneOf: [ - { - $ref: "#/definitions/RuleSetUseItem" - } - ] - } - }, - { - $ref: "#/definitions/RuleSetUseItem" - } - ] - }, - RuleSetUseItem: { - description: "A description of an applied loader.", - anyOf: [ - { - type: "object", - additionalProperties: false, - properties: { - loader: { - description: "Loader name.", - oneOf: [ - { - $ref: "#/definitions/RuleSetLoader" - } - ] - }, - options: { - description: "Loader options.", - oneOf: [ - { - $ref: "#/definitions/RuleSetLoaderOptions" - } - ] - } - } - }, - { - $ref: "#/definitions/RuleSetLoader" - } - ] - }, - SnapshotOptions: { - description: - "Options affecting how file system snapshots are created and validated.", - type: "object", - additionalProperties: false, - properties: { - module: { - description: - "Options for snapshotting dependencies of modules to determine if they need to be built again.", - type: "object", - additionalProperties: false, - properties: { - hash: { - description: - "Use hashes of the content of the files/directories to determine invalidation.", - type: "boolean" - }, - timestamp: { - description: - "Use timestamps of the files/directories to determine invalidation.", - type: "boolean" - } - } - }, - resolve: { - description: - "Options for snapshotting dependencies of request resolving to determine if requests need to be re-resolved.", - type: "object", - additionalProperties: false, - properties: { - hash: { - description: - "Use hashes of the content of the files/directories to determine invalidation.", - type: "boolean" - }, - timestamp: { - description: - "Use timestamps of the files/directories to determine invalidation.", - type: "boolean" - } - } - } - } - }, - StatsOptions: { - description: "Stats options object.", - type: "object", - additionalProperties: true, - properties: { - all: { - description: - "Fallback value for stats options when an option is not defined (has precedence over local rspack defaults).", - type: "boolean" - }, - assets: { - description: "Add assets information.", - type: "boolean" - }, - chunkGroups: { - description: - "Display all chunk groups with the corresponding bundles.", - type: "boolean" - }, - chunks: { - description: "Add chunk information.", - type: "boolean" - }, - colors: { - description: "Enables/Disables colorful output.", - type: "boolean" - }, - entrypoints: { - description: - "Display the entry points with the corresponding bundles.", - anyOf: [ - { - enum: ["auto"] - }, - { - type: "boolean" - } - ] - }, - errors: { - description: "Add errors.", - type: "boolean" - }, - errorsCount: { - description: "Add errors count.", - type: "boolean" - }, - hash: { - description: "Add the hash of the compilation.", - type: "boolean" - }, - modules: { - description: "Add built modules information.", - type: "boolean" - }, - preset: { - description: "Preset for the default values.", - anyOf: [ - { - type: "boolean" - }, - { - type: "string" - } - ] - }, - publicPath: { - description: "Add public path information.", - type: "boolean" - }, - reasons: { - description: - "Add information about the reasons why modules are included.", - type: "boolean" - }, - warnings: { - description: "Add warnings.", - type: "boolean" - }, - warningsCount: { - description: "Add warnings count.", - type: "boolean" - }, - outputPath: { - description: "Add output path information.", - type: "boolean" - }, - chunkModules: { - description: "Add built modules information to chunk information.", - type: "boolean" - }, - chunkRelations: { - description: - "Add information about parent, children and sibling chunks to chunk information.", - type: "boolean" - }, - timings: { - description: "Add timing information.", - type: "boolean" - }, - builtAt: { - description: "Add built at time information.", - type: "boolean" - }, - nestedModules: { - description: - "Add information about modules nested in other modules (like with module concatenation).", - type: "boolean" - } - } - }, - StatsValue: { - description: "Stats options object or preset name.", - anyOf: [ - { - enum: ["none", "errors-only", "errors-warnings", "normal", "verbose"] - }, - { - type: "boolean" - }, - { - $ref: "#/definitions/StatsOptions" - } - ] - }, - StrictModuleErrorHandling: { - description: - "Handles error in module loading correctly at a performance cost. This will handle module error compatible with the EcmaScript Modules spec.", - type: "boolean" - }, - Target: { - description: - "Environment to build for. An array of environments to build for all of them when possible.", - anyOf: [ - { - type: "array", - items: { - description: "Environment to build for.", - type: "string", - minLength: 1 - }, - minItems: 1 - }, - { - enum: [false] - }, - { - type: "string", - minLength: 1 - } - ] - }, - TrustedTypes: { - description: "Use a Trusted Types policy to create urls for chunks.", - type: "object", - additionalProperties: false, - properties: { - policyName: { - description: - "The name of the Trusted Types policy created by webpack to serve bundle chunks.", - type: "string", - minLength: 1 - } - } - }, - UmdNamedDefine: { - description: - "If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.", - type: "boolean" - }, - UniqueName: { - description: - "A unique name of the rspack build to avoid multiple rspack runtimes to conflict when using globals.", - type: "string", - minLength: 1 - }, - ChunkLoadingGlobal: { - description: "The global variable used by rspack for loading of chunks.", - type: "string", - minLength: 1 - }, - Watch: { - description: "Enter watch mode, which rebuilds on file change.", - type: "boolean" - }, - WatchOptions: { - description: "Options for the watcher.", - type: "object", - additionalProperties: false, - properties: { - aggregateTimeout: { - description: - "Delay the rebuilt after the first change. Value is a time in ms.", - type: "number" - }, - followSymlinks: { - description: - "Resolve symlinks and watch symlink and real file. This is usually not needed as rspack already resolves symlinks ('resolve.symlinks').", - type: "boolean" - }, - ignored: { - description: - "Ignore some files from watching (glob pattern or regexp).", - anyOf: [ - { - type: "array", - items: { - description: - "A glob pattern for files that should be ignored from watching.", - type: "string", - minLength: 1 - } - }, - { - instanceof: "RegExp" - }, - { - description: - "A single glob pattern for files that should be ignored from watching.", - type: "string", - minLength: 1 - } - ] - }, - poll: { - description: "Enable polling mode for watching.", - anyOf: [ - { - description: "`number`: use polling with specified interval.", - type: "number" - }, - { - description: "`true`: use polling.", - type: "boolean" - } - ] - }, - stdin: { - description: "Stop watching when stdin stream has ended.", - type: "boolean" - } - } - }, - RspackPluginFunction: { - description: "Function acting as plugin.", - instanceof: "Function" - }, - RspackPluginInstance: { - description: "Plugin instance.", - type: "object", - additionalProperties: true, - properties: { - apply: { - description: "The run point of the plugin, required method.", - instanceof: "Function" - } - }, - required: ["apply"] - } - }, - title: "RspackOptions", - description: "Options object as provided by the user.", - type: "object", - additionalProperties: false, - properties: { - cache: { - $ref: "#/definitions/CacheOptions" - }, - context: { - $ref: "#/definitions/Context" - }, - dependencies: { - $ref: "#/definitions/Dependencies" - }, - devServer: { - $ref: "#/definitions/DevServer" - }, - devtool: { - $ref: "#/definitions/DevTool" - }, - entry: { - $ref: "#/definitions/Entry" - }, - experiments: { - $ref: "#/definitions/Experiments" - }, - externals: { - $ref: "#/definitions/Externals" - }, - externalsType: { - $ref: "#/definitions/ExternalsType" - }, - externalsPresets: { - $ref: "#/definitions/ExternalsPresets" - }, - infrastructureLogging: { - $ref: "#/definitions/InfrastructureLogging" - }, - mode: { - $ref: "#/definitions/Mode" - }, - module: { - $ref: "#/definitions/ModuleOptions" - }, - name: { - $ref: "#/definitions/Name" - }, - node: { - $ref: "#/definitions/Node" - }, - optimization: { - $ref: "#/definitions/Optimization" - }, - output: { - $ref: "#/definitions/Output" - }, - plugins: { - $ref: "#/definitions/Plugins" - }, - resolve: { - $ref: "#/definitions/Resolve" - }, - snapshot: { - $ref: "#/definitions/SnapshotOptions" - }, - stats: { - $ref: "#/definitions/StatsValue" - }, - target: { - $ref: "#/definitions/Target" - }, - watch: { - $ref: "#/definitions/Watch" - }, - watchOptions: { - $ref: "#/definitions/WatchOptions" - }, - builtins: { - description: "Builtins features in rspack", - type: "object", - additionalProperties: true - }, - ignoreWarnings: { - $ref: "#/definitions/IgnoreWarnings" - } - } -}; diff --git a/packages/rspack/src/config/target.js b/packages/rspack/src/config/target.js index 107852deccb..d604e7fd3c0 100644 --- a/packages/rspack/src/config/target.js +++ b/packages/rspack/src/config/target.js @@ -14,6 +14,10 @@ const getBrowserslistTargetHandler = memoize(() => require("./browserslistTargetHandler") ); +/** + * @param {string} context the context directory + * @returns {import("..").Target} target properties + */ const getDefaultTarget = context => { // TODO: align with webpack // const browsers = getBrowserslistTargetHandler().load(null, context); diff --git a/packages/rspack/src/config/types.ts b/packages/rspack/src/config/types.ts deleted file mode 100644 index 0dcf7071269..00000000000 --- a/packages/rspack/src/config/types.ts +++ /dev/null @@ -1,706 +0,0 @@ -/** - * The following code is modified based on - * https://github.com/webpack/webpack/blob/4b4ca3b/declarations/WebpackOptions.d.ts - * - * MIT Licensed - * Author Tobias Koppers @sokra - * Copyright (c) JS Foundation and other contributors - * https://github.com/webpack/webpack/blob/main/LICENSE - */ - -import watchpack from "watchpack"; -import { Compiler } from "../Compiler"; -import * as oldBuiltins from "../builtin-plugin"; -import { Compilation } from ".."; -import type * as webpackDevServer from "webpack-dev-server"; -import type { Options as RspackOptions } from "./zod/_rewrite"; -import type { OptimizationConfig as Optimization } from "./zod/optimization"; -import type { RawFuncUseCtx } from "@rspack/binding"; -import { RspackBuiltinPlugin } from "../builtin-plugin"; -export type { RspackOptions, Optimization }; - -export type { - LoaderContext, - LoaderDefinitionFunction, - LoaderDefinition -} from "./adapterRuleUse"; - -export type Configuration = RspackOptions; - -export interface RspackOptionsNormalized { - name?: Name; - dependencies?: Dependencies; - context?: Context; - mode?: Mode; - entry: EntryNormalized; - output: OutputNormalized; - resolve: Resolve; - resolveLoader: Resolve; - module: ModuleOptionsNormalized; - target?: Target; - externals?: Externals; - externalsType?: ExternalsType; - externalsPresets: ExternalsPresets; - infrastructureLogging: InfrastructureLogging; - devtool?: DevTool; - node: Node; - snapshot: SnapshotOptions; - cache?: CacheOptions; - stats: StatsValue; - optimization: Optimization; - plugins: Plugins; - experiments: ExperimentsNormalized; - watch?: Watch; - watchOptions: WatchOptions; - devServer?: DevServer; - ignoreWarnings?: IgnoreWarningsNormalized; - profile?: Profile; - builtins: Builtins; -} - -///// Name ///// -export type Name = string; - -///// Dependencies ///// -export type Dependencies = Name[]; - -///// Context ///// -export type Context = string; - -///// Mode ///// -export type Mode = "development" | "production" | "none"; - -///// Entry ///// -export type Entry = EntryStatic; -export type EntryStatic = EntryObject | EntryUnnamed; -export type EntryUnnamed = EntryItem; -export type EntryRuntime = false | string; -export type EntryItem = string[] | string; -export type ChunkLoading = false | ChunkLoadingType; -export type ChunkLoadingType = - | ("jsonp" | "import-scripts" | "require" | "async-node" | "import") - | string; -export interface EntryObject { - [k: string]: EntryItem | EntryDescription; -} -export type EntryFilename = FilenameTemplate; -export interface EntryDescription { - import: EntryItem; - runtime?: EntryRuntime; - chunkLoading?: ChunkLoading; - asyncChunks?: boolean; - publicPath?: PublicPath; - baseUri?: string; - filename?: EntryFilename; -} - -export type EntryNormalized = EntryStaticNormalized; -export interface EntryStaticNormalized { - [k: string]: EntryDescriptionNormalized; -} -export interface EntryDescriptionNormalized { - import?: string[]; - runtime?: EntryRuntime; - chunkLoading?: ChunkLoading; - asyncChunks?: boolean; - publicPath?: PublicPath; - baseUri?: string; - filename?: EntryFilename; -} - -///// Output ///// -export interface Output { - path?: Path; - publicPath?: PublicPath; - clean?: Clean; - filename?: Filename; - chunkFilename?: ChunkFilename; - crossOriginLoading?: CrossOriginLoading; - cssFilename?: CssFilename; - cssChunkFilename?: CssChunkFilename; - assetModuleFilename?: AssetModuleFilename; - hotUpdateMainFilename?: HotUpdateMainFilename; - hotUpdateChunkFilename?: HotUpdateChunkFilename; - uniqueName?: UniqueName; - chunkLoadingGlobal?: ChunkLoadingGlobal; - enabledLibraryTypes?: EnabledLibraryTypes; - libraryExport?: LibraryExport; - libraryTarget?: LibraryType; - auxiliaryComment?: AuxiliaryComment; - umdNamedDefine?: UmdNamedDefine; - module?: OutputModule; - library?: Library; - strictModuleErrorHandling?: StrictModuleErrorHandling; - globalObject?: GlobalObject; - importFunctionName?: ImportFunctionName; - iife?: Iife; - wasmLoading?: WasmLoading; - enabledWasmLoadingTypes?: EnabledWasmLoadingTypes; - webassemblyModuleFilename?: WebassemblyModuleFilename; - chunkFormat?: string | false; - chunkLoading?: string | false; - enabledChunkLoadingTypes?: string[]; - trustedTypes?: true | string | TrustedTypes; - sourceMapFilename?: SourceMapFilename; - hashDigest?: HashDigest; - hashDigestLength?: HashDigestLength; - hashFunction?: HashFunction; - hashSalt?: HashSalt; - asyncChunks?: boolean; - workerChunkLoading?: ChunkLoading; - workerWasmLoading?: WasmLoading; - workerPublicPath?: WorkerPublicPath; -} -export type Path = string; -export type PublicPath = "auto" | RawPublicPath; -export type RawPublicPath = string; -export type AssetModuleFilename = string; -export type WebassemblyModuleFilename = string; -export type Filename = FilenameTemplate; -export type ChunkFilename = FilenameTemplate; -export type CrossOriginLoading = false | "anonymous" | "use-credentials"; -export type CssFilename = FilenameTemplate; -export type CssChunkFilename = FilenameTemplate; -export type HotUpdateChunkFilename = FilenameTemplate; -export type HotUpdateMainFilename = FilenameTemplate; -export type FilenameTemplate = string; -export type UniqueName = string; -export type ChunkLoadingGlobal = string; -export type Library = LibraryName | LibraryOptions; -export type StrictModuleErrorHandling = boolean; -export type OutputModule = boolean; -export type SourceMapFilename = FilenameTemplate; -export type Iife = boolean; -export type Clean = boolean; -export interface LibraryCustomUmdCommentObject { - amd?: string; - commonjs?: string; - commonjs2?: string; - root?: string; -} -export interface LibraryOptions { - auxiliaryComment?: AuxiliaryComment; - export?: LibraryExport; - name?: LibraryName; - type: LibraryType; - umdNamedDefine?: UmdNamedDefine; -} -export type LibraryName = string | string[] | LibraryCustomUmdObject; -export interface LibraryCustomUmdObject { - amd?: string; - commonjs?: string; - root?: string | string[]; -} -export type LibraryExport = string[] | string; -export type LibraryType = - | ( - | "var" - | "module" - | "assign" - | "assign-properties" - | "this" - | "window" - | "self" - | "global" - | "commonjs" - | "commonjs2" - | "commonjs-module" - | "commonjs-static" - | "amd" - | "amd-require" - | "umd" - | "umd2" - | "jsonp" - | "system" - ) - | string; -export type AuxiliaryComment = string | LibraryCustomUmdCommentObject; -export type UmdNamedDefine = boolean; -export type EnabledLibraryTypes = LibraryType[]; -export type GlobalObject = string; -export type ImportFunctionName = string; -export type WasmLoading = false | WasmLoadingType; -export type WasmLoadingType = - | ("fetch-streaming" | "fetch" | "async-node") - | string; -export type EnabledWasmLoadingTypes = WasmLoadingType[]; -export interface TrustedTypes { - policyName?: string; -} -export type HashDigest = string; -export type HashDigestLength = number; -export type HashFunction = string; -export type HashSalt = string; -export type WorkerPublicPath = string; -export interface OutputNormalized { - path?: Path; - clean?: Clean; - publicPath?: PublicPath; - filename?: Filename; - chunkFilename?: ChunkFilename; - crossOriginLoading?: CrossOriginLoading; - cssFilename?: CssFilename; - cssChunkFilename?: CssChunkFilename; - hotUpdateMainFilename?: HotUpdateMainFilename; - hotUpdateChunkFilename?: HotUpdateChunkFilename; - assetModuleFilename?: AssetModuleFilename; - uniqueName?: UniqueName; - chunkLoadingGlobal?: ChunkLoadingGlobal; - enabledLibraryTypes?: EnabledLibraryTypes; - library?: LibraryOptions; - module?: OutputModule; - strictModuleErrorHandling?: StrictModuleErrorHandling; - globalObject?: GlobalObject; - importFunctionName?: ImportFunctionName; - iife?: Iife; - wasmLoading?: WasmLoading; - enabledWasmLoadingTypes?: EnabledWasmLoadingTypes; - webassemblyModuleFilename?: WebassemblyModuleFilename; - chunkFormat?: string | false; - chunkLoading?: string | false; - enabledChunkLoadingTypes?: string[]; - trustedTypes?: TrustedTypes; - sourceMapFilename?: SourceMapFilename; - hashDigest?: HashDigest; - hashDigestLength?: HashDigestLength; - hashFunction?: HashFunction; - hashSalt?: HashSalt; - asyncChunks?: boolean; - workerChunkLoading?: ChunkLoading; - workerWasmLoading?: WasmLoading; - workerPublicPath?: WorkerPublicPath; -} - -///// Resolve ///// -export type Resolve = ResolveOptions; -export interface ResolveOptions { - alias?: ResolveAlias; - /** - * This is `aliasField: ["browser"]` in webpack, because no one - * uses aliasField other than "browser". ---@bvanjoi - */ - browserField?: boolean; - conditionNames?: string[]; - extensions?: string[]; - fallback?: ResolveAlias; - mainFields?: string[]; - mainFiles?: string[]; - modules?: string[]; - preferRelative?: boolean; - tsConfigPath?: string; - fullySpecified?: boolean; - exportsFields?: string[]; - extensionAlias?: Record; - byDependency?: { - [k: string]: ResolveOptions; - }; -} -export type ResolveAlias = { - [k: string]: false | string | Array; -}; - -///// Module ///// -export interface ModuleOptions { - defaultRules?: RuleSetRules; - rules?: RuleSetRules; - parser?: ParserOptionsByModuleType; - generator?: GeneratorOptionsByModuleType; -} -export type RuleSetRules = ("..." | RuleSetRule)[]; -export interface RuleSetRule { - test?: RuleSetCondition; - exclude?: RuleSetCondition; - include?: RuleSetCondition; - issuer?: RuleSetCondition; - dependency?: RuleSetCondition; - resource?: RuleSetCondition; - resourceFragment?: RuleSetCondition; - resourceQuery?: RuleSetCondition; - scheme?: RuleSetCondition; - mimetype?: RuleSetCondition; - descriptionData?: { - [k: string]: RuleSetCondition; - }; - oneOf?: RuleSetRule[]; - rules?: RuleSetRule[]; - type?: string; - loader?: RuleSetLoader; - options?: RuleSetLoaderOptions; - use?: RuleSetUse; - parser?: { - [k: string]: any; - }; - generator?: { - [k: string]: any; - }; - resolve?: ResolveOptions; - sideEffects?: boolean; - /** - * Specifies the category of the loader. No value means normal loader. - */ - enforce?: "pre" | "post"; -} -export type RuleSetCondition = - | RegExp - | string - | RuleSetConditions - | RuleSetLogicalConditions - | ((value: string) => boolean); -export type RuleSetConditions = RuleSetCondition[]; -export interface RuleSetLogicalConditions { - and?: RuleSetConditions; - or?: RuleSetConditions; - not?: RuleSetConditions; -} -export type RuleSetUse = - | RuleSetUseItem[] - | RuleSetUseItem - | ((funcUseCtx: RawFuncUseCtx) => RuleSetUseItem[]); -export type RuleSetUseItem = RuleSetLoaderWithOptions | RuleSetLoader; -export type RuleSetLoader = string; -export type RuleSetLoaderWithOptions = { - ident?: string; - loader: RuleSetLoader; - options?: RuleSetLoaderOptions; -}; -export type RuleSetLoaderOptions = - | string - | { - [k: string]: any; - }; -export type ParserOptionsByModuleType = ParserOptionsByModuleTypeKnown; -export interface ParserOptionsByModuleTypeKnown { - asset?: AssetParserOptions; -} -export interface AssetParserOptions { - dataUrlCondition?: AssetParserDataUrl; -} -export type AssetParserDataUrl = AssetParserDataUrlOptions; -export interface AssetParserDataUrlOptions { - maxSize?: number; -} -export type GeneratorOptionsByModuleType = GeneratorOptionsByModuleTypeKnown; -export interface GeneratorOptionsByModuleTypeKnown { - asset?: AssetGeneratorOptions; - "asset/inline"?: AssetInlineGeneratorOptions; - "asset/resource"?: AssetResourceGeneratorOptions; -} -export type AssetGeneratorOptions = AssetInlineGeneratorOptions & - AssetResourceGeneratorOptions; -export interface AssetInlineGeneratorOptions { - dataUrl?: AssetGeneratorDataUrl; -} -export type AssetGeneratorDataUrl = AssetGeneratorDataUrlOptions; -export interface AssetGeneratorDataUrlOptions { - encoding?: false | "base64"; - mimetype?: string; -} -export interface AssetResourceGeneratorOptions { - filename?: FilenameTemplate; - publicPath?: RawPublicPath; -} - -export interface ModuleOptionsNormalized { - defaultRules?: RuleSetRules; - rules: RuleSetRules; - parser: ParserOptionsByModuleType; - generator: GeneratorOptionsByModuleType; -} - -export type AvailableTarget = - | "async-node" - | "node" - | `node${number}` - | `node${number}.${number}` - | `async-node${number}.${number}` - | `async-node${number}` - | "electron-main" - | `electron${number}-main` - | `electron${number}.${number}-main` - | "electron-renderer" - | `electron${number}-renderer` - | `electron${number}.${number}-renderer` - | "electron-preload" - | `electron${number}-preload` - | `electron${number}.${number}-preload` - | "web" - | "webworker" - | `es${number}` - | "browserslist"; - -///// Target ///// -export type Target = false | AvailableTarget[] | AvailableTarget; - -///// Externals ///// -export type Externals = ExternalItem[] | ExternalItem; -export type ExternalItem = - | string - | RegExp - | ExternalItemObjectUnknown - | ( - | (( - data: ExternalItemFunctionData, - callback: ( - err?: Error, - result?: ExternalItemValue, - type?: ExternalsType - ) => void - ) => void) - | ((data: ExternalItemFunctionData) => Promise) - ); - -export interface ExternalItemFunctionData { - /** - * The directory in which the request is placed. - */ - context?: string; - /** - * Contextual information. - */ - // contextInfo?: import("../lib/ModuleFactory").ModuleFactoryCreateDataContextInfo; - /** - * The category of the referencing dependencies. - */ - dependencyType?: string; - /** - * Get a resolve function with the current resolver options. - */ - // getResolve?: ( - // options?: ResolveOptions - // ) => - // | (( - // context: string, - // request: string, - // callback: (err?: Error, result?: string) => void - // ) => void) - // | ((context: string, request: string) => Promise); - /** - * The request as written by the user in the require/import expression/statement. - */ - request?: string; -} -export interface ExternalItemObjectUnknown { - [k: string]: ExternalItemValue; -} -export type ExternalItemValue = string | boolean; - -///// ExternalsType ///// -export type ExternalsType = - | "var" - | "module" - | "assign" - | "this" - | "window" - | "self" - | "global" - | "commonjs" - | "commonjs2" - | "commonjs-module" - | "commonjs-static" - | "amd" - | "amd-require" - | "umd" - | "umd2" - | "jsonp" - | "system" - | "promise" - | "import" - | "script" - | "node-commonjs"; - -///// ExternalsPresets ///// -export interface ExternalsPresets { - node?: boolean; - web?: boolean; - electron?: boolean; - electronMain?: boolean; - electronPreload?: boolean; - electronRenderer?: boolean; -} - -///// InfrastructureLogging ///// -export interface InfrastructureLogging { - appendOnly?: boolean; - colors?: boolean; - console?: Console; - debug?: boolean | FilterTypes; - level?: "none" | "error" | "warn" | "info" | "log" | "verbose"; - stream?: NodeJS.WritableStream; -} -export type FilterTypes = FilterItemTypes[] | FilterItemTypes; -export type FilterItemTypes = RegExp | string | ((value: string) => boolean); - -///// DevTool ///// -export type DevTool = - | false - | "cheap-source-map" - | "cheap-module-source-map" - | "source-map" - | "inline-cheap-source-map" - | "inline-cheap-module-source-map" - | "inline-source-map" - | "inline-nosources-cheap-module-source-map" - | "inline-nosources-source-map" - | "nosources-cheap-source-map" - | "nosources-cheap-module-source-map" - | "nosources-source-map" - | "hidden-nosources-cheap-source-map" - | "hidden-nosources-cheap-module-source-map" - | "hidden-nosources-source-map" - | "hidden-cheap-source-map" - | "hidden-cheap-module-source-map" - | "hidden-source-map" - | "eval-cheap-source-map" - | "eval-cheap-module-source-map" - | "eval-source-map" - | "eval-nosources-cheap-source-map" - | "eval-nosources-cheap-module-source-map" - | "eval-nosources-source-map"; - -///// Node ///// -export type Node = false | NodeOptions; - -export interface NodeOptions { - __dirname?: false | true | "warn-mock" | "mock" | "eval-only"; - __filename?: false | true | "warn-mock" | "mock" | "eval-only"; - global?: boolean | "warn"; -} - -///// Snapshot ///// -export interface SnapshotOptions { - module?: { - hash?: boolean; - timestamp?: boolean; - }; - resolve?: { - hash?: boolean; - timestamp?: boolean; - }; -} - -///// Cache ///// -// TODO: align with webpack -export type CacheOptions = true | false; - -///// Stats ///// -export type StatsValue = - | ("none" | "errors-only" | "errors-warnings" | "normal" | "verbose") - | boolean - | StatsOptions; -export interface StatsOptions { - all?: boolean; - preset?: "normal" | "none" | "verbose" | "errors-only" | "errors-warnings"; - assets?: boolean; - chunks?: boolean; - modules?: boolean; - entrypoints?: boolean; - chunkGroups?: boolean; - warnings?: boolean; - warningsCount?: boolean; - errors?: boolean; - errorsCount?: boolean; - colors?: boolean; - hash?: boolean; - version?: boolean; - reasons?: boolean; - publicPath?: boolean; - outputPath?: boolean; - chunkModules?: boolean; - chunkRelations?: boolean; - timings?: boolean; - builtAt?: boolean; - moduleAssets?: boolean; - modulesSpace?: number; - nestedModules?: boolean; - source?: boolean; - logging?: ("none" | "error" | "warn" | "info" | "log" | "verbose") | boolean; - loggingDebug?: boolean | FilterTypes; - loggingTrace?: boolean; -} - -export type OptimizationRuntimeChunk = - | ("single" | "multiple") - | boolean - | { - name?: string | Function; - }; -export type OptimizationRuntimeChunkNormalized = - | false - | { - name: (...args: any[]) => string | undefined; - }; - -///// Plugins ///// -export type Plugins = ( - | RspackPluginInstance - | RspackPluginFunction - | RspackBuiltinPlugin -)[]; -export interface RspackPluginInstance { - apply: (compiler: Compiler) => void; - [k: string]: any; -} -export type RspackPluginFunction = (this: Compiler, compiler: Compiler) => void; - -///// Experiments ///// -export interface Experiments { - lazyCompilation?: boolean; - incrementalRebuild?: boolean | IncrementalRebuildOptions; - asyncWebAssembly?: boolean; - outputModule?: boolean; - newSplitChunks?: boolean; - css?: boolean; -} -export interface IncrementalRebuildOptions { - make?: boolean; - emitAsset?: boolean; -} -export interface RspackFutureOptions {} -// TODO: discuss with webpack, should move to css generator options -// export interface CssExperimentOptions { -// exportsOnly?: boolean; -// localsConvention?: -// | "asIs" -// | "camelCase" -// | "camelCaseOnly" -// | "dashes" -// | "dashesOnly"; -// localIdentName?: string; -// } -export interface ExperimentsNormalized { - lazyCompilation?: boolean; - incrementalRebuild?: false | IncrementalRebuildOptions; - asyncWebAssembly?: boolean; - outputModule?: boolean; - newSplitChunks?: boolean; - css?: boolean; - futureDefaults?: boolean; - rspackFuture?: RspackFutureOptions; -} - -///// Watch ///// -export type Watch = boolean; - -///// WatchOptions ///// -export type WatchOptions = watchpack.WatchOptions; - -///// DevServer ///// -export interface DevServer extends webpackDevServer.Configuration { - hot?: boolean; -} - -///// IgnoreWarnings ///// -export type IgnoreWarningsPattern = ( - | RegExp - | ((warning: Error, compilation: Compilation) => boolean) -)[]; -export type IgnoreWarningsNormalized = (( - warning: Error, - compilation: Compilation -) => boolean)[]; - -///// Profile ///// -export type Profile = boolean; - -///// Builtins ///// -export type Builtins = oldBuiltins.Builtins; diff --git a/packages/rspack/src/config/zod.ts b/packages/rspack/src/config/zod.ts new file mode 100644 index 00000000000..b3e4ec53103 --- /dev/null +++ b/packages/rspack/src/config/zod.ts @@ -0,0 +1,1053 @@ +import { RawFuncUseCtx } from "@rspack/binding"; +import { z } from "zod"; +import { Compilation, Compiler } from ".."; +import type * as oldBuiltins from "../builtin-plugin"; +import type * as webpackDevServer from "webpack-dev-server"; + +//#region Name +const name = z.string(); +export type Name = z.infer; +//#endregion + +//#region Dependencies +const dependencies = z.array(name); +export type Dependencies = z.infer; +//#endregion + +//#region Context +const context = z.string(); +export type Context = z.infer; +//#endregion + +//#region Mode +const mode = z.enum(["development", "production", "none"]); +export type Mode = z.infer; +//#endregion + +//#region Entry +const rawPublicPath = z.string(); +export type RawPublicPath = z.infer; + +const publicPath = z.literal("auto").or(rawPublicPath); +export type PublicPath = z.infer; + +const baseUri = z.string(); +export type BaseUri = z.infer; + +const chunkLoadingType = z + .enum(["jsonp", "import-scripts", "require", "async-node", "import"]) + .or(z.string()); +export type ChunkLoadingType = z.infer; + +const chunkLoading = z.literal(false).or(chunkLoadingType); +export type ChunkLoading = z.infer; + +const asyncChunks = z.boolean(); +export type AsyncChunks = z.infer; + +const wasmLoadingType = z + .enum(["fetch-streaming", "fetch", "async-node"]) + .or(z.string()); +export type WasmLoadingType = z.infer; + +const wasmLoading = z.literal(false).or(wasmLoadingType); +export type WasmLoading = z.infer; + +const filenameTemplate = z.string(); +export type FilenameTemplate = z.infer; + +const filename = filenameTemplate; +export type Filename = z.infer; + +const entryFilename = filenameTemplate; +export type EntryFilename = z.infer; + +const entryRuntime = z.literal(false).or(z.string()); +export type EntryRuntime = z.infer; + +const entryItem = z.string().or(z.array(z.string())); +export type EntryItem = z.infer; + +const entryDescription = z.strictObject({ + import: entryItem, + runtime: entryRuntime.optional(), + publicPath: publicPath.optional(), + baseUri: baseUri.optional(), + chunkLoading: chunkLoading.optional(), + asyncChunks: asyncChunks.optional(), + wasmLoading: wasmLoading.optional(), + filename: entryFilename.optional() +}); +export type EntryDescription = z.infer; + +const entryUnnamed = entryItem; +export type EntryUnnamed = z.infer; + +const entryObject = z.record(entryItem.or(entryDescription)); +export type EntryObject = z.infer; + +const entryStatic = entryObject.or(entryUnnamed); +export type EntryStatic = z.infer; + +const entry = entryStatic; +export type Entry = z.infer; +//#endregion + +//#region Output +const path = z.string(); +export type Path = z.infer; + +const assetModuleFilename = z.string(); +export type AssetModuleFilename = z.infer; + +const webassemblyModuleFilename = z.string(); +export type WebassemblyModuleFilename = z.infer< + typeof webassemblyModuleFilename +>; + +const chunkFilename = filenameTemplate; +export type ChunkFilename = z.infer; + +const crossOriginLoading = z + .literal(false) + .or(z.enum(["anonymous", "use-credentials"])); +export type CrossOriginLoading = z.infer; + +const cssFilename = filenameTemplate; +export type CssFilename = z.infer; + +const cssChunkFilename = filenameTemplate; +export type CssChunkFilename = z.infer; + +const hotUpdateChunkFilename = filenameTemplate; +export type HotUpdateChunkFilename = z.infer; + +const hotUpdateMainFilename = filenameTemplate; +export type HotUpdateMainFilename = z.infer; + +const uniqueName = z.string(); +export type UniqueName = z.infer; + +const chunkLoadingGlobal = z.string(); +export type ChunkLoadingGlobal = z.infer; + +const libraryCustomUmdObject = z.strictObject({ + amd: z.string().optional(), + commonjs: z.string().optional(), + root: z.string().or(z.array(z.string())).optional() +}); +export type LibraryCustomUmdObject = z.infer; + +const libraryName = z + .string() + .or(z.array(z.string())) + .or(libraryCustomUmdObject); +export type LibraryName = z.infer; + +const libraryCustomUmdCommentObject = z.strictObject({ + amd: z.string().optional(), + commonjs: z.string().optional(), + commonjs2: z.string().optional(), + root: z.string().optional() +}); +export type LibraryCustomUmdCommentObject = z.infer< + typeof libraryCustomUmdCommentObject +>; + +const auxiliaryComment = z.string().or(libraryCustomUmdCommentObject); +export type AuxiliaryComment = z.infer; + +const libraryExport = z.string().or(z.array(z.string())); +export type LibraryExport = z.infer; + +const libraryType = z + .enum([ + "var", + "module", + "assign", + "assign-properties", + "this", + "window", + "self", + "global", + "commonjs", + "commonjs2", + "commonjs-module", + "commonjs-static", + "amd", + "amd-require", + "umd", + "umd2", + "jsonp", + "system" + ]) + .or(z.string()); +export type LibraryType = z.infer; + +const umdNamedDefine = z.boolean(); +export type UmdNamedDefine = z.infer; + +const libraryOptions = z.strictObject({ + auxiliaryComment: auxiliaryComment.optional(), + export: libraryExport.optional(), + name: libraryName.optional(), + type: libraryType, + umdNamedDefine: umdNamedDefine.optional() +}); +export type LibraryOptions = z.infer; + +const enabledLibraryTypes = z.array(libraryType); +export type EnabledLibraryTypes = z.infer; + +const clean = z.boolean(); +export type Clean = z.infer; + +const outputModule = z.boolean(); +export type OutputModule = z.infer; + +const strictModuleErrorHandling = z.boolean(); +export type StrictModuleErrorHandling = z.infer< + typeof strictModuleErrorHandling +>; + +const globalObject = z.string(); +export type GlobalObject = z.infer; + +const enabledWasmLoadingTypes = z.array(wasmLoadingType); +export type EnabledWasmLoadingTypes = z.infer; + +const importFunctionName = z.string(); +export type ImportFunctionName = z.infer; + +const iife = z.boolean(); +export type Iife = z.infer; + +const enabledChunkLoadingTypes = z.array(chunkLoadingType); +export type EnabledChunkLoadingTypes = z.infer; + +const chunkFormat = z.literal(false).or(z.string()); +export type ChunkFormat = z.infer; + +const workerPublicPath = z.string(); +export type WorkerPublicPath = z.infer; + +const trustedTypes = z.strictObject({ + policyName: z.string().optional() +}); +export type TrustedTypes = z.infer; + +const hashDigest = z.string(); +export type HashDigest = z.infer; + +const hashDigestLength = z.number(); +export type HashDigestLength = z.infer; + +const hashFunction = z.string(); +export type HashFunction = z.infer; + +const hashSalt = z.string(); +export type HashSalt = z.infer; + +const sourceMapFilename = z.string(); +export type SourceMapFilename = z.infer; + +const output = z.strictObject({ + path: path.optional(), + clean: clean.optional(), + publicPath: publicPath.optional(), + filename: filename.optional(), + chunkFilename: chunkFilename.optional(), + crossOriginLoading: crossOriginLoading.optional(), + cssFilename: cssFilename.optional(), + cssChunkFilename: cssChunkFilename.optional(), + hotUpdateMainFilename: hotUpdateMainFilename.optional(), + hotUpdateChunkFilename: hotUpdateChunkFilename.optional(), + assetModuleFilename: assetModuleFilename.optional(), + uniqueName: uniqueName.optional(), + chunkLoadingGlobal: chunkLoadingGlobal.optional(), + enabledLibraryTypes: enabledLibraryTypes.optional(), + library: libraryName.or(libraryOptions).optional(), + libraryExport: libraryExport.optional(), + libraryTarget: libraryType.optional(), + umdNamedDefine: umdNamedDefine.optional(), + auxiliaryComment: auxiliaryComment.optional(), + module: outputModule.optional(), + strictModuleErrorHandling: strictModuleErrorHandling.optional(), + globalObject: globalObject.optional(), + importFunctionName: importFunctionName.optional(), + iife: iife.optional(), + wasmLoading: wasmLoading.optional(), + enabledWasmLoadingTypes: enabledWasmLoadingTypes.optional(), + webassemblyModuleFilename: webassemblyModuleFilename.optional(), + chunkFormat: chunkFormat.optional(), + chunkLoading: chunkLoading.optional(), + enabledChunkLoadingTypes: enabledChunkLoadingTypes.optional(), + trustedTypes: z.literal(true).or(z.string()).or(trustedTypes).optional(), + sourceMapFilename: sourceMapFilename.optional(), + hashDigest: hashDigest.optional(), + hashDigestLength: hashDigestLength.optional(), + hashFunction: hashFunction.optional(), + hashSalt: hashSalt.optional(), + asyncChunks: asyncChunks.optional(), + workerChunkLoading: chunkLoading.optional(), + workerWasmLoading: wasmLoading.optional(), + workerPublicPath: workerPublicPath.optional() +}); +export type Output = z.infer; +//#endregion + +//#region Resolve +const resolveAlias = z.record( + z + .literal(false) + .or(z.string()) + .or(z.array(z.string().or(z.literal(false)))) +); +export type ResolveAlias = z.infer; + +const baseResolveOptions = z.strictObject({ + alias: resolveAlias.optional(), + /** + * This is `aliasField: ["browser"]` in webpack, because no one + * uses aliasField other than "browser". ---@bvanjoi + */ + browserField: z.boolean().optional(), + conditionNames: z.array(z.string()).optional(), + extensions: z.array(z.string()).optional(), + fallback: resolveAlias.optional(), + mainFields: z.array(z.string()).optional(), + mainFiles: z.array(z.string()).optional(), + modules: z.array(z.string()).optional(), + preferRelative: z.boolean().optional(), + tsConfigPath: z.string().optional(), + fullySpecified: z.boolean().optional(), + exportsFields: z.array(z.string()).optional(), + extensionAlias: z.record(z.string().or(z.array(z.string()))).optional() +}); + +export type ResolveOptions = z.infer & { + byDependency?: Record; +}; +const resolveOptions: z.ZodType = baseResolveOptions.extend({ + byDependency: z.lazy(() => z.record(resolveOptions)).optional() +}); + +const resolve = resolveOptions; +export type Resolve = z.infer; +//#endregion + +//#region Module +export type RuleSetCondition = + | RegExp + | string + | RuleSetConditions + | RuleSetLogicalConditions + | ((value: string) => boolean); +const ruleSetCondition: z.ZodType = z + .instanceof(RegExp) + .or(z.string()) + .or(z.lazy(() => ruleSetConditions)) + .or(z.lazy(() => ruleSetLogicalConditions)) + .or(z.function().args(z.string()).returns(z.boolean())); + +export type RuleSetConditions = RuleSetCondition[]; +const ruleSetConditions: z.ZodType = z.lazy(() => + z.array(ruleSetCondition) +); + +export type RuleSetLogicalConditions = { + and?: RuleSetConditions; + or?: RuleSetConditions; + not?: RuleSetConditions; +}; +const ruleSetLogicalConditions: z.ZodType = + z.strictObject({ + and: ruleSetConditions.optional(), + or: ruleSetConditions.optional(), + not: ruleSetConditions.optional() + }); + +const ruleSetLoader = z.string(); +export type RuleSetLoader = z.infer; + +const ruleSetLoaderOptions = z.string().or(z.record(z.any())); +export type RuleSetLoaderOptions = z.infer; + +const ruleSetLoaderWithOptions = z.strictObject({ + ident: z.string().optional(), + loader: ruleSetLoader, + options: ruleSetLoaderOptions.optional() +}); +export type RuleSetLoaderWithOptions = z.infer; + +const ruleSetUseItem = ruleSetLoader.or(ruleSetLoaderWithOptions); +export type RuleSetUseItem = z.infer; + +const ruleSetUse = ruleSetUseItem + .or(ruleSetUseItem.array()) + .or( + z.function().args(z.custom()).returns(ruleSetUseItem.array()) + ); +export type RuleSetUse = z.infer; + +export type RuleSetRule = { + test?: RuleSetCondition; + exclude?: RuleSetCondition; + include?: RuleSetCondition; + issuer?: RuleSetCondition; + dependency?: RuleSetCondition; + resource?: RuleSetCondition; + resourceFragment?: RuleSetCondition; + resourceQuery?: RuleSetCondition; + scheme?: RuleSetCondition; + mimetype?: RuleSetCondition; + descriptionData?: { + [k: string]: RuleSetCondition; + }; + oneOf?: RuleSetRule[]; + rules?: RuleSetRule[]; + type?: string; + loader?: RuleSetLoader; + options?: RuleSetLoaderOptions; + use?: RuleSetUse; + parser?: { + [k: string]: any; + }; + generator?: { + [k: string]: any; + }; + resolve?: ResolveOptions; + sideEffects?: boolean; + enforce?: "pre" | "post"; +}; +const ruleSetRule: z.ZodType = z.strictObject({ + test: ruleSetCondition.optional(), + exclude: ruleSetCondition.optional(), + include: ruleSetCondition.optional(), + issuer: ruleSetCondition.optional(), + dependency: ruleSetCondition.optional(), + resource: ruleSetCondition.optional(), + resourceFragment: ruleSetCondition.optional(), + resourceQuery: ruleSetCondition.optional(), + scheme: ruleSetCondition.optional(), + mimetype: ruleSetCondition.optional(), + descriptionData: z.record(ruleSetCondition).optional(), + oneOf: z.lazy(() => ruleSetRule.array()).optional(), + rules: z.lazy(() => ruleSetRule.array()).optional(), + type: z.string().optional(), + loader: ruleSetLoader.optional(), + options: ruleSetLoaderOptions.optional(), + use: ruleSetUse.optional(), + parser: z.record(z.any()).optional(), + generator: z.record(z.any()).optional(), + resolve: resolveOptions.optional(), + sideEffects: z.boolean().optional(), + enforce: z.literal("pre").or(z.literal("post")).optional() +}); + +const ruleSetRules = z.array(z.literal("...").or(ruleSetRule)); +export type RuleSetRules = z.infer; + +const assetParserDataUrlOptions = z.strictObject({ + maxSize: z.number().optional() +}); +export type AssetParserDataUrlOptions = z.infer< + typeof assetParserDataUrlOptions +>; + +const assetParserDataUrl = assetParserDataUrlOptions; +export type AssetParserDataUrl = z.infer; + +const assetParserOptions = z.strictObject({ + dataUrlCondition: assetParserDataUrl.optional() +}); +export type AssetParserOptions = z.infer; + +const parserOptionsByModuleTypeKnown = z.strictObject({ + asset: assetParserOptions.optional() +}); +export type ParserOptionsByModuleTypeKnown = z.infer< + typeof parserOptionsByModuleTypeKnown +>; + +const parserOptionsByModuleTypeUnknown = z.record(z.record(z.any())); +export type ParserOptionsByModuleTypeUnknown = z.infer< + typeof parserOptionsByModuleTypeUnknown +>; + +const parserOptionsByModuleType = parserOptionsByModuleTypeKnown.or( + parserOptionsByModuleTypeUnknown +); +export type ParserOptionsByModuleType = z.infer< + typeof parserOptionsByModuleType +>; + +const assetGeneratorDataUrlOptions = z.strictObject({ + encoding: z.literal(false).or(z.literal("base64")).optional(), + mimetype: z.string().optional() +}); +export type AssetGeneratorDataUrlOptions = z.infer< + typeof assetGeneratorDataUrlOptions +>; + +const assetGeneratorDataUrl = assetGeneratorDataUrlOptions; +export type AssetGeneratorDataUrl = z.infer; + +const assetInlineGeneratorOptions = z.strictObject({ + dataUrl: assetGeneratorDataUrl.optional() +}); +export type AssetInlineGeneratorOptions = z.infer< + typeof assetInlineGeneratorOptions +>; + +const assetResourceGeneratorOptions = z.strictObject({ + filename: filenameTemplate.optional(), + publicPath: publicPath.optional() +}); +export type AssetResourceGeneratorOptions = z.infer< + typeof assetResourceGeneratorOptions +>; + +const assetGeneratorOptions = assetInlineGeneratorOptions.merge( + assetResourceGeneratorOptions +); +export type AssetGeneratorOptions = z.infer; + +const generatorOptionsByModuleTypeKnown = z.strictObject({ + asset: assetGeneratorOptions.optional(), + "asset/inline": assetInlineGeneratorOptions.optional(), + "asset/resource": assetResourceGeneratorOptions.optional() +}); +export type GeneratorOptionsByModuleTypeKnown = z.infer< + typeof generatorOptionsByModuleTypeKnown +>; + +const generatorOptionsByModuleTypeUnknown = z.record(z.record(z.any())); +export type GeneratorOptionsByModuleTypeUnknown = z.infer< + typeof generatorOptionsByModuleTypeUnknown +>; + +const generatorOptionsByModuleType = generatorOptionsByModuleTypeKnown.or( + generatorOptionsByModuleTypeUnknown +); +export type GeneratorOptionsByModuleType = z.infer< + typeof generatorOptionsByModuleType +>; + +const moduleOptions = z.strictObject({ + defaultRules: ruleSetRules.optional(), + rules: ruleSetRules.optional(), + parser: parserOptionsByModuleType.optional(), + generator: generatorOptionsByModuleType.optional() +}); +export type ModuleOptions = z.infer; +//#endregion + +//#region Target +const allowTarget = z + .enum([ + "web", + "webworker", + "es3", + "es5", + "es2015", + "es2016", + "es2017", + "es2018", + "es2019", + "es2020", + "es2021", + "es2022", + "browserslist" + ]) + .or(z.literal("node")) + .or(z.literal("async-node")) + .or( + z.custom<`node${number}`>( + value => typeof value === "string" && /^node\d+$/.test(value) + ) + ) + .or( + z.custom<`async-node${number}`>( + value => typeof value === "string" && /^async-node\d+$/.test(value) + ) + ) + .or( + z.custom<`node${number}.${number}`>( + value => typeof value === "string" && /^node\d+\.\d+$/.test(value) + ) + ) + .or( + z.custom<`async-node${number}.${number}`>( + value => typeof value === "string" && /^async-node\d+\.\d+$/.test(value) + ) + ) + .or(z.literal("electron-main")) + .or( + z.custom<`electron${number}-main`>( + value => typeof value === "string" && /^electron\d+-main$/.test(value) + ) + ) + .or( + z.custom<`electron${number}.${number}-main`>( + value => + typeof value === "string" && /^electron\d+\.\d+-main$/.test(value) + ) + ) + .or(z.literal("electron-renderer")) + .or( + z.custom<`electron${number}-renderer`>( + value => typeof value === "string" && /^electron\d+-renderer$/.test(value) + ) + ) + .or( + z.custom<`electron${number}.${number}-renderer`>( + value => + typeof value === "string" && /^electron\d+\.\d+-renderer$/.test(value) + ) + ) + .or(z.literal("electron-preload")) + .or( + z.custom<`electron${number}-preload`>( + value => typeof value === "string" && /^electron\d+-preload$/.test(value) + ) + ) + .or( + z.custom<`electron${number}.${number}-preload`>( + value => + typeof value === "string" && /^electron\d+\.\d+-preload$/.test(value) + ) + ); +const target = z.literal(false).or(allowTarget).or(allowTarget.array()); +export type Target = z.infer; +//#endregion + +//#region ExternalsType +const externalsType = z.enum([ + "var", + "module", + "assign", + "this", + "window", + "self", + "global", + "commonjs", + "commonjs2", + "commonjs-module", + "commonjs-static", + "amd", + "amd-require", + "umd", + "umd2", + "jsonp", + "system", + "promise", + "import", + "script", + "node-commonjs" +]); +export type ExternalsType = z.infer; +//#endregion + +//#region Externals +const externalItemValue = z.string().or(z.boolean()).or(z.string().array()); +export type ExternalItemValue = z.infer; + +const externalItemObjectUnknown = z.record(externalItemValue); +export type ExternalItemObjectUnknown = z.infer< + typeof externalItemObjectUnknown +>; + +const externalItemFunctionData = z.strictObject({ + context: z.string().optional(), + dependencyType: z.string().optional(), + request: z.string().optional() +}); +export type ExternalItemFunctionData = z.infer; + +const externalItem = z + .string() + .or(z.instanceof(RegExp)) + .or(externalItemObjectUnknown) + .or( + z + .function() + .args( + externalItemFunctionData, + z + .function() + .args( + z.instanceof(Error).optional(), + externalItemValue.optional(), + externalsType.optional() + ) + .returns(z.void()) + ) + ) + .or( + z + .function() + .args(externalItemFunctionData) + .returns(z.promise(externalItemValue)) + ); +export type ExternalItem = z.infer; + +const externals = externalItem.array().or(externalItem); +export type Externals = z.infer; +//#endregion + +//#region ExternalsPresets +const externalsPresets = z.strictObject({ + node: z.boolean().optional(), + web: z.boolean().optional(), + electron: z.boolean().optional(), + electronMain: z.boolean().optional(), + electronPreload: z.boolean().optional(), + electronRenderer: z.boolean().optional() +}); +export type ExternalsPresets = z.infer; +//#endregion + +//#region InfrastructureLogging +const filterItemTypes = z + .instanceof(RegExp) + .or(z.string()) + .or(z.function().args(z.string()).returns(z.boolean())); +export type FilterItemTypes = z.infer; + +const filterTypes = filterItemTypes.array().or(filterItemTypes); +export type FilterTypes = z.infer; + +const infrastructureLogging = z.strictObject({ + appendOnly: z.boolean().optional(), + colors: z.boolean().optional(), + console: z.custom().optional(), + debug: z.boolean().or(filterTypes).optional(), + level: z.enum(["none", "error", "warn", "info", "log", "verbose"]).optional(), + stream: z.custom().optional() +}); +export type InfrastructureLogging = z.infer; +//#endregion + +//#region DevTool +const devTool = z + .literal(false) + .or( + z.enum([ + "cheap-source-map", + "cheap-module-source-map", + "source-map", + "inline-cheap-source-map", + "inline-cheap-module-source-map", + "inline-source-map", + "inline-nosources-cheap-module-source-map", + "inline-nosources-source-map", + "nosources-cheap-source-map", + "nosources-cheap-module-source-map", + "nosources-source-map", + "hidden-nosources-cheap-source-map", + "hidden-nosources-cheap-module-source-map", + "hidden-nosources-source-map", + "hidden-cheap-source-map", + "hidden-cheap-module-source-map", + "hidden-source-map", + "eval-cheap-source-map", + "eval-cheap-module-source-map", + "eval-source-map", + "eval-nosources-cheap-source-map", + "eval-nosources-cheap-module-source-map", + "eval-nosources-source-map" + ]) + ); +export type DevTool = z.infer; +//#endregion + +//#region Node +const nodeOptions = z.strictObject({ + __dirname: z + .boolean() + .or(z.enum(["warn-mock", "mock", "eval-only"])) + .optional(), + __filename: z + .boolean() + .or(z.enum(["warn-mock", "mock", "eval-only"])) + .optional(), + global: z.boolean().or(z.literal("warn")).optional() +}); +export type NodeOptions = z.infer; + +const node = z.literal(false).or(nodeOptions); +export type Node = z.infer; +//#endregion + +//#region Snapshot +const snapshotOptions = z.strictObject({ + module: z + .strictObject({ + hash: z.boolean().optional(), + timestamp: z.boolean().optional() + }) + .optional(), + resolve: z + .strictObject({ + hash: z.boolean().optional(), + timestamp: z.boolean().optional() + }) + .optional() +}); +export type SnapshotOptions = z.infer; +//#endregion + +//#region Cache +const cacheOptions = z.boolean(); +export type CacheOptions = z.infer; +//#endregion + +//#region Stats +const statsOptions = z.strictObject({ + all: z.boolean().optional(), + preset: z + .enum(["normal", "none", "verbose", "errors-only", "errors-warnings"]) + .optional(), + assets: z.boolean().optional(), + chunks: z.boolean().optional(), + modules: z.boolean().optional(), + entrypoints: z.boolean().optional(), + chunkGroups: z.boolean().optional(), + warnings: z.boolean().optional(), + warningsCount: z.boolean().optional(), + errors: z.boolean().optional(), + errorsCount: z.boolean().optional(), + colors: z.boolean().optional(), + hash: z.boolean().optional(), + version: z.boolean().optional(), + reasons: z.boolean().optional(), + publicPath: z.boolean().optional(), + outputPath: z.boolean().optional(), + chunkModules: z.boolean().optional(), + chunkRelations: z.boolean().optional(), + ids: z.boolean().optional(), + timings: z.boolean().optional(), + builtAt: z.boolean().optional(), + moduleAssets: z.boolean().optional(), + modulesSpace: z.number().optional(), + nestedModules: z.boolean().optional(), + source: z.boolean().optional(), + logging: z + .enum(["none", "error", "warn", "info", "log", "verbose"]) + .or(z.boolean()) + .optional(), + loggingDebug: z.boolean().or(filterTypes).optional(), + loggingTrace: z.boolean().optional() +}); +export type StatsOptions = z.infer; + +const statsValue = z + .enum(["none", "errors-only", "errors-warnings", "normal", "verbose"]) + .or(z.boolean()) + .or(statsOptions); +export type StatsValue = z.infer; +//#endregion + +//#region Plugins +export interface RspackPluginInstance { + apply: (compiler: Compiler) => void; + [k: string]: any; +} +export type RspackPluginFunction = (this: Compiler, compiler: Compiler) => void; + +const plugin = z.union([ + z.custom(), + z.custom() +]); +const plugins = plugin.array(); +export type Plugins = z.infer; +//#endregion + +//#region Optimization +const optimizationRuntimeChunk = z + .enum(["single", "multiple"]) + .or(z.boolean()) + .or( + z.strictObject({ + name: z + .string() + .or(z.function().returns(z.string().or(z.undefined()))) + .optional() + }) + ); +export type OptimizationRuntimeChunk = z.infer; + +const optimizationSplitChunksName = z.string().or(z.literal(false)); +const optimizationSplitChunksChunks = z + .enum(["initial", "async", "all"]) + .or(z.instanceof(RegExp)); +const optimizationSplitChunksSizes = z.number(); +const sharedOptimizationSplitChunksCacheGroup = { + chunks: optimizationSplitChunksChunks.optional(), + minChunks: z.number().optional(), + name: optimizationSplitChunksName.optional(), + minSize: optimizationSplitChunksSizes.optional(), + maxSize: optimizationSplitChunksSizes.optional(), + maxAsyncSize: optimizationSplitChunksSizes.optional(), + maxInitialSize: optimizationSplitChunksSizes.optional() +}; +const optimizationSplitChunksCacheGroup = z.strictObject({ + test: z.string().or(z.instanceof(RegExp)).optional(), + priority: z.number().optional(), + enforce: z.boolean().optional(), + reuseExistingChunk: z.boolean().optional(), + type: z.string().or(z.instanceof(RegExp)).optional(), + idHint: z.string().optional(), + ...sharedOptimizationSplitChunksCacheGroup +}); +export type OptimizationSplitChunksCacheGroup = z.infer< + typeof optimizationSplitChunksCacheGroup +>; + +const optimizationSplitChunksOptions = z.strictObject({ + cacheGroups: z + .record(z.literal(false).or(optimizationSplitChunksCacheGroup)) + .optional(), + maxAsyncRequests: z.number().optional(), + maxInitialRequests: z.number().optional(), + fallbackCacheGroup: z + .strictObject({ + chunks: optimizationSplitChunksChunks.optional(), + minSize: z.number().optional(), + maxSize: z.number().optional(), + maxAsyncSize: z.number().optional(), + maxInitialSize: z.number().optional() + }) + .optional(), + ...sharedOptimizationSplitChunksCacheGroup +}); +export type OptimizationSplitChunksOptions = z.infer< + typeof optimizationSplitChunksOptions +>; + +const optimization = z.strictObject({ + moduleIds: z.enum(["named", "deterministic"]).optional(), + chunkIds: z.enum(["named", "deterministic"]).optional(), + minimize: z.boolean().optional(), + minimizer: z.literal("...").or(plugin).array().optional(), + splitChunks: optimizationSplitChunksOptions.optional(), + runtimeChunk: optimizationRuntimeChunk.optional(), + removeAvailableModules: z.boolean().optional(), + removeEmptyChunks: z.boolean().optional(), + realContentHash: z.boolean().optional(), + sideEffects: z.enum(["flag"]).or(z.boolean()).optional() +}); +export type Optimization = z.infer; +//#endregion + +//#region Experiments +const incrementalRebuildOptions = z.strictObject({ + make: z.boolean().optional(), + emitAsset: z.boolean().optional() +}); +export type IncrementalRebuildOptions = z.infer< + typeof incrementalRebuildOptions +>; + +const rspackFutureOptions = z.strictObject({ + newResolver: z.boolean().optional() +}); +export type RspackFutureOptions = z.infer; + +const experiments = z.strictObject({ + lazyCompilation: z.boolean().optional(), + incrementalRebuild: z.boolean().or(incrementalRebuildOptions).optional(), + asyncWebAssembly: z.boolean().optional(), + outputModule: z.boolean().optional(), + newSplitChunks: z.boolean().optional(), + css: z.boolean().optional(), + futureDefaults: z.boolean().optional(), + rspackFuture: rspackFutureOptions.optional() +}); +export type Experiments = z.infer; +//#endregion + +//#region Watch +const watch = z.boolean(); +export type Watch = z.infer; +//#endregion + +//#region WatchOptions +const watchOptions = z.strictObject({ + aggregateTimeout: z.number().optional(), + followSymlinks: z.boolean().optional(), + ignored: z + .string() + .array() + .or(z.instanceof(RegExp)) + .or(z.string()) + .optional(), + poll: z.number().or(z.boolean()).optional(), + stdin: z.boolean().optional() +}); +export type WatchOptions = z.infer; +//#endregion + +//#region DevServer +export interface DevServer extends webpackDevServer.Configuration { + hot?: boolean; +} +const devServer = z.custom(); +//#endregion + +//#region IgnoreWarnings +const ignoreWarnings = z + .instanceof(RegExp) + .or( + z + .function() + .args(z.instanceof(Error), z.custom()) + .returns(z.boolean()) + ) + .array(); +export type IgnoreWarnings = z.infer; +//#endregion + +//#region Profile +const profile = z.boolean(); +export type Profile = z.infer; +//#endregion + +//#region Builtins (deprecated) +const builtins = z.custom(); +export type Builtins = z.infer; +//#endregion + +export const rspackOptions = z.strictObject({ + name: name.optional(), + dependencies: dependencies.optional(), + entry: entry.optional(), + output: output.optional(), + target: target.optional(), + mode: mode.optional(), + experiments: experiments.optional(), + externals: externals.optional(), + externalsType: externalsType.optional(), + externalsPresets: externalsPresets.optional(), + infrastructureLogging: infrastructureLogging.optional(), + cache: cacheOptions.optional(), + context: context.optional(), + devtool: devTool.optional(), + node: node.optional(), + ignoreWarnings: ignoreWarnings.optional(), + watchOptions: watchOptions.optional(), + watch: watch.optional(), + stats: statsValue.optional(), + snapshot: snapshotOptions.optional(), + optimization: optimization.optional(), + resolve: resolve.optional(), + resolveLoader: resolve.optional(), + plugins: plugins.optional(), + devServer: devServer.optional(), + builtins: builtins.optional(), + module: moduleOptions.optional(), + profile: profile.optional() +}); +export type RspackOptions = z.infer; +export type Configuration = RspackOptions; diff --git a/packages/rspack/src/config/zod/_rewrite.ts b/packages/rspack/src/config/zod/_rewrite.ts deleted file mode 100644 index 221081d611e..00000000000 --- a/packages/rspack/src/config/zod/_rewrite.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { z } from "zod"; -import { configSchema } from "./index"; -import type { Output, ModuleOptions, DevServer, Builtins } from "../types"; - -// The final goal is to infer the type using the schema without any rewriting. -// But currently there are some schema are loose, so we need to rewrite the `Config` -// type to expose the correct type to users. -type Config = z.infer>; - -type Rewritten = { - output?: Output; - module?: ModuleOptions; - builtins?: Omit> & - NonNullable; - devServer?: DevServer; -}; - -export type Options = Omit & Rewritten; diff --git a/packages/rspack/src/config/zod/builtins.ts b/packages/rspack/src/config/zod/builtins.ts deleted file mode 100644 index 1b6a93899c3..00000000000 --- a/packages/rspack/src/config/zod/builtins.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { z } from "zod"; - -export function builtins() { - // TODO(hyf0): need to use `strictObject` mode when developer have time to finish the whole schema - return z.object({ - postcss: z - .strictObject({ - pxtorem: z - .strictObject({ - rootValue: z.number().optional(), - unitPrecision: z.number().optional(), - selectorBlackList: z.string().array().optional(), - propList: z.string().array().optional(), - replace: z.boolean().optional(), - mediaQuery: z.boolean().optional(), - minPixelValue: z.number().optional() - }) - .optional() - }) - .refine(() => { - console.warn( - "warn: `builtins.postcss` is removed in 0.3.0. Please use `postcss-loader` instead." - ); - return true; - }) - .optional(), - html: z - .object({ - title: z.string().optional(), - filename: z.string().optional(), - template: z.string().optional(), - templateParameters: z.record(z.string()).optional(), - inject: z.enum(["head", "body"]).optional(), - publicPath: z.string().optional(), - scriptLoading: z.enum(["blocking", "defer", "module"]).optional(), - chunks: z.string().array().optional(), - excludedChunks: z.string().array().optional(), - sri: z.enum(["sha256", "sha384", "sha512"]).optional(), - minify: z.boolean().optional(), - favicon: z.string().optional(), - meta: z.record(z.string().or(z.record(z.string()))).optional() - }) - .array() - .optional() - }); -} diff --git a/packages/rspack/src/config/zod/devtool.ts b/packages/rspack/src/config/zod/devtool.ts deleted file mode 100644 index 04ec6b2bb42..00000000000 --- a/packages/rspack/src/config/zod/devtool.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { z } from "zod"; - -export function devtool() { - return z - .literal(false) - .or( - z.enum([ - "cheap-source-map", - "cheap-module-source-map", - "source-map", - "inline-cheap-source-map", - "inline-cheap-module-source-map", - "inline-source-map", - "inline-nosources-cheap-module-source-map", - "inline-nosources-source-map", - "nosources-cheap-source-map", - "nosources-cheap-module-source-map", - "nosources-source-map", - "hidden-nosources-cheap-source-map", - "hidden-nosources-cheap-module-source-map", - "hidden-nosources-source-map", - "hidden-cheap-source-map", - "hidden-cheap-module-source-map", - "hidden-source-map", - "eval-cheap-source-map", - "eval-cheap-module-source-map", - "eval-source-map", - "eval-nosources-cheap-source-map", - "eval-nosources-cheap-module-source-map", - "eval-nosources-source-map" - ]) - ); -} diff --git a/packages/rspack/src/config/zod/entry.ts b/packages/rspack/src/config/zod/entry.ts deleted file mode 100644 index e0d347eb98b..00000000000 --- a/packages/rspack/src/config/zod/entry.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { z } from "zod"; -import { filename, publicPath } from "./output"; - -const entryItem = z - .string() - .min(1) - .describe("The string is resolved to a module which is loaded upon startup.") - .or(z.string().min(1).array().min(1)); - -const entryDescription = z - .object({ - import: entryItem, - runtime: z.literal(false).or(z.string().min(1)).optional(), - publicPath: publicPath().optional(), - baseUri: z.string().optional(), - chunkLoading: z - .literal(false) - .or( - z - .enum(["jsonp", "require", "async-node", "import", "import-scripts"]) - .or(z.string()) - .optional() - ), - asyncChunks: z.boolean().optional(), - wasmLoading: z - .literal(false) - .or(z.enum(["fetch-streaming", "fetch", "async-node"])) - .optional(), - filename: filename().optional() - }) - .strict(); - -const entryObject = z.record(entryItem.or(entryDescription)); - -export function entry() { - return entryItem.or(entryObject); -} diff --git a/packages/rspack/src/config/zod/experiments.ts b/packages/rspack/src/config/zod/experiments.ts deleted file mode 100644 index 86f4a332c2b..00000000000 --- a/packages/rspack/src/config/zod/experiments.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { z } from "zod"; - -export function experiments() { - return z.object({ - asyncWebAssembly: z.boolean().optional(), - incrementalRebuild: z - .boolean() - .or( - z.strictObject({ - make: z.boolean().optional(), - emitAsset: z.boolean().optional() - }) - ) - .optional(), - lazyCompilation: z.boolean().optional(), - outputModule: z.boolean().optional(), - newSplitChunks: z.boolean().optional(), - css: z.boolean().optional(), - rspackFuture: z.strictObject({}).optional() - }); -} diff --git a/packages/rspack/src/config/zod/externals-presets.ts b/packages/rspack/src/config/zod/externals-presets.ts deleted file mode 100644 index d860658c4da..00000000000 --- a/packages/rspack/src/config/zod/externals-presets.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { z } from "zod"; - -export function externalsPresets() { - return z - .object({ - web: z.boolean().optional(), - node: z.boolean().optional(), - electron: z.boolean().optional(), - electronMain: z.boolean().optional(), - electronPreload: z.boolean().optional(), - electronRenderer: z.boolean().optional() - }) - .strict(); -} diff --git a/packages/rspack/src/config/zod/externals-type.ts b/packages/rspack/src/config/zod/externals-type.ts deleted file mode 100644 index db4eb556b0b..00000000000 --- a/packages/rspack/src/config/zod/externals-type.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { z } from "zod"; - -export function externalsType() { - return z.enum([ - "var", - "module", - "assign", - "this", - "window", - "self", - "global", - "commonjs", - "commonjs2", - "commonjs-module", - "commonjs-static", - "amd", - "amd-require", - "umd", - "umd2", - "jsonp", - "system", - "promise", - "import", - "script", - "node-commonjs" - ]); -} diff --git a/packages/rspack/src/config/zod/externals.ts b/packages/rspack/src/config/zod/externals.ts deleted file mode 100644 index f92e2c115b2..00000000000 --- a/packages/rspack/src/config/zod/externals.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { z } from "zod"; - -const externalItem = z - .instanceof(RegExp) - .or(z.string()) - .or(z.function()) - .or(z.any()); - -export function externals() { - return externalItem.or(externalItem.array()); -} diff --git a/packages/rspack/src/config/zod/index.ts b/packages/rspack/src/config/zod/index.ts deleted file mode 100644 index 86436444ea8..00000000000 --- a/packages/rspack/src/config/zod/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { z } from "zod"; -import { entry } from "./entry"; -import { experiments } from "./experiments"; -import { externals } from "./externals"; -import { externalsType } from "./externals-type"; -import { externalsPresets } from "./externals-presets"; -import { infrastructureLogging } from "./infrastructure-logging"; -import { node } from "./node"; -import { builtins } from "./builtins"; -import { watchOptions } from "./watch-options"; -import { target } from "./target"; -import { stats } from "./stats"; -import { snapshot } from "./snapshot"; -import { output } from "./output"; -import { devtool } from "./devtool"; -import { optimization } from "./optimization"; -import { resolve } from "./resolve"; -import { plugins } from "./plugins"; - -export function configSchema() { - return z - .object({ - target: target().optional(), - mode: z.enum(["development", "production", "none"]).optional(), - entry: entry().optional(), - experiments: experiments().optional(), - externals: externals().optional(), - externalsType: externalsType().optional(), - externalsPresets: externalsPresets().optional(), - infrastructureLogging: infrastructureLogging().optional(), - cache: z.boolean().optional(), - context: z.string().optional(), - dependencies: z.string().array().optional(), - devtool: devtool().optional(), - node: node().optional(), - ignoreWarnings: z.instanceof(RegExp).or(z.function()).array().optional(), - watchOptions: watchOptions().optional(), - watch: z.boolean().optional(), - stats: stats().optional(), - snapshot: snapshot().optional(), - optimization: optimization().optional(), - resolve: resolve().optional(), - resolveLoader: resolve().optional(), - plugins: plugins().optional(), - // TODO(hyf0): what's the usage of this? - name: z.string().optional(), - // TODO - devServer: z.object({}).optional(), - output: output().optional(), - builtins: builtins().optional(), - module: z.any().optional(), - profile: z.boolean().optional() - }) - .strict(); -} diff --git a/packages/rspack/src/config/zod/infrastructure-logging.ts b/packages/rspack/src/config/zod/infrastructure-logging.ts deleted file mode 100644 index c2ca33d2eda..00000000000 --- a/packages/rspack/src/config/zod/infrastructure-logging.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { z } from "zod"; - -export function infrastructureLogging() { - return z - .object({ - appendOnly: z.boolean().optional(), - colors: z.boolean().optional(), - console: z.boolean().optional(), - debug: z.boolean().or(z.any()).optional(), - level: z - .enum(["none", "error", "warn", "info", "log", "verbose"]) - .optional(), - stream: z.boolean().or(z.object({})).optional() - }) - .strict(); -} diff --git a/packages/rspack/src/config/zod/node.ts b/packages/rspack/src/config/zod/node.ts deleted file mode 100644 index a4a912db29f..00000000000 --- a/packages/rspack/src/config/zod/node.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { z } from "zod"; - -export function node() { - return z.literal(false).or( - z - .object({ - __dirname: z - .boolean() - .or(z.enum(["warn-mock", "mock", "eval-only"])) - .optional(), - __filename: z - .boolean() - .or(z.enum(["warn-mock", "mock", "eval-only"])) - .optional(), - global: z - .boolean() - .or(z.enum(["warn"])) - .optional() - }) - .strict() - ); -} diff --git a/packages/rspack/src/config/zod/optimization/index.ts b/packages/rspack/src/config/zod/optimization/index.ts deleted file mode 100644 index 21bf01e733a..00000000000 --- a/packages/rspack/src/config/zod/optimization/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { z } from "zod"; -import { Compiler } from "../../../Compiler"; -import { splitChunks } from "./split-chunks"; - -const rspackPluginInstance = z.any(); - -export function optimization() { - return z.strictObject({ - moduleIds: z.enum(["named", "deterministic"]).optional(), - chunkIds: z.enum(["named", "deterministic"]).optional(), - minimize: z.boolean().optional(), - minimizer: z.literal("...").or(rspackPluginInstance).array().optional(), - splitChunks: splitChunks().optional(), - runtimeChunk: z - .enum(["single", "multiple"]) - .or(z.boolean()) - .or( - z.strictObject({ - name: z - .string() - .or(z.function().returns(z.string().or(z.undefined()))) - .optional() - }) - ) - .optional(), - removeAvailableModules: z.boolean().optional(), - removeEmptyChunks: z.boolean().optional(), - realContentHash: z.boolean().optional(), - sideEffects: z.enum(["flag"]).or(z.boolean()).optional() - }); -} - -export type OptimizationConfig = z.TypeOf>; - -export type OptimizationRuntimeChunkConfig = NonNullable< - OptimizationConfig["runtimeChunk"] ->; diff --git a/packages/rspack/src/config/zod/optimization/split-chunks.ts b/packages/rspack/src/config/zod/optimization/split-chunks.ts deleted file mode 100644 index 83e4ef2955e..00000000000 --- a/packages/rspack/src/config/zod/optimization/split-chunks.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { string, z } from "zod"; - -function chunks() { - return z.enum(["initial", "async", "all"]).or(z.instanceof(RegExp)); -} - -function name() { - return z.string().or(z.literal(false)); -} - -const sharedCacheGroupConfigPart = { - chunks: chunks().optional(), - minChunks: z.number().optional(), - name: name().optional(), - minSize: z.number().optional(), - maxSize: z.number().optional(), - maxAsyncSize: z.number().optional(), - maxInitialSize: z.number().optional() -}; - -const cacheGroupOptions = z.strictObject({ - test: z.string().or(z.instanceof(RegExp)).optional(), - priority: z.number().optional(), - enforce: z.boolean().optional(), - reuseExistingChunk: z.boolean().optional(), - type: z.string().or(z.instanceof(RegExp)).optional(), - idHint: z.string().optional(), - ...sharedCacheGroupConfigPart -}); - -export function splitChunks() { - return z.literal(false).or( - // We use loose object here to prevent breaking change on config - z.object({ - cacheGroups: z.record(z.literal(false).or(cacheGroupOptions)).optional(), - maxAsyncRequests: z.number().optional(), - maxInitialRequests: z.number().optional(), - fallbackCacheGroup: z - .strictObject({ - chunks: chunks().optional(), - minSize: z.number().optional(), - maxSize: z.number().optional(), - maxAsyncSize: z.number().optional(), - maxInitialSize: z.number().optional() - }) - .optional(), - ...sharedCacheGroupConfigPart - }) - ); -} - -export type SplitChunksConfig = z.TypeOf>; -export type CacheGroupOptionsConfig = z.TypeOf; diff --git a/packages/rspack/src/config/zod/output.ts b/packages/rspack/src/config/zod/output.ts deleted file mode 100644 index b68032dd874..00000000000 --- a/packages/rspack/src/config/zod/output.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { z } from "zod"; - -function chunkLoadingType() { - return z - .enum(["jsonp", "import-scripts", "require", "async-node", "import"]) - .or(z.string()); -} - -function chunkLoading() { - return z.literal(false).or(chunkLoadingType()); -} - -function wasmLoadingType() { - return z.enum(["...", "fetch-streaming", "fetch", "async-node"]); -} - -function wasmLoading() { - return z.literal(false).or(wasmLoadingType()); -} - -export function publicPath() { - return z.literal("auto").or(z.string()); -} - -export function filename() { - return z.string(); -} - -function workerPublicPath() { - return z.string(); -} - -function libraryType() { - return z.enum([ - "...", - "var", - "module", - "assign", - "assign-properties", - "this", - "window", - "self", - "global", - "commonjs", - "commonjs2", - "commonjs-module", - "commonjs-static", - "amd", - "amd-require", - "umd", - "umd2", - "jsonp", - "system" - ]); -} - -const umdNamedDefine = z.boolean(); - -const auxiliaryComment = z.string().or( - z.strictObject({ - amd: z.string().optional(), - commonjs: z.string().optional(), - commonjs2: z.string().optional(), - root: z.string().optional() - }) -); - -const libraryName = z - .string() - .or(z.string().array()) - .or( - z.strictObject({ - amd: z.string().optional(), - commonjs: z.string().optional(), - root: z.string().or(z.string().array()).optional() - }) - ); - -const libraryOptions = z.strictObject({ - auxiliaryComment: auxiliaryComment.optional(), - export: z.string().array().or(z.string()).optional(), - name: libraryName.optional(), - type: libraryType().optional(), - umdNamedDefine: umdNamedDefine.optional() -}); - -export function output() { - return z.strictObject({ - iife: z.boolean().optional(), - clean: z.boolean().optional(), - assetModuleFilename: z.string().optional(), - auxiliaryComment: auxiliaryComment.optional(), - chunkFormat: z - .enum(["array-push", "commonjs", "module"]) - .or(z.literal(false)) - .optional(), - chunkLoading: chunkLoading().optional(), - enabledChunkLoadingTypes: chunkLoadingType().array().optional(), - chunkFilename: z.string().optional(), - cssChunkFilename: z.string().optional(), - cssFilename: z.string().optional(), - hotUpdateChunkFilename: z.string().optional(), - hotUpdateMainFilename: z.string().optional(), - webassemblyModuleFilename: z.string().optional(), - hashSalt: z.string().optional(), - filename: filename().optional(), - sourceMapFilename: z.string().optional(), - importFunctionName: z.string().optional(), - publicPath: publicPath().optional(), - uniqueName: z.string().optional(), - path: z.string().optional(), - crossOriginLoading: z - .literal(false) - .or(z.enum(["anonymous", "use-credentials"])) - .optional(), - enabledWasmLoadingTypes: wasmLoadingType().array().optional(), - wasmLoading: wasmLoading().optional(), - enabledLibraryTypes: libraryType().or(libraryType().array()).optional(), - globalObject: z.string().min(1).optional(), - libraryExport: z.string().min(1).or(z.string().min(1).array()).optional(), - libraryTarget: libraryType().optional(), - hashFunction: z.string().or(z.function()).optional(), - // TODO(hyf0) - module: z.any().optional(), - strictModuleErrorHandling: z.boolean().optional(), - umdNamedDefine: umdNamedDefine.optional(), - chunkLoadingGlobal: z.string().optional(), - trustedTypes: z - .literal(true) - .or(z.string()) - .or( - z.strictObject({ - policyName: z.string().optional() - }) - ) - .optional(), - hashDigest: z.string().optional(), - hashDigestLength: z.number().optional(), - library: libraryName.or(libraryOptions).optional(), - asyncChunks: z.boolean().optional(), - workerChunkLoading: chunkLoading().optional(), - workerWasmLoading: wasmLoading().optional(), - workerPublicPath: workerPublicPath().optional() - }); -} diff --git a/packages/rspack/src/config/zod/plugins.ts b/packages/rspack/src/config/zod/plugins.ts deleted file mode 100644 index c071fbb8c84..00000000000 --- a/packages/rspack/src/config/zod/plugins.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { z } from "zod"; -import { Compiler } from "../../Compiler"; - -const rspackPluginFunction = z - .function() - .args(z.instanceof(Compiler)) - .returns(z.void()); - -const rspackPluginInstance = z.object({ - name: z.string().optional(), - apply: rspackPluginFunction -}); - -export function plugins() { - const functionOrInstance = rspackPluginFunction.or(rspackPluginInstance); - return functionOrInstance.array(); -} diff --git a/packages/rspack/src/config/zod/resolve.ts b/packages/rspack/src/config/zod/resolve.ts deleted file mode 100644 index 0dbfb0cba78..00000000000 --- a/packages/rspack/src/config/zod/resolve.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { z } from "zod"; - -const resolveAlias = z.record( - z - .literal(false) - .or(z.string()) - .or(z.array(z.literal(false).or(z.string()))) -); - -const baseResolve = z.strictObject({ - alias: resolveAlias.optional(), - browserField: z.boolean().optional(), - conditionNames: z.string().array().optional(), - extensions: z.string().array().optional(), - fallback: resolveAlias.optional(), - mainFields: z.string().array().optional(), - mainFiles: z.string().array().optional(), - modules: z.string().array().optional(), - preferRelative: z.boolean().optional(), - tsConfigPath: z.string().optional(), - fullySpecified: z.boolean().optional(), - exportsFields: z.string().array().optional(), - extensionAlias: z.record(z.string().or(z.string().array())).optional() -}); - -// Recursive types need to write this way. -// See https://zod.dev/?id=recursive-types -type ResolveConfig = z.infer & { - byDependency?: Record; -}; - -export function resolve(): z.ZodType { - return baseResolve.extend({ - byDependency: z.lazy(() => z.record(resolve())).optional() - }); -} diff --git a/packages/rspack/src/config/zod/snapshot.ts b/packages/rspack/src/config/zod/snapshot.ts deleted file mode 100644 index c6542177981..00000000000 --- a/packages/rspack/src/config/zod/snapshot.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { z } from "zod"; - -export function snapshot() { - return z.strictObject({ - module: z - .strictObject({ - hash: z.boolean().optional(), - timestamp: z.boolean().optional() - }) - .optional(), - resolve: z - .strictObject({ - hash: z.boolean().optional(), - timestamp: z.boolean().optional() - }) - .optional() - }); -} diff --git a/packages/rspack/src/config/zod/stats.ts b/packages/rspack/src/config/zod/stats.ts deleted file mode 100644 index ef0fc7e5b90..00000000000 --- a/packages/rspack/src/config/zod/stats.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { z } from "zod"; - -export function stats() { - return z - .enum(["none", "errors-only", "errors-warnings", "normal", "verbose"]) - .or(z.boolean()) - .or( - z.object({ - all: z.boolean().optional(), - assets: z.boolean().optional(), - chunkGroups: z.boolean().optional(), - chunks: z.boolean().optional(), - colors: z.boolean().optional(), - entrypoints: z.boolean().optional(), - errors: z.boolean().optional(), - errorsCount: z.boolean().optional(), - hash: z.boolean().optional(), - modules: z.boolean().optional(), - preset: z - .enum(["normal", "none", "verbose", "errors-only", "errors-warnings"]) - .optional(), - publicPath: z.boolean().optional(), - reasons: z.boolean().optional(), - warnings: z.boolean().optional(), - warningsCount: z.boolean().optional(), - outputPath: z.boolean().optional(), - chunkModules: z.boolean().optional(), - chunkRelations: z.boolean().optional(), - timings: z.boolean().optional(), - builtAt: z.boolean().optional(), - nestedModules: z.boolean().optional(), - source: z.boolean().optional() - }) - ); -} diff --git a/packages/rspack/src/config/zod/target.ts b/packages/rspack/src/config/zod/target.ts deleted file mode 100644 index 4eda8f5f7aa..00000000000 --- a/packages/rspack/src/config/zod/target.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { z } from "zod"; - -const allowTarget = z - .enum([ - "web", - "webworker", - "es3", - "es5", - "es2015", - "es2016", - "es2017", - "es2018", - "es2019", - "es2020", - "es2021", - "es2022", - "browserslist" - ]) - .or(z.literal("node")) - .or(z.literal("async-node")) - .or( - z.custom<`node${number}`>( - value => typeof value === "string" && /^node\d+$/.test(value) - ) - ) - .or( - z.custom<`async-node${number}`>( - value => typeof value === "string" && /^async-node\d+$/.test(value) - ) - ) - .or( - z.custom<`node${number}.${number}`>( - value => typeof value === "string" && /^node\d+\.\d+$/.test(value) - ) - ) - .or( - z.custom<`async-node${number}.${number}`>( - value => typeof value === "string" && /^async-node\d+\.\d+$/.test(value) - ) - ) - .or(z.literal("electron-main")) - .or( - z.custom<`electron${number}-main`>( - value => typeof value === "string" && /^electron\d+-main$/.test(value) - ) - ) - .or( - z.custom<`electron${number}.${number}-main`>( - value => - typeof value === "string" && /^electron\d+\.\d+-main$/.test(value) - ) - ) - .or(z.literal("electron-renderer")) - .or( - z.custom<`electron${number}-renderer`>( - value => typeof value === "string" && /^electron\d+-renderer$/.test(value) - ) - ) - .or( - z.custom<`electron${number}.${number}-renderer`>( - value => - typeof value === "string" && /^electron\d+\.\d+-renderer$/.test(value) - ) - ) - .or(z.literal("electron-preload")) - .or( - z.custom<`electron${number}-preload`>( - value => typeof value === "string" && /^electron\d+-preload$/.test(value) - ) - ) - .or( - z.custom<`electron${number}.${number}-preload`>( - value => - typeof value === "string" && /^electron\d+\.\d+-preload$/.test(value) - ) - ); - -export function target() { - return z.literal(false).or(allowTarget).or(allowTarget.array()); -} diff --git a/packages/rspack/src/config/zod/watch-options.ts b/packages/rspack/src/config/zod/watch-options.ts deleted file mode 100644 index 783c39fd7dd..00000000000 --- a/packages/rspack/src/config/zod/watch-options.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { z } from "zod"; - -export function watchOptions() { - return z - .object({ - aggregateTimeout: z.number().optional(), - followSymlinks: z.boolean().optional(), - ignored: z - .string() - .array() - .or(z.instanceof(RegExp)) - .or(z.string()) - .optional(), - poll: z.number().or(z.boolean()).optional(), - stdin: z.boolean().optional() - }) - .strict(); -} diff --git a/packages/rspack/src/index.ts b/packages/rspack/src/index.ts index c533891d5a6..b4ec6762100 100644 --- a/packages/rspack/src/index.ts +++ b/packages/rspack/src/index.ts @@ -10,7 +10,6 @@ export * from "./MultiStats"; export * from "./ChunkGroup"; export * from "./NormalModuleFactory"; export { cachedCleverMerge as cleverMerge } from "./util/cleverMerge"; -export { BannerPlugin } from "./lib/BannerPlugin"; export { EnvironmentPlugin } from "./lib/EnvironmentPlugin"; export { LoaderOptionsPlugin } from "./lib/LoaderOptionsPlugin"; export { @@ -23,6 +22,7 @@ export type OptimizationSplitChunksOptions = NonNullable< Configuration["optimization"] >["splitChunks"]; export { + BannerPlugin, DefinePlugin, ProvidePlugin, ProgressPlugin, @@ -34,10 +34,10 @@ export { ExternalsPlugin } from "./builtin-plugin"; export type { + BannerPluginArgument, DefinePluginOptions, ProvidePluginOptions, - ProgressPluginOptions, - BannerPluginOptions, + ProgressPluginArgument, HtmlPluginOptions, SwcJsMinimizerPluginOptions, CopyPluginOptions, diff --git a/packages/rspack/src/lib/BannerPlugin.js b/packages/rspack/src/lib/BannerPlugin.js deleted file mode 100644 index 0f5fb6a6cd4..00000000000 --- a/packages/rspack/src/lib/BannerPlugin.js +++ /dev/null @@ -1,127 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -"use strict"; - -const { ConcatSource } = require("webpack-sources"); -const Compilation = require("../Compilation"); -const ModuleFilenameHelpers = require("./ModuleFilenameHelpers"); -const Template = require("./Template"); -const createSchemaValidation = require("./util/create-schema-validation"); - -/** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginArgument} BannerPluginArgument */ -/** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginOptions} BannerPluginOptions */ -/** @typedef {import("./Compiler")} Compiler */ - -const validate = createSchemaValidation( - require("../schemas/plugins/BannerPlugin.check.js"), - () => require("../schemas/plugins/BannerPlugin.json"), - { - name: "Banner Plugin", - baseDataPath: "options" - } -); - -const wrapComment = str => { - if (!str.includes("\n")) { - return Template.toComment(str); - } - return `/*!\n * ${str - .replace(/\*\//g, "* /") - .split("\n") - .join("\n * ") - .replace(/\s+\n/g, "\n") - .trimEnd()}\n */`; -}; - -class BannerPlugin { - /** - * @param {BannerPluginArgument} options options object - */ - constructor(options) { - if (typeof options === "string" || typeof options === "function") { - options = { - banner: options - }; - } - - validate(options); - - this.options = options; - - const bannerOption = options.banner; - if (typeof bannerOption === "function") { - const getBanner = bannerOption; - this.banner = this.options.raw - ? getBanner - : data => wrapComment(getBanner(data)); - } else { - const banner = this.options.raw - ? bannerOption - : wrapComment(bannerOption); - this.banner = () => banner; - } - } - - /** - * Apply the plugin - * @param {Compiler} compiler the compiler instance - * @returns {void} - */ - apply(compiler) { - const options = this.options; - const banner = this.banner; - const matchObject = ModuleFilenameHelpers.matchObject.bind( - undefined, - options - ); - const cache = new WeakMap(); - - compiler.hooks.compilation.tap("BannerPlugin", compilation => { - compilation.hooks.processAssets.tap( - { - name: "BannerPlugin", - stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS - }, - () => { - for (const chunk of compilation.chunks) { - if (options.entryOnly && !chunk.canBeInitial()) { - continue; - } - - for (const file of chunk.files) { - if (!matchObject(file)) { - continue; - } - - const data = { - chunk, - filename: file - }; - - let normalizedBanner = - typeof banner === "function" ? banner({ chunk }) : banner; - const comment = compilation.getPath(normalizedBanner, data); - - compilation.updateAsset(file, old => { - let cached = cache.get(old); - if (!cached || cached.comment !== comment) { - const source = options.footer - ? new ConcatSource(old, "\n", comment) - : new ConcatSource(comment, "\n", old); - cache.set(old, { source, comment }); - return source; - } - return cached.source; - }); - } - } - } - ); - }); - } -} - -export { BannerPlugin }; diff --git a/packages/rspack/src/lib/LoaderOptionsPlugin.js b/packages/rspack/src/lib/LoaderOptionsPlugin.js index 7bc3670ab67..e24e730dc03 100644 --- a/packages/rspack/src/lib/LoaderOptionsPlugin.js +++ b/packages/rspack/src/lib/LoaderOptionsPlugin.js @@ -7,25 +7,15 @@ const ModuleFilenameHelpers = require("./ModuleFilenameHelpers"); const { NormalModule } = require("../NormalModule"); -const createSchemaValidation = require("./util/create-schema-validation"); /** @typedef {import("../declarations/plugins/LoaderOptionsPlugin").LoaderOptionsPluginOptions} LoaderOptionsPluginOptions */ /** @typedef {import("./Compiler")} Compiler */ -const validate = createSchemaValidation( - require("../schemas/plugins/LoaderOptionsPlugin.check.js"), - () => require("../schemas/plugins/LoaderOptionsPlugin.json"), - { - name: "Loader Options Plugin", - baseDataPath: "options" - } -); class LoaderOptionsPlugin { /** * @param {LoaderOptionsPluginOptions} options options object */ constructor(options = {}) { - validate(options); if (typeof options !== "object") options = {}; if (!options.test) { options.test = { diff --git a/packages/rspack/src/lib/Template.js b/packages/rspack/src/lib/Template.js deleted file mode 100644 index 35c17ec2b97..00000000000 --- a/packages/rspack/src/lib/Template.js +++ /dev/null @@ -1,419 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -"use strict"; - -const { ConcatSource, PrefixSource } = require("webpack-sources"); - -/** @typedef {import("webpack-sources").Source} Source */ -/** @typedef {import("../declarations/WebpackOptions").Output} OutputOptions */ -/** @typedef {import("./Chunk")} Chunk */ -/** @typedef {import("./ChunkGraph")} ChunkGraph */ -/** @typedef {import("./CodeGenerationResults")} CodeGenerationResults */ -/** @typedef {import("./Compilation").AssetInfo} AssetInfo */ -/** @typedef {import("./Compilation").PathData} PathData */ -/** @typedef {import("./DependencyTemplates")} DependencyTemplates */ -/** @typedef {import("./Module")} Module */ -/** @typedef {import("./ModuleGraph")} ModuleGraph */ -/** @typedef {import("./ModuleTemplate")} ModuleTemplate */ -/** @typedef {import("./RuntimeModule")} RuntimeModule */ -/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */ -/** @typedef {import("./javascript/JavascriptModulesPlugin").ChunkRenderContext} ChunkRenderContext */ -/** @typedef {import("./javascript/JavascriptModulesPlugin").RenderContext} RenderContext */ - -const START_LOWERCASE_ALPHABET_CODE = "a".charCodeAt(0); -const START_UPPERCASE_ALPHABET_CODE = "A".charCodeAt(0); -const DELTA_A_TO_Z = "z".charCodeAt(0) - START_LOWERCASE_ALPHABET_CODE + 1; -const NUMBER_OF_IDENTIFIER_START_CHARS = DELTA_A_TO_Z * 2 + 2; // a-z A-Z _ $ -const NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS = - NUMBER_OF_IDENTIFIER_START_CHARS + 10; // a-z A-Z _ $ 0-9 -const FUNCTION_CONTENT_REGEX = /^function\s?\(\)\s?\{\r?\n?|\r?\n?\}$/g; -const INDENT_MULTILINE_REGEX = /^\t/gm; -const LINE_SEPARATOR_REGEX = /\r?\n/g; -const IDENTIFIER_NAME_REPLACE_REGEX = /^([^a-zA-Z$_])/; -const IDENTIFIER_ALPHA_NUMERIC_NAME_REPLACE_REGEX = /[^a-zA-Z0-9$]+/g; -const COMMENT_END_REGEX = /\*\//g; -const PATH_NAME_NORMALIZE_REPLACE_REGEX = /[^a-zA-Z0-9_!§$()=\-^°]+/g; -const MATCH_PADDED_HYPHENS_REPLACE_REGEX = /^-|-$/g; - -/** - * @typedef {Object} RenderManifestOptions - * @property {Chunk} chunk the chunk used to render - * @property {string} hash - * @property {string} fullHash - * @property {OutputOptions} outputOptions - * @property {CodeGenerationResults} codeGenerationResults - * @property {{javascript: ModuleTemplate}} moduleTemplates - * @property {DependencyTemplates} dependencyTemplates - * @property {RuntimeTemplate} runtimeTemplate - * @property {ModuleGraph} moduleGraph - * @property {ChunkGraph} chunkGraph - */ - -/** @typedef {RenderManifestEntryTemplated | RenderManifestEntryStatic} RenderManifestEntry */ - -/** - * @typedef {Object} RenderManifestEntryTemplated - * @property {function(): Source} render - * @property {string | function(PathData, AssetInfo=): string} filenameTemplate - * @property {PathData=} pathOptions - * @property {AssetInfo=} info - * @property {string} identifier - * @property {string=} hash - * @property {boolean=} auxiliary - */ - -/** - * @typedef {Object} RenderManifestEntryStatic - * @property {function(): Source} render - * @property {string} filename - * @property {AssetInfo} info - * @property {string} identifier - * @property {string=} hash - * @property {boolean=} auxiliary - */ - -/** - * @typedef {Object} HasId - * @property {number | string} id - */ - -/** - * @typedef {function(Module, number): boolean} ModuleFilterPredicate - */ - -class Template { - /** - * - * @param {Function} fn a runtime function (.runtime.js) "template" - * @returns {string} the updated and normalized function string - */ - static getFunctionContent(fn) { - return fn - .toString() - .replace(FUNCTION_CONTENT_REGEX, "") - .replace(INDENT_MULTILINE_REGEX, "") - .replace(LINE_SEPARATOR_REGEX, "\n"); - } - - /** - * @param {string} str the string converted to identifier - * @returns {string} created identifier - */ - static toIdentifier(str) { - if (typeof str !== "string") return ""; - return str - .replace(IDENTIFIER_NAME_REPLACE_REGEX, "_$1") - .replace(IDENTIFIER_ALPHA_NUMERIC_NAME_REPLACE_REGEX, "_"); - } - /** - * - * @param {string} str string to be converted to commented in bundle code - * @returns {string} returns a commented version of string - */ - static toComment(str) { - if (!str) return ""; - return `/*! ${str.replace(COMMENT_END_REGEX, "* /")} */`; - } - - /** - * - * @param {string} str string to be converted to "normal comment" - * @returns {string} returns a commented version of string - */ - static toNormalComment(str) { - if (!str) return ""; - return `/* ${str.replace(COMMENT_END_REGEX, "* /")} */`; - } - - /** - * @param {string} str string path to be normalized - * @returns {string} normalized bundle-safe path - */ - static toPath(str) { - if (typeof str !== "string") return ""; - return str - .replace(PATH_NAME_NORMALIZE_REPLACE_REGEX, "-") - .replace(MATCH_PADDED_HYPHENS_REPLACE_REGEX, ""); - } - - // map number to a single character a-z, A-Z or multiple characters if number is too big - /** - * @param {number} n number to convert to ident - * @returns {string} returns single character ident - */ - static numberToIdentifier(n) { - if (n >= NUMBER_OF_IDENTIFIER_START_CHARS) { - // use multiple letters - return ( - Template.numberToIdentifier(n % NUMBER_OF_IDENTIFIER_START_CHARS) + - Template.numberToIdentifierContinuation( - Math.floor(n / NUMBER_OF_IDENTIFIER_START_CHARS) - ) - ); - } - - // lower case - if (n < DELTA_A_TO_Z) { - return String.fromCharCode(START_LOWERCASE_ALPHABET_CODE + n); - } - n -= DELTA_A_TO_Z; - - // upper case - if (n < DELTA_A_TO_Z) { - return String.fromCharCode(START_UPPERCASE_ALPHABET_CODE + n); - } - - if (n === DELTA_A_TO_Z) return "_"; - return "$"; - } - - /** - * @param {number} n number to convert to ident - * @returns {string} returns single character ident - */ - static numberToIdentifierContinuation(n) { - if (n >= NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS) { - // use multiple letters - return ( - Template.numberToIdentifierContinuation( - n % NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS - ) + - Template.numberToIdentifierContinuation( - Math.floor(n / NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS) - ) - ); - } - - // lower case - if (n < DELTA_A_TO_Z) { - return String.fromCharCode(START_LOWERCASE_ALPHABET_CODE + n); - } - n -= DELTA_A_TO_Z; - - // upper case - if (n < DELTA_A_TO_Z) { - return String.fromCharCode(START_UPPERCASE_ALPHABET_CODE + n); - } - n -= DELTA_A_TO_Z; - - // numbers - if (n < 10) { - return `${n}`; - } - - if (n === 10) return "_"; - return "$"; - } - - /** - * - * @param {string | string[]} s string to convert to identity - * @returns {string} converted identity - */ - static indent(s) { - if (Array.isArray(s)) { - return s.map(Template.indent).join("\n"); - } else { - const str = s.trimEnd(); - if (!str) return ""; - const ind = str[0] === "\n" ? "" : "\t"; - return ind + str.replace(/\n([^\n])/g, "\n\t$1"); - } - } - - /** - * - * @param {string|string[]} s string to create prefix for - * @param {string} prefix prefix to compose - * @returns {string} returns new prefix string - */ - static prefix(s, prefix) { - const str = Template.asString(s).trim(); - if (!str) return ""; - const ind = str[0] === "\n" ? "" : prefix; - return ind + str.replace(/\n([^\n])/g, "\n" + prefix + "$1"); - } - - /** - * - * @param {string|string[]} str string or string collection - * @returns {string} returns a single string from array - */ - static asString(str) { - if (Array.isArray(str)) { - return str.join("\n"); - } - return str; - } - - /** - * @typedef {Object} WithId - * @property {string|number} id - */ - - /** - * @param {WithId[]} modules a collection of modules to get array bounds for - * @returns {[number, number] | false} returns the upper and lower array bounds - * or false if not every module has a number based id - */ - static getModulesArrayBounds(modules) { - let maxId = -Infinity; - let minId = Infinity; - for (const module of modules) { - const moduleId = module.id; - if (typeof moduleId !== "number") return false; - if (maxId < moduleId) maxId = moduleId; - if (minId > moduleId) minId = moduleId; - } - if (minId < 16 + ("" + minId).length) { - // add minId x ',' instead of 'Array(minId).concat(…)' - minId = 0; - } - // start with -1 because the first module needs no comma - let objectOverhead = -1; - for (const module of modules) { - // module id + colon + comma - objectOverhead += `${module.id}`.length + 2; - } - // number of commas, or when starting non-zero the length of Array(minId).concat() - const arrayOverhead = minId === 0 ? maxId : 16 + `${minId}`.length + maxId; - return arrayOverhead < objectOverhead ? [minId, maxId] : false; - } - - /** - * @param {ChunkRenderContext} renderContext render context - * @param {Module[]} modules modules to render (should be ordered by identifier) - * @param {function(Module): Source} renderModule function to render a module - * @param {string=} prefix applying prefix strings - * @returns {Source} rendered chunk modules in a Source object - */ - static renderChunkModules(renderContext, modules, renderModule, prefix = "") { - const { chunkGraph } = renderContext; - var source = new ConcatSource(); - if (modules.length === 0) { - return null; - } - /** @type {{id: string|number, source: Source|string}[]} */ - const allModules = modules.map(module => { - return { - id: chunkGraph.getModuleId(module), - source: renderModule(module) || "false" - }; - }); - const bounds = Template.getModulesArrayBounds(allModules); - if (bounds) { - // Render a spare array - const minId = bounds[0]; - const maxId = bounds[1]; - if (minId !== 0) { - source.add(`Array(${minId}).concat(`); - } - source.add("[\n"); - /** @type {Map} */ - const modules = new Map(); - for (const module of allModules) { - modules.set(module.id, module); - } - for (let idx = minId; idx <= maxId; idx++) { - const module = modules.get(idx); - if (idx !== minId) { - source.add(",\n"); - } - source.add(`/* ${idx} */`); - if (module) { - source.add("\n"); - source.add(module.source); - } - } - source.add("\n" + prefix + "]"); - if (minId !== 0) { - source.add(")"); - } - } else { - // Render an object - source.add("{\n"); - for (let i = 0; i < allModules.length; i++) { - const module = allModules[i]; - if (i !== 0) { - source.add(",\n"); - } - source.add(`\n/***/ ${JSON.stringify(module.id)}:\n`); - source.add(module.source); - } - source.add(`\n\n${prefix}}`); - } - return source; - } - - /** - * @param {RuntimeModule[]} runtimeModules array of runtime modules in order - * @param {RenderContext & { codeGenerationResults?: CodeGenerationResults }} renderContext render context - * @returns {Source} rendered runtime modules in a Source object - */ - static renderRuntimeModules(runtimeModules, renderContext) { - const source = new ConcatSource(); - for (const module of runtimeModules) { - const codeGenerationResults = renderContext.codeGenerationResults; - let runtimeSource; - if (codeGenerationResults) { - runtimeSource = codeGenerationResults.getSource( - module, - renderContext.chunk.runtime, - "runtime" - ); - } else { - const codeGenResult = module.codeGeneration({ - chunkGraph: renderContext.chunkGraph, - dependencyTemplates: renderContext.dependencyTemplates, - moduleGraph: renderContext.moduleGraph, - runtimeTemplate: renderContext.runtimeTemplate, - runtime: renderContext.chunk.runtime, - codeGenerationResults - }); - if (!codeGenResult) continue; - runtimeSource = codeGenResult.sources.get("runtime"); - } - if (runtimeSource) { - source.add(Template.toNormalComment(module.identifier()) + "\n"); - if (!module.shouldIsolate()) { - source.add(runtimeSource); - source.add("\n\n"); - } else if (renderContext.runtimeTemplate.supportsArrowFunction()) { - source.add("(() => {\n"); - source.add(new PrefixSource("\t", runtimeSource)); - source.add("\n})();\n\n"); - } else { - source.add("!function() {\n"); - source.add(new PrefixSource("\t", runtimeSource)); - source.add("\n}();\n\n"); - } - } - } - return source; - } - - /** - * @param {RuntimeModule[]} runtimeModules array of runtime modules in order - * @param {RenderContext} renderContext render context - * @returns {Source} rendered chunk runtime modules in a Source object - */ - static renderChunkRuntimeModules(runtimeModules, renderContext) { - return new PrefixSource( - "/******/ ", - new ConcatSource( - "function(__webpack_require__) { // webpackRuntimeModules\n", - this.renderRuntimeModules(runtimeModules, renderContext), - "}\n" - ) - ); - } -} - -module.exports = Template; -module.exports.NUMBER_OF_IDENTIFIER_START_CHARS = - NUMBER_OF_IDENTIFIER_START_CHARS; -module.exports.NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS = - NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS; diff --git a/packages/rspack/src/lib/util/create-schema-validation.js b/packages/rspack/src/lib/util/create-schema-validation.js deleted file mode 100644 index 290eaf47d65..00000000000 --- a/packages/rspack/src/lib/util/create-schema-validation.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -"use strict"; - -const memoize = require("./memoize"); - -const getValidate = memoize(() => require("schema-utils").validate); - -const createSchemaValidation = (check, getSchema, options) => { - getSchema = memoize(getSchema); - return value => { - if (check && !check(value)) { - getValidate()(getSchema(), value, options); - if (check) { - require("util").deprecate( - () => {}, - "webpack bug: Pre-compiled schema reports error while real schema is happy. This has performance drawbacks.", - "DEP_WEBPACK_PRE_COMPILED_SCHEMA_INVALID" - )(); - } - } - }; -}; - -module.exports = createSchemaValidation; diff --git a/packages/rspack/src/lib/util/memoize.js b/packages/rspack/src/lib/util/memoize.js deleted file mode 100644 index 981b5318882..00000000000 --- a/packages/rspack/src/lib/util/memoize.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php -*/ - -"use strict"; - -/** @template T @typedef {function(): T} FunctionReturning */ - -/** - * @template T - * @param {FunctionReturning} fn memorized function - * @returns {FunctionReturning} new function - */ -const memoize = fn => { - let cache = false; - /** @type {T} */ - let result = undefined; - return () => { - if (cache) { - return result; - } else { - result = fn(); - cache = true; - // Allow to clean up memory for fn - // and all dependent resources - fn = undefined; - return result; - } - }; -}; - -module.exports = memoize; diff --git a/packages/rspack/src/logging/Logger.ts b/packages/rspack/src/logging/Logger.ts index 8a112d3d94e..5f46bfad3b5 100644 --- a/packages/rspack/src/logging/Logger.ts +++ b/packages/rspack/src/logging/Logger.ts @@ -27,7 +27,8 @@ export const LogType = Object.freeze({ time: /** @type {"time"} */ "time", // name, time as [seconds, nanoseconds] clear: /** @type {"clear"} */ "clear", // no arguments - status: /** @type {"status"} */ "status" // message, arguments + status: /** @type {"status"} */ "status", // message, arguments + cache: /** @type {"cache"} */ "cache" // [hit, total] }); export function getLogTypeBitFlag(type: LogTypeEnum) { diff --git a/packages/rspack/src/rspack.ts b/packages/rspack/src/rspack.ts index d0b00df9b76..2ee3d658475 100644 --- a/packages/rspack/src/rspack.ts +++ b/packages/rspack/src/rspack.ts @@ -13,7 +13,7 @@ import { applyRspackOptionsBaseDefaults, applyRspackOptionsDefaults, RspackPluginFunction, - validateConfig + rspackOptions } from "./config"; import { Compiler } from "./Compiler"; import { Stats } from "./Stats"; @@ -30,7 +30,7 @@ import { Callback } from "tapable"; import MultiStats from "./MultiStats"; import assert from "assert"; import { asArray, isNil } from "./util"; -import IgnoreWarningsPlugin from "./lib/ignoreWarningsPlugin"; +import { validate } from "./util/validate"; function createMultiCompiler(options: MultiRspackOptions): MultiCompiler { const compilers = options.map(createCompiler); @@ -113,7 +113,7 @@ function rspack( callback?: Callback | Callback ) { asArray(options).every(opts => { - validateConfig(opts); + validate(opts, rspackOptions); }); const create = () => { if (isMultiRspackOptions(options)) { diff --git a/packages/rspack/src/rspackOptionsApply.ts b/packages/rspack/src/rspackOptionsApply.ts index aa535748e73..860164cada6 100644 --- a/packages/rspack/src/rspackOptionsApply.ts +++ b/packages/rspack/src/rspackOptionsApply.ts @@ -10,7 +10,8 @@ import { RspackOptionsNormalized, Compiler, - OptimizationRuntimeChunkNormalized + OptimizationRuntimeChunkNormalized, + RspackPluginFunction } from "."; import fs from "graceful-fs"; @@ -96,7 +97,7 @@ export class RspackOptionsApply { if (minimize && minimizer) { for (const item of minimizer) { if (typeof item === "function") { - item.call(compiler, compiler); + (item as RspackPluginFunction).call(compiler, compiler); } else if (item !== "...") { item.apply(compiler); } diff --git a/packages/rspack/src/schemas/plugins/BannerPlugin.check.js b/packages/rspack/src/schemas/plugins/BannerPlugin.check.js deleted file mode 100644 index 7e5578b8bd1..00000000000 --- a/packages/rspack/src/schemas/plugins/BannerPlugin.check.js +++ /dev/null @@ -1,327 +0,0 @@ -/* - * This file was automatically generated. - * DO NOT MODIFY BY HAND. - * Run `yarn special-lint-fix` to update - */ -"use strict"; -function n( - t, - { - instancePath: l = "", - parentData: e, - parentDataProperty: s, - rootData: a = t - } = {} -) { - let r = null, - o = 0; - const u = o; - let i = !1; - const p = o; - if (o === p) - if (Array.isArray(t)) { - const n = t.length; - for (let l = 0; l < n; l++) { - let n = t[l]; - const e = o, - s = o; - let a = !1, - u = null; - const i = o, - p = o; - let f = !1; - const h = o; - if (!(n instanceof RegExp)) { - const n = { params: {} }; - null === r ? (r = [n]) : r.push(n), o++; - } - var c = h === o; - if (((f = f || c), !f)) { - const t = o; - if (o === t) - if ("string" == typeof n) { - if (n.length < 1) { - const n = { params: {} }; - null === r ? (r = [n]) : r.push(n), o++; - } - } else { - const n = { params: { type: "string" } }; - null === r ? (r = [n]) : r.push(n), o++; - } - (c = t === o), (f = f || c); - } - if (f) (o = p), null !== r && (p ? (r.length = p) : (r = null)); - else { - const n = { params: {} }; - null === r ? (r = [n]) : r.push(n), o++; - } - if ((i === o && ((a = !0), (u = 0)), a)) - (o = s), null !== r && (s ? (r.length = s) : (r = null)); - else { - const n = { params: { passingSchemas: u } }; - null === r ? (r = [n]) : r.push(n), o++; - } - if (e !== o) break; - } - } else { - const n = { params: { type: "array" } }; - null === r ? (r = [n]) : r.push(n), o++; - } - var f = p === o; - if (((i = i || f), !i)) { - const n = o, - l = o; - let e = !1; - const s = o; - if (!(t instanceof RegExp)) { - const n = { params: {} }; - null === r ? (r = [n]) : r.push(n), o++; - } - var h = s === o; - if (((e = e || h), !e)) { - const n = o; - if (o === n) - if ("string" == typeof t) { - if (t.length < 1) { - const n = { params: {} }; - null === r ? (r = [n]) : r.push(n), o++; - } - } else { - const n = { params: { type: "string" } }; - null === r ? (r = [n]) : r.push(n), o++; - } - (h = n === o), (e = e || h); - } - if (e) (o = l), null !== r && (l ? (r.length = l) : (r = null)); - else { - const n = { params: {} }; - null === r ? (r = [n]) : r.push(n), o++; - } - (f = n === o), (i = i || f); - } - if (!i) { - const t = { params: {} }; - return null === r ? (r = [t]) : r.push(t), o++, (n.errors = r), !1; - } - return ( - (o = u), - null !== r && (u ? (r.length = u) : (r = null)), - (n.errors = r), - 0 === o - ); -} -function t( - l, - { - instancePath: e = "", - parentData: s, - parentDataProperty: a, - rootData: r = l - } = {} -) { - let o = null, - u = 0; - const i = u; - let p = !1; - const c = u; - if (u === c) - if ("string" == typeof l) { - if (l.length < 1) { - const n = { params: {} }; - null === o ? (o = [n]) : o.push(n), u++; - } - } else { - const n = { params: { type: "string" } }; - null === o ? (o = [n]) : o.push(n), u++; - } - var f = c === u; - if (((p = p || f), !p)) { - const t = u; - if (u === t) - if (l && "object" == typeof l && !Array.isArray(l)) { - let t; - if (void 0 === l.banner && (t = "banner")) { - const n = { params: { missingProperty: t } }; - null === o ? (o = [n]) : o.push(n), u++; - } else { - const t = u; - for (const n in l) - if ( - "banner" !== n && - "entryOnly" !== n && - "exclude" !== n && - "footer" !== n && - "include" !== n && - "raw" !== n && - "test" !== n - ) { - const t = { params: { additionalProperty: n } }; - null === o ? (o = [t]) : o.push(t), u++; - break; - } - if (t === u) { - if (void 0 !== l.banner) { - let n = l.banner; - const t = u, - e = u; - let s = !1; - const a = u; - if ("string" != typeof n) { - const n = { params: { type: "string" } }; - null === o ? (o = [n]) : o.push(n), u++; - } - var h = a === u; - if (((s = s || h), !s)) { - const t = u; - if (!(n instanceof Function)) { - const n = { params: {} }; - null === o ? (o = [n]) : o.push(n), u++; - } - (h = t === u), (s = s || h); - } - if (s) (u = e), null !== o && (e ? (o.length = e) : (o = null)); - else { - const n = { params: {} }; - null === o ? (o = [n]) : o.push(n), u++; - } - var y = t === u; - } else y = !0; - if (y) { - if (void 0 !== l.entryOnly) { - const n = u; - if ("boolean" != typeof l.entryOnly) { - const n = { params: { type: "boolean" } }; - null === o ? (o = [n]) : o.push(n), u++; - } - y = n === u; - } else y = !0; - if (y) { - if (void 0 !== l.exclude) { - const t = u, - s = u; - let a = !1, - i = null; - const p = u; - if ( - (n(l.exclude, { - instancePath: e + "/exclude", - parentData: l, - parentDataProperty: "exclude", - rootData: r - }) || - ((o = null === o ? n.errors : o.concat(n.errors)), - (u = o.length)), - p === u && ((a = !0), (i = 0)), - a) - ) - (u = s), null !== o && (s ? (o.length = s) : (o = null)); - else { - const n = { params: { passingSchemas: i } }; - null === o ? (o = [n]) : o.push(n), u++; - } - y = t === u; - } else y = !0; - if (y) { - if (void 0 !== l.footer) { - const n = u; - if ("boolean" != typeof l.footer) { - const n = { params: { type: "boolean" } }; - null === o ? (o = [n]) : o.push(n), u++; - } - y = n === u; - } else y = !0; - if (y) { - if (void 0 !== l.include) { - const t = u, - s = u; - let a = !1, - i = null; - const p = u; - if ( - (n(l.include, { - instancePath: e + "/include", - parentData: l, - parentDataProperty: "include", - rootData: r - }) || - ((o = null === o ? n.errors : o.concat(n.errors)), - (u = o.length)), - p === u && ((a = !0), (i = 0)), - a) - ) - (u = s), - null !== o && (s ? (o.length = s) : (o = null)); - else { - const n = { params: { passingSchemas: i } }; - null === o ? (o = [n]) : o.push(n), u++; - } - y = t === u; - } else y = !0; - if (y) { - if (void 0 !== l.raw) { - const n = u; - if ("boolean" != typeof l.raw) { - const n = { params: { type: "boolean" } }; - null === o ? (o = [n]) : o.push(n), u++; - } - y = n === u; - } else y = !0; - if (y) - if (void 0 !== l.test) { - const t = u, - s = u; - let a = !1, - i = null; - const p = u; - if ( - (n(l.test, { - instancePath: e + "/test", - parentData: l, - parentDataProperty: "test", - rootData: r - }) || - ((o = null === o ? n.errors : o.concat(n.errors)), - (u = o.length)), - p === u && ((a = !0), (i = 0)), - a) - ) - (u = s), - null !== o && (s ? (o.length = s) : (o = null)); - else { - const n = { params: { passingSchemas: i } }; - null === o ? (o = [n]) : o.push(n), u++; - } - y = t === u; - } else y = !0; - } - } - } - } - } - } - } - } else { - const n = { params: { type: "object" } }; - null === o ? (o = [n]) : o.push(n), u++; - } - if (((f = t === u), (p = p || f), !p)) { - const n = u; - if (!(l instanceof Function)) { - const n = { params: {} }; - null === o ? (o = [n]) : o.push(n), u++; - } - (f = n === u), (p = p || f); - } - } - if (!p) { - const n = { params: {} }; - return null === o ? (o = [n]) : o.push(n), u++, (t.errors = o), !1; - } - return ( - (u = i), - null !== o && (i ? (o.length = i) : (o = null)), - (t.errors = o), - 0 === u - ); -} -(module.exports = t), (module.exports.default = t); diff --git a/packages/rspack/src/schemas/plugins/BannerPlugin.json b/packages/rspack/src/schemas/plugins/BannerPlugin.json deleted file mode 100644 index 2e6bae067f7..00000000000 --- a/packages/rspack/src/schemas/plugins/BannerPlugin.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "definitions": { - "BannerFunction": { - "description": "The banner as function, it will be wrapped in a comment.", - "instanceof": "Function", - "tsType": "(data: { hash: string, chunk: import('../../lib/Chunk'), filename: string }) => string" - }, - "Rule": { - "description": "Filtering rule as regex or string.", - "anyOf": [ - { - "instanceof": "RegExp", - "tsType": "RegExp" - }, - { - "type": "string", - "minLength": 1 - } - ] - }, - "Rules": { - "description": "Filtering rules.", - "anyOf": [ - { - "type": "array", - "items": { - "description": "A rule condition.", - "oneOf": [ - { - "$ref": "#/definitions/Rule" - } - ] - } - }, - { - "$ref": "#/definitions/Rule" - } - ] - } - }, - "title": "BannerPluginArgument", - "anyOf": [ - { - "description": "The banner as string, it will be wrapped in a comment.", - "type": "string", - "minLength": 1 - }, - { - "title": "BannerPluginOptions", - "type": "object", - "additionalProperties": false, - "properties": { - "banner": { - "description": "Specifies the banner.", - "anyOf": [ - { - "type": "string" - }, - { - "$ref": "#/definitions/BannerFunction" - } - ] - }, - "entryOnly": { - "description": "If true, the banner will only be added to the entry chunks.", - "type": "boolean" - }, - "exclude": { - "description": "Exclude all modules matching any of these conditions.", - "oneOf": [ - { - "$ref": "#/definitions/Rules" - } - ] - }, - "footer": { - "description": "If true, banner will be placed at the end of the output.", - "type": "boolean" - }, - "include": { - "description": "Include all modules matching any of these conditions.", - "oneOf": [ - { - "$ref": "#/definitions/Rules" - } - ] - }, - "raw": { - "description": "If true, banner will not be wrapped in a comment.", - "type": "boolean" - }, - "test": { - "description": "Include all modules that pass test assertion.", - "oneOf": [ - { - "$ref": "#/definitions/Rules" - } - ] - } - }, - "required": ["banner"] - }, - { - "$ref": "#/definitions/BannerFunction" - } - ] -} diff --git a/packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.check.js b/packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.check.js deleted file mode 100644 index d8cb9c6c3f1..00000000000 --- a/packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.check.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file was automatically generated. - * DO NOT MODIFY BY HAND. - * Run `yarn special-lint-fix` to update - */ -const r = /^(?:[A-Za-z]:[\\/]|\\\\|\/)/; -function e( - t, - { - instancePath: o = "", - parentData: a, - parentDataProperty: i, - rootData: n = t - } = {} -) { - if (!t || "object" != typeof t || Array.isArray(t)) - return (e.errors = [{ params: { type: "object" } }]), !1; - if (void 0 !== t.debug) { - const r = 0; - if ("boolean" != typeof t.debug) - return (e.errors = [{ params: { type: "boolean" } }]), !1; - var s = 0 === r; - } else s = !0; - if (s) { - if (void 0 !== t.minimize) { - const r = 0; - if ("boolean" != typeof t.minimize) - return (e.errors = [{ params: { type: "boolean" } }]), !1; - s = 0 === r; - } else s = !0; - if (s) - if (void 0 !== t.options) { - let o = t.options; - const a = 0; - if (0 === a) { - if (!o || "object" != typeof o || Array.isArray(o)) - return (e.errors = [{ params: { type: "object" } }]), !1; - if (void 0 !== o.context) { - let t = o.context; - if ("string" != typeof t) - return (e.errors = [{ params: { type: "string" } }]), !1; - if (t.includes("!") || !0 !== r.test(t)) - return (e.errors = [{ params: {} }]), !1; - } - } - s = 0 === a; - } else s = !0; - } - return (e.errors = null), !0; -} -(module.exports = e), (module.exports.default = e); diff --git a/packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.json b/packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.json deleted file mode 100644 index 912095c5975..00000000000 --- a/packages/rspack/src/schemas/plugins/LoaderOptionsPlugin.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "title": "LoaderOptionsPluginOptions", - "type": "object", - "additionalProperties": true, - "properties": { - "debug": { - "description": "Whether loaders should be in debug mode or not. debug will be removed as of webpack 3.", - "type": "boolean" - }, - "minimize": { - "description": "Where loaders can be switched to minimize mode.", - "type": "boolean" - }, - "options": { - "description": "A configuration object that can be used to configure older loaders.", - "type": "object", - "additionalProperties": true, - "properties": { - "context": { - "description": "The context that can be used to configure older loaders.", - "type": "string", - "absolutePath": true - } - } - } - } -} diff --git a/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts b/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts index 528f9a032ef..26e08e808a4 100644 --- a/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts +++ b/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts @@ -385,7 +385,11 @@ const SIMPLE_EXTRACTORS: SimpleExtractors = { } if (!context.cachedGetWarnings) { context.cachedGetWarnings = _compilation => { - return context._inner.getWarnings(); + const warnings = context._inner.getWarnings(); + + return compilation.hooks.processWarnings.call( + warnings as any + ) as unknown as typeof warnings; }; } if (compilation.name) { @@ -416,7 +420,8 @@ const SIMPLE_EXTRACTORS: SimpleExtractors = { LogType.profileEnd, LogType.time, LogType.status, - LogType.clear + LogType.clear, + LogType.cache ]); collapsedGroups = true; } else if (logging === "log" || logging === true) { @@ -708,12 +713,23 @@ const SIMPLE_EXTRACTORS: SimpleExtractors = { }, asset: { _: (object, asset, context: KnownStatsFactoryContext, options, factory) => { - Object.assign(object, asset); + object.type = asset.type; + object.name = asset.name; + object.size = asset.size; + object.emitted = asset.emitted; + object.info = asset.info; + Object.assign( + object, + factory.create(`${context.type}$visible`, asset, context) + ); } }, - chunk: { - _: (object, chunk) => { - Object.assign(object, chunk); + asset$visible: { + _: (object, asset) => { + object.chunkNames = asset.chunkNames; + }, + ids: (object, asset) => { + object.chunks = asset.chunks; } }, module: { @@ -725,11 +741,48 @@ const SIMPLE_EXTRACTORS: SimpleExtractors = { factory ) => { const { type } = context; - Object.assign(object, module); + object.type = module.type; + object.moduleType = module.moduleType; + object.size = module.size; + Object.assign(object, factory.create(`${type}$visible`, module, context)); + } + }, + module$visible: { + _: (object, module, context, options, factory) => { + const { type } = context; + object.identifier = module.identifier; + object.name = module.name; + object.nameForCondition = module.nameForCondition; + object.issuer = module.issuer; + object.issuerName = module.issuerName; + object.issuerPath = factory.create( + `${type.slice(0, -8)}.issuerPath`, + module.issuerPath, + context + ); const profile = module.profile; if (profile) { object.profile = factory.create(`${type}.profile`, profile, context); } + }, + ids: (object, module) => { + object.id = module.id; + object.issuerId = module.issuerId; + object.chunks = module.chunks; + }, + moduleAssets: (object, module) => { + object.assets = module.assets; + }, + reasons: (object, module, context, options, factory) => { + const { type } = context; + object.reasons = factory.create( + `${type.slice(0, -8)}.reasons`, + module.reasons, + context + ); + }, + source: (object, module) => { + object.source = module.source; } }, profile: { @@ -745,6 +798,53 @@ const SIMPLE_EXTRACTORS: SimpleExtractors = { }; Object.assign(object, statsProfile); } + }, + moduleIssuer: { + _: (object, module, context, options, factory) => { + object.identifier = module.identifier; + object.name = module.name; + }, + ids: (object, module) => { + object.id = module.id; + } + }, + moduleReason: { + _: (object, reason) => { + object.moduleIdentifier = reason.moduleIdentifier; + object.moduleName = reason.moduleName; + object.type = reason.type; + object.userRequest = reason.userRequest; + }, + ids: (object, reason) => { + object.moduleId = reason.moduleId; + } + }, + chunk: { + _: (object, chunk) => { + object.type = chunk.type; + object.initial = chunk.initial; + object.entry = chunk.entry; + object.size = chunk.size; + object.names = chunk.names; + object.files = chunk.files; + object.auxiliaryFiles = chunk.auxiliaryFiles; + }, + ids: (object, chunk) => { + object.id = chunk.id; + }, + chunkRelations: (object, chunk) => { + object.siblings = chunk.siblings; + object.parents = chunk.parents; + object.children = chunk.children; + }, + chunkModules: (object, chunk, context, options, factory) => { + const { type } = context; + object.modules = factory.create( + `${type}.modules`, + chunk.modules, + context + ); + } } }; diff --git a/packages/rspack/src/stats/DefaultStatsPrinterPlugin.ts b/packages/rspack/src/stats/DefaultStatsPrinterPlugin.ts index bc9e96a3545..6e50b374276 100644 --- a/packages/rspack/src/stats/DefaultStatsPrinterPlugin.ts +++ b/packages/rspack/src/stats/DefaultStatsPrinterPlugin.ts @@ -609,6 +609,8 @@ const SIMPLE_PRINTERS: Record< mapLines(message, x => `

${magenta(x)}`), "loggingEntry(time).loggingEntry.message": (message, { magenta }) => mapLines(message, x => ` ${magenta(x)}`), + "loggingEntry(cache).loggingEntry.message": (message, { magenta }) => + mapLines(message, x => ` ${magenta(x)}`), "loggingEntry(group).loggingEntry.message": (message, { cyan }) => mapLines(message, x => `<-> ${cyan(x)}`), "loggingEntry(groupCollapsed).loggingEntry.message": (message, { cyan }) => diff --git a/packages/rspack/src/stats/statsFactoryUtils.ts b/packages/rspack/src/stats/statsFactoryUtils.ts index e36a4a84cce..b03fb90a7d6 100644 --- a/packages/rspack/src/stats/statsFactoryUtils.ts +++ b/packages/rspack/src/stats/statsFactoryUtils.ts @@ -7,7 +7,7 @@ import { import type { Compilation } from "../Compilation"; import type { StatsOptions } from "../config"; -import type { StatsFactory } from "./StatsFactory"; +import type { StatsFactory, StatsFactoryContext } from "./StatsFactory"; export type KnownStatsChunkGroup = binding.JsStatsChunkGroup; @@ -36,6 +36,9 @@ export type KnownStatsProfile = { export type StatsModule = KnownStatsModule & Record; +export type StatsModuleIssuer = binding.JsStatsModuleIssuer & + Record; + type StatsError = binding.JsStatsError & Record; type StatsWarnings = binding.JsStatsWarning & Record; @@ -111,7 +114,7 @@ type ExtractorsByOption = { [x: string]: ( object: O, data: T, - context: any, + context: StatsFactoryContext, options: any, factory: StatsFactory ) => void; @@ -124,7 +127,7 @@ type PreprocessedAsset = StatsAsset & { export type SimpleExtractors = { compilation: ExtractorsByOption; - // asset$visible: ExtractorsByOption; + asset$visible: ExtractorsByOption; asset: ExtractorsByOption; // chunkGroup: ExtractorsByOption< // { @@ -134,10 +137,16 @@ export type SimpleExtractors = { // StatsChunkGroup // >; module: ExtractorsByOption; - // module$visible: ExtractorsByOption; - // moduleIssuer: ExtractorsByOption; + module$visible: ExtractorsByOption; + moduleIssuer: ExtractorsByOption< + binding.JsStatsModuleIssuer, + StatsModuleIssuer + >; profile: ExtractorsByOption; - // moduleReason: ExtractorsByOption; + moduleReason: ExtractorsByOption< + binding.JsStatsModuleReason, + StatsModuleReason + >; chunk: ExtractorsByOption; // chunkOrigin: ExtractorsByOption; // error: ExtractorsByOption; diff --git a/packages/rspack/src/util/index.ts b/packages/rspack/src/util/index.ts index 520c0b046d2..346ec580e18 100644 --- a/packages/rspack/src/util/index.ts +++ b/packages/rspack/src/util/index.ts @@ -1,7 +1,6 @@ import type { JsAssetInfo, JsStatsError } from "@rspack/binding"; import { AssetInfo } from "../Compilation"; import terminalLink from "terminal-link"; -import * as util from "util"; export function mapValues( record: Record, diff --git a/packages/rspack/src/util/validate.ts b/packages/rspack/src/util/validate.ts new file mode 100644 index 00000000000..e900d28cc3f --- /dev/null +++ b/packages/rspack/src/util/validate.ts @@ -0,0 +1,29 @@ +import { z } from "zod"; +import { fromZodError } from "zod-validation-error"; + +export function validate(opts: any, schema: T) { + const res = schema.safeParse(opts); + if (!res.success) { + const strategy = process.env.RSPACK_CONFIG_VALIDATE ?? "strict"; + if (strategy === "loose-silent") return; + const issueSeparator = "$issue$"; + const prefixSeparator = "$prefix$"; + const validationErr = fromZodError(res.error, { + prefix: "Configuration error", + prefixSeparator, + issueSeparator + }); + // The output validationErr.message looks like + // `Configuration error$prefix$xxxx error$issue$yyy error$issue$zzz error` + const [prefix, reason] = validationErr.message.split(prefixSeparator); + const reasonItem = reason.split(issueSeparator); + const friendlyErr = new Error( + `${prefix}:\n${reasonItem.map(item => `- ${item}`).join("\n")}` + ); + if (strategy === "loose") { + console.error(friendlyErr.message); + } else { + throw friendlyErr; + } + } +} diff --git a/packages/rspack/tests/ConfigCase.template.ts b/packages/rspack/tests/ConfigCase.template.ts index 7eea966ae80..4cc23fa0d54 100644 --- a/packages/rspack/tests/ConfigCase.template.ts +++ b/packages/rspack/tests/ConfigCase.template.ts @@ -209,10 +209,10 @@ export const describeCases = config => { return; }; it(`${testName} should compile`, _done => { - console.info("running:", testName); - console.time(testName); + // console.info("running:", testName); + // console.time(testName); const done = (...args: any[]) => { - console.timeEnd(testName); + // console.timeEnd(testName); return _done(...args); }; rimraf.sync(outputDirectory); diff --git a/packages/rspack/tests/Stats.test.ts b/packages/rspack/tests/Stats.test.ts index 167f1f0794f..dff1a5fd2f5 100644 --- a/packages/rspack/tests/Stats.test.ts +++ b/packages/rspack/tests/Stats.test.ts @@ -1,5 +1,6 @@ import * as util from "util"; -import { rspack, RspackOptions } from "../src"; +import path from "path"; +import { rspack, RspackOptions, Stats } from "../src"; import serializer from "jest-serializer-path"; expect.addSnapshotSerializer(serializer); @@ -23,139 +24,7 @@ describe("Stats", () => { version: false }; expect(typeof stats?.hash).toBe("string"); - expect(stats?.toJson(statsOptions)).toMatchInlineSnapshot(` - { - "assets": [ - { - "chunkNames": [ - "main", - ], - "chunks": [ - "main", - ], - "emitted": true, - "info": { - "development": false, - "hotModuleReplacement": false, - }, - "name": "main.js", - "size": 215, - "type": "asset", - }, - ], - "assetsByChunkName": { - "main": [ - "main.js", - ], - }, - "chunks": [ - { - "auxiliaryFiles": [], - "children": [], - "entry": true, - "files": [ - "main.js", - ], - "id": "main", - "initial": true, - "modules": [ - { - "assets": [], - "chunks": [ - "main", - ], - "id": "876", - "identifier": "/tests/fixtures/a.js", - "issuerPath": [], - "moduleType": "javascript/auto", - "name": "./fixtures/a.js", - "reasons": [ - { - "type": "entry", - "userRequest": "./fixtures/a", - }, - ], - "size": 55, - "source": "module.exports = function a() { - return "This is a"; - };", - "type": "module", - }, - ], - "names": [ - "main", - ], - "parents": [], - "siblings": [], - "size": 55, - "type": "chunk", - }, - ], - "entrypoints": { - "main": { - "assets": [ - { - "name": "main.js", - "size": 215, - }, - ], - "assetsSize": 215, - "chunks": [ - "main", - ], - "name": "main", - }, - }, - "errors": [], - "errorsCount": 0, - "filteredModules": undefined, - "hash": "b32fac08a5e8721cacff", - "logging": {}, - "modules": [ - { - "assets": [], - "chunks": [ - "main", - ], - "id": "876", - "identifier": "/tests/fixtures/a.js", - "issuerPath": [], - "moduleType": "javascript/auto", - "name": "./fixtures/a.js", - "reasons": [ - { - "type": "entry", - "userRequest": "./fixtures/a", - }, - ], - "size": 55, - "source": "module.exports = function a() { - return "This is a"; - };", - "type": "module", - }, - ], - "namedChunkGroups": { - "main": { - "assets": [ - { - "name": "main.js", - "size": 215, - }, - ], - "assetsSize": 215, - "chunks": [ - "main", - ], - "name": "main", - }, - }, - "outputPath": "/dist", - "publicPath": "auto", - "warnings": [], - "warningsCount": 0, - } - `); + expect(stats?.toJson(statsOptions)).toMatchSnapshot(); expect(stats?.toString(statsOptions)).toMatchInlineSnapshot(` "PublicPath: auto asset main.js 215 bytes {main} [emitted] (name: main) @@ -165,7 +34,7 @@ describe("Stats", () => { entry ./fixtures/a ./fixtures/a.js [876] {main} entry ./fixtures/a - rspack compiled successfully (b32fac08a5e8721cacff)" + rspack compiled successfully (31124a919605602fc9ae)" `); }); @@ -190,12 +59,12 @@ describe("Stats", () => { stats?.toString({ timings: false, version: false }).replace(/\\/g, "/") ).toMatchInlineSnapshot(` "PublicPath: auto - asset main.js 419 bytes {main} [emitted] (name: main) + asset main.js 419 bytes [emitted] (name: main) Entrypoint main 419 bytes = main.js - ./fixtures/a.js [876] {main} - ./fixtures/b.js [211] {main} - ./fixtures/c.js [537] {main} - ./fixtures/abc.js [222] {main} + ./fixtures/a.js + ./fixtures/b.js + ./fixtures/c.js + ./fixtures/abc.js error[javascript]: JavaScript parsing error ┌─ tests/fixtures/b.js:6:1 @@ -210,10 +79,25 @@ describe("Stats", () => { - rspack compiled with 1 error (27ec1c09308b67dcfd6f)" + rspack compiled with 1 error (18c9b1da202481673984)" `); }); + it("should output stats with query", async () => { + const stats = await compile({ + context: __dirname, + entry: "./fixtures/abc-query" + }); + + const statsOptions = { + all: true, + timings: false, + builtAt: false, + version: false + }; + expect(stats?.toJson(statsOptions)).toMatchSnapshot(); + }); + it("should output the specified number of modules when set stats.modulesSpace", async () => { const stats = await compile({ context: __dirname, @@ -253,6 +137,11 @@ describe("Stats", () => { .replace(/\d+ ms/g, "X ms") ).toMatchInlineSnapshot(` "LOG from rspack.Compilation + make hook: X ms + module add task: X ms + module process dependencies task: X ms + module factorize task: X ms + module build task: X ms finish modules: X ms optimize dependencies: X ms create chunks: X ms @@ -272,11 +161,6 @@ describe("Stats", () => { process assets: X ms LOG from rspack.Compiler - make hook: X ms - module add task: X ms - module process dependencies task: X ms - module factorize task: X ms - module build task: X ms make: X ms finish make hook: X ms finish compilation: X ms @@ -321,17 +205,148 @@ describe("Stats", () => { .replace(/\\/g, "/") .replace(/\d+ ms/g, "X ms") ).toMatchInlineSnapshot(` - "./fixtures/a.js [876] {main} - [222] -> + "./fixtures/a.js X ms (resolving: X ms, integration: X ms, building: X ms) - ./fixtures/b.js [211] {main} - [222] -> + ./fixtures/b.js X ms (resolving: X ms, integration: X ms, building: X ms) - ./fixtures/c.js [537] {main} - [222] -> + ./fixtures/c.js X ms (resolving: X ms, integration: X ms, building: X ms) - ./fixtures/abc.js [222] {main} + ./fixtures/abc.js X ms (resolving: X ms, integration: X ms, building: X ms)" `); }); + + it("should have cache hits log when logging verbose and cache is enabled", async () => { + const compiler = rspack({ + context: __dirname, + entry: "./fixtures/abc", + cache: true, + experiments: { + incrementalRebuild: false + } + }); + await new Promise((resolve, reject) => { + compiler.build(err => { + if (err) { + return reject(err); + } + resolve(); + }); + }); + const stats = await new Promise((resolve, reject) => { + compiler.rebuild( + new Set([path.join(__dirname, "./fixtures/a")]), + new Set(), + err => { + if (err) { + return reject(err); + } + const stats = new Stats(compiler.compilation).toString({ + all: false, + logging: "verbose" + }); + resolve(stats); + } + ); + }); + expect(stats).toContain("module build cache: 100.0% (4/4)"); + expect(stats).toContain("module factorize cache: 100.0% (5/5)"); + expect(stats).toContain("module code generation cache: 100.0% (4/4)"); + }); + + it("should not have any cache hits log when cache is disabled", async () => { + const compiler = rspack({ + context: __dirname, + entry: "./fixtures/abc", + cache: false, + experiments: { + incrementalRebuild: false + } + }); + await new Promise((resolve, reject) => { + compiler.build(err => { + if (err) { + return reject(err); + } + resolve(); + }); + }); + const stats = await new Promise((resolve, reject) => { + compiler.rebuild( + new Set([path.join(__dirname, "./fixtures/a")]), + new Set(), + err => { + if (err) { + return reject(err); + } + const stats = new Stats(compiler.compilation).toString({ + all: false, + logging: "verbose" + }); + resolve(stats); + } + ); + }); + expect(stats).not.toContain("module build cache"); + expect(stats).not.toContain("module factorize cache"); + expect(stats).not.toContain("module code generation cache"); + }); + + it("should have any cache hits log of modules in incremental rebuild mode", async () => { + const compiler = rspack({ + context: __dirname, + entry: "./fixtures/abc", + cache: true, + experiments: { + incrementalRebuild: true + } + }); + await new Promise((resolve, reject) => { + compiler.build(err => { + if (err) { + return reject(err); + } + resolve(); + }); + }); + const stats = await new Promise((resolve, reject) => { + compiler.rebuild( + new Set([path.join(__dirname, "./fixtures/a")]), + new Set(), + err => { + if (err) { + return reject(err); + } + const stats = new Stats(compiler.compilation).toString({ + all: false, + logging: "verbose" + }); + resolve(stats); + } + ); + }); + expect(stats).toContain("module build cache: 100.0% (1/1)"); + expect(stats).toContain("module factorize cache: 100.0% (1/1)"); + expect(stats).toContain("module code generation cache: 100.0% (4/4)"); + }); + + it("should have ids when ids is true", async () => { + const stats = await compile({ + context: __dirname, + entry: "./fixtures/a" + }); + const options = { + all: false, + assets: true, + modules: true, + chunks: true, + ids: true + }; + expect(stats?.toJson(options)).toMatchSnapshot(); + expect(stats?.toString(options).replace(/\\/g, "/")).toMatchInlineSnapshot(` + "asset main.js 215 bytes {main} [emitted] (name: main) + chunk {main} main.js (main) [entry] + ./fixtures/a.js [876] {main}" + `); + }); }); diff --git a/packages/rspack/tests/__snapshots__/Defaults.unittest.ts.snap b/packages/rspack/tests/__snapshots__/Defaults.unittest.ts.snap index 9eadbe140b1..728f86b31f8 100644 --- a/packages/rspack/tests/__snapshots__/Defaults.unittest.ts.snap +++ b/packages/rspack/tests/__snapshots__/Defaults.unittest.ts.snap @@ -24,7 +24,9 @@ exports[`snapshots should have the correct base config 1`] = ` }, "lazyCompilation": false, "newSplitChunks": true, - "rspackFuture": {}, + "rspackFuture": { + "newResolver": false, + }, }, "externals": undefined, "externalsPresets": { diff --git a/packages/rspack/tests/__snapshots__/Stats.test.ts.snap b/packages/rspack/tests/__snapshots__/Stats.test.ts.snap new file mode 100644 index 00000000000..1f265223833 --- /dev/null +++ b/packages/rspack/tests/__snapshots__/Stats.test.ts.snap @@ -0,0 +1,584 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Stats should have ids when ids is true 1`] = ` +{ + "assets": [ + { + "chunkNames": [ + "main", + ], + "chunks": [ + "main", + ], + "emitted": true, + "info": { + "development": false, + "hotModuleReplacement": false, + }, + "name": "main.js", + "size": 215, + "type": "asset", + }, + ], + "assetsByChunkName": { + "main": [ + "main.js", + ], + }, + "chunks": [ + { + "auxiliaryFiles": [], + "entry": true, + "files": [ + "main.js", + ], + "id": "main", + "initial": true, + "names": [ + "main", + ], + "size": 55, + "type": "chunk", + }, + ], + "filteredModules": undefined, + "modules": [ + { + "chunks": [ + "main", + ], + "id": "876", + "identifier": "/tests/fixtures/a.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, + "issuerPath": [], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", + "size": 55, + "type": "module", + }, + ], +} +`; + +exports[`Stats should have stats 1`] = ` +{ + "assets": [ + { + "chunkNames": [ + "main", + ], + "chunks": [ + "main", + ], + "emitted": true, + "info": { + "development": false, + "hotModuleReplacement": false, + }, + "name": "main.js", + "size": 215, + "type": "asset", + }, + ], + "assetsByChunkName": { + "main": [ + "main.js", + ], + }, + "chunks": [ + { + "auxiliaryFiles": [], + "children": [], + "entry": true, + "files": [ + "main.js", + ], + "id": "main", + "initial": true, + "modules": [ + { + "assets": [], + "chunks": [ + "main", + ], + "id": "876", + "identifier": "/tests/fixtures/a.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, + "issuerPath": [], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, + "type": "entry", + "userRequest": "./fixtures/a", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; +};", + "type": "module", + }, + ], + "names": [ + "main", + ], + "parents": [], + "siblings": [], + "size": 55, + "type": "chunk", + }, + ], + "entrypoints": { + "main": { + "assets": [ + { + "name": "main.js", + "size": 215, + }, + ], + "assetsSize": 215, + "chunks": [ + "main", + ], + "name": "main", + }, + }, + "errors": [], + "errorsCount": 0, + "filteredModules": undefined, + "hash": "31124a919605602fc9ae", + "logging": {}, + "modules": [ + { + "assets": [], + "chunks": [ + "main", + ], + "id": "876", + "identifier": "/tests/fixtures/a.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, + "issuerPath": [], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, + "type": "entry", + "userRequest": "./fixtures/a", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; +};", + "type": "module", + }, + ], + "namedChunkGroups": { + "main": { + "assets": [ + { + "name": "main.js", + "size": 215, + }, + ], + "assetsSize": 215, + "chunks": [ + "main", + ], + "name": "main", + }, + }, + "outputPath": "/dist", + "publicPath": "auto", + "warnings": [], + "warningsCount": 0, +} +`; + +exports[`Stats should output stats with query 1`] = ` +{ + "assets": [ + { + "chunkNames": [ + "main", + ], + "chunks": [ + "main", + ], + "emitted": true, + "info": { + "development": false, + "hotModuleReplacement": false, + }, + "name": "main.js", + "size": 394, + "type": "asset", + }, + ], + "assetsByChunkName": { + "main": [ + "main.js", + ], + }, + "chunks": [ + { + "auxiliaryFiles": [], + "children": [], + "entry": true, + "files": [ + "main.js", + ], + "id": "main", + "initial": true, + "modules": [ + { + "assets": [], + "chunks": [ + "main", + ], + "id": "876", + "identifier": "/tests/fixtures/a.js", + "issuer": "/tests/fixtures/c.js?c=3", + "issuerId": "970", + "issuerName": "./fixtures/c.js?c=3", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + { + "id": "970", + "identifier": "/tests/fixtures/c.js?c=3", + "name": "./fixtures/c.js?c=3", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": "970", + "moduleIdentifier": "/tests/fixtures/c.js?c=3", + "moduleName": "./fixtures/c.js?c=3", + "type": "cjs require", + "userRequest": "./a", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; +};", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "304", + "identifier": "/tests/fixtures/a.js?a=1", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js?a=1", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./a?a=1", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; +};", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "970", + "identifier": "/tests/fixtures/c.js?c=3", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/c.js?c=3", + "nameForCondition": "/tests/fixtures/c.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./c?c=3", + }, + ], + "size": 72, + "source": "module.exports = function b() { + require("./a"); + return "This is c"; +};", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, + "issuerPath": [], + "moduleType": "javascript/auto", + "name": "./fixtures/abc-query.js", + "nameForCondition": "/tests/fixtures/abc-query.js", + "reasons": [ + { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, + "type": "entry", + "userRequest": "./fixtures/abc-query", + }, + ], + "size": 99, + "source": "exports.a = require("./a?a=1"); +// exports.b = require("./b?b=2"); +exports.c = require("./c?c=3"); +", + "type": "module", + }, + ], + "names": [ + "main", + ], + "parents": [], + "siblings": [], + "size": 281, + "type": "chunk", + }, + ], + "entrypoints": { + "main": { + "assets": [ + { + "name": "main.js", + "size": 394, + }, + ], + "assetsSize": 394, + "chunks": [ + "main", + ], + "name": "main", + }, + }, + "errors": [], + "errorsCount": 0, + "filteredModules": undefined, + "hash": "089a36c7767bb55f0574", + "logging": {}, + "modules": [ + { + "assets": [], + "chunks": [ + "main", + ], + "id": "876", + "identifier": "/tests/fixtures/a.js", + "issuer": "/tests/fixtures/c.js?c=3", + "issuerId": "970", + "issuerName": "./fixtures/c.js?c=3", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + { + "id": "970", + "identifier": "/tests/fixtures/c.js?c=3", + "name": "./fixtures/c.js?c=3", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": "970", + "moduleIdentifier": "/tests/fixtures/c.js?c=3", + "moduleName": "./fixtures/c.js?c=3", + "type": "cjs require", + "userRequest": "./a", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; +};", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "304", + "identifier": "/tests/fixtures/a.js?a=1", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/a.js?a=1", + "nameForCondition": "/tests/fixtures/a.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./a?a=1", + }, + ], + "size": 55, + "source": "module.exports = function a() { + return "This is a"; +};", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "970", + "identifier": "/tests/fixtures/c.js?c=3", + "issuer": "/tests/fixtures/abc-query.js", + "issuerId": "661", + "issuerName": "./fixtures/abc-query.js", + "issuerPath": [ + { + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "name": "./fixtures/abc-query.js", + }, + ], + "moduleType": "javascript/auto", + "name": "./fixtures/c.js?c=3", + "nameForCondition": "/tests/fixtures/c.js", + "reasons": [ + { + "moduleId": "661", + "moduleIdentifier": "/tests/fixtures/abc-query.js", + "moduleName": "./fixtures/abc-query.js", + "type": "cjs require", + "userRequest": "./c?c=3", + }, + ], + "size": 72, + "source": "module.exports = function b() { + require("./a"); + return "This is c"; +};", + "type": "module", + }, + { + "assets": [], + "chunks": [ + "main", + ], + "id": "661", + "identifier": "/tests/fixtures/abc-query.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, + "issuerPath": [], + "moduleType": "javascript/auto", + "name": "./fixtures/abc-query.js", + "nameForCondition": "/tests/fixtures/abc-query.js", + "reasons": [ + { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, + "type": "entry", + "userRequest": "./fixtures/abc-query", + }, + ], + "size": 99, + "source": "exports.a = require("./a?a=1"); +// exports.b = require("./b?b=2"); +exports.c = require("./c?c=3"); +", + "type": "module", + }, + ], + "namedChunkGroups": { + "main": { + "assets": [ + { + "name": "main.js", + "size": 394, + }, + ], + "assetsSize": 394, + "chunks": [ + "main", + ], + "name": "main", + }, + }, + "outputPath": "/dist", + "publicPath": "auto", + "warnings": [], + "warningsCount": 0, +} +`; diff --git a/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap b/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap index a7b4eb932b5..d121012c97a 100644 --- a/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap +++ b/packages/rspack/tests/__snapshots__/StatsTestCases.test.ts.snap @@ -74,6 +74,7 @@ exports[`StatsTestCases should print correct stats for auxiliary-files-test 1`] ], "moduleType": "asset/resource", "name": "./raw.png", + "nameForCondition": "/tests/statsCases/auxiliary-files-test/raw.png", "reasons": [ { "moduleId": "10", @@ -105,11 +106,18 @@ exports[`StatsTestCases should print correct stats for auxiliary-files-test 1`] ], "id": "10", "identifier": "/tests/statsCases/auxiliary-files-test/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/auxiliary-files-test/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -138,6 +146,7 @@ import rawModule from './raw.png'", ], "moduleType": "javascript/auto", "name": "./stringModule.js", + "nameForCondition": "/tests/statsCases/auxiliary-files-test/stringModule.js", "reasons": [ { "moduleId": "10", @@ -179,7 +188,7 @@ import rawModule from './raw.png'", "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "31df38a93780ad25981d", + "hash": "94f12aa69d3989830e25", "logging": {}, "modules": [ { @@ -201,6 +210,7 @@ import rawModule from './raw.png'", ], "moduleType": "asset/resource", "name": "./raw.png", + "nameForCondition": "/tests/statsCases/auxiliary-files-test/raw.png", "reasons": [ { "moduleId": "10", @@ -232,11 +242,18 @@ import rawModule from './raw.png'", ], "id": "10", "identifier": "/tests/statsCases/auxiliary-files-test/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/auxiliary-files-test/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -265,6 +282,7 @@ import rawModule from './raw.png'", ], "moduleType": "javascript/auto", "name": "./stringModule.js", + "nameForCondition": "/tests/statsCases/auxiliary-files-test/stringModule.js", "reasons": [ { "moduleId": "10", @@ -319,7 +337,7 @@ chunk {main} bundle.js (main) [entry] entry ./index ./stringModule.js [363] {main} esm import ./stringModule [10] -rspack compiled successfully (31df38a93780ad25981d)" +rspack compiled successfully (94f12aa69d3989830e25)" `; exports[`StatsTestCases should print correct stats for filename 1`] = ` @@ -391,6 +409,7 @@ exports[`StatsTestCases should print correct stats for filename 1`] = ` ], "moduleType": "javascript/auto", "name": "./dynamic.js", + "nameForCondition": "/tests/statsCases/filename/dynamic.js", "reasons": [ { "moduleId": "10", @@ -433,11 +452,18 @@ exports[`StatsTestCases should print correct stats for filename 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/filename/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/filename/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -474,7 +500,7 @@ exports[`StatsTestCases should print correct stats for filename 1`] = ` "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "7cdaa6ba61cec2cb7288", + "hash": "b1dbe683f73e6b1f7f2c", "logging": {}, "modules": [ { @@ -484,11 +510,18 @@ exports[`StatsTestCases should print correct stats for filename 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/filename/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/filename/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -516,6 +549,7 @@ exports[`StatsTestCases should print correct stats for filename 1`] = ` ], "moduleType": "javascript/auto", "name": "./dynamic.js", + "nameForCondition": "/tests/statsCases/filename/dynamic.js", "reasons": [ { "moduleId": "10", @@ -568,7 +602,7 @@ chunk {main} main.xxxx.js (main) >{dynamic_js}< [entry] entry ./index ./dynamic.js [426] {dynamic_js} dynamic import ./dynamic [10] -rspack compiled successfully (7cdaa6ba61cec2cb7288)" +rspack compiled successfully (b1dbe683f73e6b1f7f2c)" `; exports[`StatsTestCases should print correct stats for hot+production 1`] = ` @@ -614,11 +648,18 @@ exports[`StatsTestCases should print correct stats for hot+production 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/hot+production/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/hot+production/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index.js", }, @@ -656,7 +697,7 @@ exports[`StatsTestCases should print correct stats for hot+production 1`] = ` "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "d71f062b534cbc5af842", + "hash": "86548123ac24bd06a6bf", "logging": {}, "modules": [ { @@ -666,11 +707,18 @@ exports[`StatsTestCases should print correct stats for hot+production 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/hot+production/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/hot+production/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index.js", }, @@ -712,7 +760,7 @@ chunk {main} bundle.js (main) [entry] entry ./index.js ./index.js [10] {main} entry ./index.js -rspack compiled successfully (d71f062b534cbc5af842)" +rspack compiled successfully (86548123ac24bd06a6bf)" `; exports[`StatsTestCases should print correct stats for identifier-let-strict-mode 1`] = ` @@ -777,108 +825,85 @@ exports[`StatsTestCases should print correct stats for ignore-plugin 1`] = ` "filteredModules": undefined, "modules": [ { - "chunks": [ - "main", - ], - "id": "10", "identifier": "/tests/statsCases/ignore-plugin/index.js", + "issuer": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/ignore-plugin/index.js", "size": 103, "type": "module", }, { - "chunks": [ - "main", - ], - "id": "208", "identifier": "/tests/statsCases/ignore-plugin/locals/en.js", "issuer": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", - "issuerId": "247", "issuerName": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", "issuerPath": [ { - "id": "10", "identifier": "/tests/statsCases/ignore-plugin/index.js", "name": "./index.js", }, { - "id": "247", "identifier": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", "name": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", }, ], "moduleType": "javascript/auto", "name": "./locals/en.js", + "nameForCondition": "/tests/statsCases/ignore-plugin/locals/en.js", "size": 30, "type": "module", }, { - "chunks": [ - "main", - ], - "id": "136", "identifier": "missing|/tests/statsCases/ignore-plugin/locals/./zh.js", "issuer": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", - "issuerId": "247", "issuerName": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", "issuerPath": [ { - "id": "10", "identifier": "/tests/statsCases/ignore-plugin/index.js", "name": "./index.js", }, { - "id": "247", "identifier": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", "name": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", }, ], "moduleType": "javascript/auto", "name": "/tests/statsCases/ignore-plugin/locals/./zh.js (missing)", + "nameForCondition": undefined, "size": 160, "type": "module", }, { - "chunks": [ - "main", - ], - "id": "239", "identifier": "missing|/tests/statsCases/ignore-plugin/./globalIndex.js", "issuer": "/tests/statsCases/ignore-plugin/index.js", - "issuerId": "10", "issuerName": "./index.js", "issuerPath": [ { - "id": "10", "identifier": "/tests/statsCases/ignore-plugin/index.js", "name": "./index.js", }, ], "moduleType": "javascript/auto", "name": "/tests/statsCases/ignore-plugin/./globalIndex.js (missing)", + "nameForCondition": undefined, "size": 160, "type": "module", }, { - "chunks": [ - "main", - ], - "id": "247", "identifier": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", "issuer": "/tests/statsCases/ignore-plugin/index.js", - "issuerId": "10", "issuerName": "./index.js", "issuerPath": [ { - "id": "10", "identifier": "/tests/statsCases/ignore-plugin/index.js", "name": "./index.js", }, ], "moduleType": "javascript/auto", "name": "/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }", + "nameForCondition": undefined, "size": 160, "type": "module", }, @@ -887,13 +912,25 @@ exports[`StatsTestCases should print correct stats for ignore-plugin 1`] = ` `; exports[`StatsTestCases should print correct stats for ignore-plugin 2`] = ` -"./index.js [10] {main} -./locals/en.js [208] {main} -/tests/statsCases/ignore-plugin/locals/./zh.js (missing) [136] {main} -/tests/statsCases/ignore-plugin/./globalIndex.js (missing) [239] {main} -/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset } [247] {main}" +"./index.js +./locals/en.js +/tests/statsCases/ignore-plugin/locals/./zh.js (missing) +/tests/statsCases/ignore-plugin/./globalIndex.js (missing) +/tests/statsCases/ignore-plugin/locals|None|None|ContextOptions { mode: Sync, recursive: true, reg_exp: RspackRegex { algo: Regress(Regex { cr: CompiledRegex { insns: [StartOfLine, ByteSeq2([46, 47]), Loop1CharBody { min_iters: 0, max_iters: 18446744073709551615, greedy: true }, MatchAnyExceptLineTerminator, EndOfLine, Goal], brackets: [], start_pred: Arbitrary, loops: 0, groups: 0, named_group_indices: {}, flags: Flags { icase: false, multiline: false, dot_all: false, no_opt: false, unicode: false } } }) }, reg_str: "^//.///.*$", include: None, exclude: None, category: CommonJS, request: "./locals", namespace_object: Unset }" `; +exports[`StatsTestCases should print correct stats for ignore-warning 1`] = ` +{ + "errors": [], + "errorsCount": 0, + "logging": {}, + "warnings": [], + "warningsCount": 0, +} +`; + +exports[`StatsTestCases should print correct stats for ignore-warning 2`] = `"rspack compiled successfully"`; + exports[`StatsTestCases should print correct stats for issue-3558 1`] = ` { "errors": [], @@ -1130,7 +1167,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e1.js", ], - "id": "e1", "initial": true, "names": [ "e1", @@ -1144,7 +1180,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e1~runtime.js", ], - "id": "e1~runtime", "initial": true, "names": [ "e1~runtime", @@ -1158,7 +1193,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e2.js", ], - "id": "e2", "initial": true, "names": [ "e2", @@ -1172,7 +1206,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e2~runtime.js", ], - "id": "e2~runtime", "initial": true, "names": [ "e2~runtime", @@ -1264,10 +1297,10 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun exports[`StatsTestCases should print correct stats for optimization-runtime-chunk 2`] = ` "Entrypoint e1 2.8 KiB = e1~runtime.js 2.4 KiB e1.js 412 bytes Entrypoint e2 2.8 KiB = e2~runtime.js 2.4 KiB e2.js 412 bytes -chunk {e1} e1.js (e1) [entry] -chunk {e1~runtime} e1~runtime.js (e1~runtime) [initial] -chunk {e2} e2.js (e2) [entry] -chunk {e2~runtime} e2~runtime.js (e2~runtime) [initial]" +chunk e1.js (e1) [entry] +chunk e1~runtime.js (e1~runtime) [initial] +chunk e2.js (e2) [entry] +chunk e2~runtime.js (e2~runtime) [initial]" `; exports[`StatsTestCases should print correct stats for optimization-runtime-chunk-multiple 1`] = ` @@ -1279,7 +1312,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e1.js", ], - "id": "e1", "initial": true, "names": [ "e1", @@ -1293,7 +1325,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e2.js", ], - "id": "e2", "initial": true, "names": [ "e2", @@ -1307,7 +1338,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "runtime~e1.js", ], - "id": "runtime~e1", "initial": true, "names": [ "runtime~e1", @@ -1321,7 +1351,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "runtime~e2.js", ], - "id": "runtime~e2", "initial": true, "names": [ "runtime~e2", @@ -1413,10 +1442,10 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun exports[`StatsTestCases should print correct stats for optimization-runtime-chunk-multiple 2`] = ` "Entrypoint e1 2.8 KiB = runtime~e1.js 2.4 KiB e1.js 406 bytes Entrypoint e2 2.8 KiB = runtime~e2.js 2.4 KiB e2.js 406 bytes -chunk {e1} e1.js (e1) [entry] -chunk {e2} e2.js (e2) [entry] -chunk {runtime~e1} runtime~e1.js (runtime~e1) [initial] -chunk {runtime~e2} runtime~e2.js (runtime~e2) [initial]" +chunk e1.js (e1) [entry] +chunk e2.js (e2) [entry] +chunk runtime~e1.js (runtime~e1) [initial] +chunk runtime~e2.js (runtime~e2) [initial]" `; exports[`StatsTestCases should print correct stats for optimization-runtime-chunk-single 1`] = ` @@ -1428,7 +1457,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e1.js", ], - "id": "e1", "initial": true, "names": [ "e1", @@ -1442,7 +1470,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e2.js", ], - "id": "e2", "initial": true, "names": [ "e2", @@ -1456,7 +1483,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "runtime.js", ], - "id": "runtime", "initial": true, "names": [ "runtime", @@ -1548,9 +1574,9 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun exports[`StatsTestCases should print correct stats for optimization-runtime-chunk-single 2`] = ` "Entrypoint e1 2.8 KiB = runtime.js 2.4 KiB e1.js 409 bytes Entrypoint e2 2.8 KiB = runtime.js 2.4 KiB e2.js 409 bytes -chunk {e1} e1.js (e1) [entry] -chunk {e2} e2.js (e2) [entry] -chunk {runtime} runtime.js (runtime) [initial]" +chunk e1.js (e1) [entry] +chunk e2.js (e2) [entry] +chunk runtime.js (runtime) [initial]" `; exports[`StatsTestCases should print correct stats for optimization-runtime-chunk-true 1`] = ` @@ -1562,7 +1588,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e1.js", ], - "id": "e1", "initial": true, "names": [ "e1", @@ -1576,7 +1601,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "e2.js", ], - "id": "e2", "initial": true, "names": [ "e2", @@ -1590,7 +1614,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "runtime~e1.js", ], - "id": "runtime~e1", "initial": true, "names": [ "runtime~e1", @@ -1604,7 +1627,6 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun "files": [ "runtime~e2.js", ], - "id": "runtime~e2", "initial": true, "names": [ "runtime~e2", @@ -1696,10 +1718,10 @@ exports[`StatsTestCases should print correct stats for optimization-runtime-chun exports[`StatsTestCases should print correct stats for optimization-runtime-chunk-true 2`] = ` "Entrypoint e1 2.8 KiB = runtime~e1.js 2.4 KiB e1.js 406 bytes Entrypoint e2 2.8 KiB = runtime~e2.js 2.4 KiB e2.js 406 bytes -chunk {e1} e1.js (e1) [entry] -chunk {e2} e2.js (e2) [entry] -chunk {runtime~e1} runtime~e1.js (runtime~e1) [initial] -chunk {runtime~e2} runtime~e2.js (runtime~e2) [initial]" +chunk e1.js (e1) [entry] +chunk e2.js (e2) [entry] +chunk runtime~e1.js (runtime~e1) [initial] +chunk runtime~e2.js (runtime~e2) [initial]" `; exports[`StatsTestCases should print correct stats for parse-error 1`] = ` @@ -1749,6 +1771,50 @@ exports[`StatsTestCases should print correct stats for parse-error 2`] = ` +rspack compiled with 1 error" +`; + +exports[`StatsTestCases should print correct stats for parse-error-builtin-swc-loader 1`] = ` +{ + "errors": [ + { + "formatted": "error[internal]: + x Expected '{', got 'error' + ,-[/tests/statsCases/parse-error-builtin-swc-loader/index.ts:1:1] + 1 | export error; + : ^^^^^ + \`---- + + +", + "message": " + x Expected '{', got 'error' + ,-[/tests/statsCases/parse-error-builtin-swc-loader/index.ts:1:1] + 1 | export error; + : ^^^^^ + \`---- +", + "title": "", + }, + ], + "errorsCount": 1, + "logging": {}, + "warnings": [], + "warningsCount": 0, +} +`; + +exports[`StatsTestCases should print correct stats for parse-error-builtin-swc-loader 2`] = ` +"error[internal]: + x Expected '{', got 'error' + ,-[/tests/statsCases/parse-error-builtin-swc-loader/index.ts:1:1] + 1 | export error; + : ^^^^^ + \`---- + + + + rspack compiled with 1 error" `; @@ -1757,26 +1823,20 @@ exports[`StatsTestCases should print correct stats for reasons 1`] = ` "filteredModules": undefined, "modules": [ { - "chunks": [ - "main", - ], - "id": "847", "identifier": "/tests/statsCases/reasons/a.js", "issuer": "/tests/statsCases/reasons/index.js", - "issuerId": "10", "issuerName": "./index.js", "issuerPath": [ { - "id": "10", "identifier": "/tests/statsCases/reasons/index.js", "name": "./index.js", }, ], "moduleType": "javascript/auto", "name": "./a.js", + "nameForCondition": "/tests/statsCases/reasons/a.js", "reasons": [ { - "moduleId": "10", "moduleIdentifier": "/tests/statsCases/reasons/index.js", "moduleName": "./index.js", "type": "cjs require", @@ -1787,16 +1847,17 @@ exports[`StatsTestCases should print correct stats for reasons 1`] = ` "type": "module", }, { - "chunks": [ - "main", - ], - "id": "10", "identifier": "/tests/statsCases/reasons/index.js", + "issuer": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/reasons/index.js", "reasons": [ { + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -1809,9 +1870,9 @@ exports[`StatsTestCases should print correct stats for reasons 1`] = ` `; exports[`StatsTestCases should print correct stats for reasons 2`] = ` -"./a.js [847] {main} - cjs require ./a [10] -./index.js [10] {main} +"./a.js + cjs require ./a +./index.js entry ./index" `; @@ -1858,11 +1919,18 @@ exports[`StatsTestCases should print correct stats for resolve-overflow 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/resolve-overflow/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/resolve-overflow/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -1892,6 +1960,7 @@ console.log(a); ], "moduleType": "javascript/auto", "name": "/tests/statsCases/resolve-overflow/cycle-alias/a (missing)", + "nameForCondition": undefined, "reasons": [ { "moduleId": "10", @@ -1909,6 +1978,7 @@ console.log(a); }, ], "size": 160, + "source": undefined, "type": "module", }, ], @@ -1953,7 +2023,7 @@ console.log(a); ], "errorsCount": 1, "filteredModules": undefined, - "hash": "7d2d2ee1a9384ad715fa", + "hash": "6ca407f06f94c982125d", "logging": {}, "modules": [ { @@ -1963,11 +2033,18 @@ console.log(a); ], "id": "10", "identifier": "/tests/statsCases/resolve-overflow/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/resolve-overflow/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -1997,6 +2074,7 @@ console.log(a); ], "moduleType": "javascript/auto", "name": "/tests/statsCases/resolve-overflow/cycle-alias/a (missing)", + "nameForCondition": undefined, "reasons": [ { "moduleId": "10", @@ -2014,6 +2092,7 @@ console.log(a); }, ], "size": 160, + "source": undefined, "type": "module", }, ], @@ -2065,7 +2144,7 @@ error[internal]: Resolve error -rspack compiled with 1 error (7d2d2ee1a9384ad715fa)" +rspack compiled with 1 error (6ca407f06f94c982125d)" `; exports[`StatsTestCases should print correct stats for resolve-unexpected-exports-in-pkg 1`] = ` @@ -2111,11 +2190,18 @@ exports[`StatsTestCases should print correct stats for resolve-unexpected-export ], "id": "10", "identifier": "/tests/statsCases/resolve-unexpected-exports-in-pkg/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/resolve-unexpected-exports-in-pkg/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2145,6 +2231,7 @@ console.log(a); ], "moduleType": "javascript/auto", "name": "/tests/statsCases/resolve-unexpected-exports-in-pkg/pkg-a (missing)", + "nameForCondition": undefined, "reasons": [ { "moduleId": "10", @@ -2162,6 +2249,7 @@ console.log(a); }, ], "size": 160, + "source": undefined, "type": "module", }, ], @@ -2200,7 +2288,7 @@ console.log(a); ], "errorsCount": 1, "filteredModules": undefined, - "hash": "d4ff2f3bc5aba8d564b3", + "hash": "a9093cd411993b4b9c01", "logging": {}, "modules": [ { @@ -2210,11 +2298,18 @@ console.log(a); ], "id": "10", "identifier": "/tests/statsCases/resolve-unexpected-exports-in-pkg/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/resolve-unexpected-exports-in-pkg/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2244,6 +2339,7 @@ console.log(a); ], "moduleType": "javascript/auto", "name": "/tests/statsCases/resolve-unexpected-exports-in-pkg/pkg-a (missing)", + "nameForCondition": undefined, "reasons": [ { "moduleId": "10", @@ -2261,6 +2357,7 @@ console.log(a); }, ], "size": 160, + "source": undefined, "type": "module", }, ], @@ -2306,7 +2403,7 @@ error[internal]: Export should be relative path and start with "./", but got ../ -rspack compiled with 1 error (d4ff2f3bc5aba8d564b3)" +rspack compiled with 1 error (a9093cd411993b4b9c01)" `; exports[`StatsTestCases should print correct stats for simple 1`] = ` @@ -2352,11 +2449,18 @@ exports[`StatsTestCases should print correct stats for simple 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/simple/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/simple/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2394,7 +2498,7 @@ exports[`StatsTestCases should print correct stats for simple 1`] = ` "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "44e8a78970b06279cd26", + "hash": "7a27fed57156f35cc7b2", "logging": {}, "modules": [ { @@ -2404,11 +2508,18 @@ exports[`StatsTestCases should print correct stats for simple 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/simple/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/simple/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2450,7 +2561,7 @@ chunk {main} bundle.js (main) [entry] entry ./index ./index.js [10] {main} entry ./index -rspack compiled successfully (44e8a78970b06279cd26)" +rspack compiled successfully (7a27fed57156f35cc7b2)" `; exports[`StatsTestCases should print correct stats for simple-module-source 1`] = ` @@ -2508,6 +2619,7 @@ exports[`StatsTestCases should print correct stats for simple-module-source 1`] ], "moduleType": "javascript/auto", "name": "./raw.png", + "nameForCondition": "/tests/statsCases/simple-module-source/raw.png", "reasons": [ { "moduleId": "10", @@ -2528,11 +2640,18 @@ exports[`StatsTestCases should print correct stats for simple-module-source 1`] ], "id": "10", "identifier": "/tests/statsCases/simple-module-source/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/simple-module-source/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2561,6 +2680,7 @@ import rawModule from './raw.png'", ], "moduleType": "javascript/auto", "name": "./stringModule.js", + "nameForCondition": "/tests/statsCases/simple-module-source/stringModule.js", "reasons": [ { "moduleId": "10", @@ -2602,7 +2722,7 @@ import rawModule from './raw.png'", "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "a563cef06d977112d3e1", + "hash": "ef78d0d53e6750a65525", "logging": {}, "modules": [ { @@ -2624,6 +2744,7 @@ import rawModule from './raw.png'", ], "moduleType": "javascript/auto", "name": "./raw.png", + "nameForCondition": "/tests/statsCases/simple-module-source/raw.png", "reasons": [ { "moduleId": "10", @@ -2644,11 +2765,18 @@ import rawModule from './raw.png'", ], "id": "10", "identifier": "/tests/statsCases/simple-module-source/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/simple-module-source/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2677,6 +2805,7 @@ import rawModule from './raw.png'", ], "moduleType": "javascript/auto", "name": "./stringModule.js", + "nameForCondition": "/tests/statsCases/simple-module-source/stringModule.js", "reasons": [ { "moduleId": "10", @@ -2715,12 +2844,12 @@ import rawModule from './raw.png'", exports[`StatsTestCases should print correct stats for simple-module-source 2`] = ` "PublicPath: (none) -asset bundle.js 631 bytes {main} [emitted] (name: main) +asset bundle.js 631 bytes [emitted] (name: main) Entrypoint main 631 bytes = bundle.js -./raw.png [98] {main} -./index.js [10] {main} -./stringModule.js [363] {main} -rspack compiled successfully (a563cef06d977112d3e1)" +./raw.png +./index.js +./stringModule.js +rspack compiled successfully (ef78d0d53e6750a65525)" `; exports[`StatsTestCases should print correct stats for stats-hooks 1`] = ` @@ -2767,11 +2896,18 @@ exports[`StatsTestCases should print correct stats for stats-hooks 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/stats-hooks/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/stats-hooks/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2807,7 +2943,7 @@ exports[`StatsTestCases should print correct stats for stats-hooks 1`] = ` "errors": [], "errorsCount": 0, "filteredModules": undefined, - "hash": "44e8a78970b06279cd26", + "hash": "7a27fed57156f35cc7b2", "logging": {}, "modules": [ { @@ -2817,11 +2953,18 @@ exports[`StatsTestCases should print correct stats for stats-hooks 1`] = ` ], "id": "10", "identifier": "/tests/statsCases/stats-hooks/index.js", + "issuer": undefined, + "issuerId": undefined, + "issuerName": undefined, "issuerPath": [], "moduleType": "javascript/auto", "name": "./index.js", + "nameForCondition": "/tests/statsCases/stats-hooks/index.js", "reasons": [ { + "moduleId": undefined, + "moduleIdentifier": undefined, + "moduleName": undefined, "type": "entry", "userRequest": "./index", }, @@ -2854,10 +2997,10 @@ exports[`StatsTestCases should print correct stats for stats-hooks 1`] = ` exports[`StatsTestCases should print correct stats for stats-hooks 2`] = ` "PublicPath: (none) -asset bundle.js 589 bytes {main} [emitted111] (name: main) [testA: aaaaaa] +asset bundle.js 589 bytes [emitted111] (name: main) [testA: aaaaaa] Entrypoint main 589 bytes = bundle.js -./index.js [10] {main} -rspack compiled successfully (44e8a78970b06279cd26)" +./index.js +rspack compiled successfully (7a27fed57156f35cc7b2)" `; exports[`StatsTestCases should print correct stats for try-require--module 1`] = ` diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/index.js b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/index.js new file mode 100644 index 00000000000..07942dd5fe6 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/index.js @@ -0,0 +1,7 @@ +import { AaBb } from "./src/foo"; +import { CcDd } from "./src/bar"; + +it("should resolve right", () => { + expect(AaBb).toBe("Foo"); + expect(CcDd).toBe("Bar"); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/bar/cc-dd/index.js b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/bar/cc-dd/index.js new file mode 100644 index 00000000000..91b83d550c7 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/bar/cc-dd/index.js @@ -0,0 +1 @@ +export default "Bar"; diff --git a/packages/rspack/src/config/zod/module.ts b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/bar/cc-dd/style.css similarity index 100% rename from packages/rspack/src/config/zod/module.ts rename to packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/bar/cc-dd/style.css diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/index.js b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/index.js new file mode 100644 index 00000000000..1b25fd8f4ba --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/index.js @@ -0,0 +1 @@ +export default "Foo"; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/style/index.js b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/style/index.js new file mode 100644 index 00000000000..dd37ec7b3e1 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/src/foo/aa-bb/style/index.js @@ -0,0 +1 @@ +console.log("foo-style"); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/webpack.config.js new file mode 100644 index 00000000000..e4aaea82c43 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/plugin-import/webpack.config.js @@ -0,0 +1,34 @@ +module.exports = { + module: { + rules: [ + { + test: /.css/, + type: "asset" + } + ] + }, + module: { + rules: [ + { + test: /\.js$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + import: [ + { + libraryName: "./src/foo", + customName: "./src/foo/{{ kebabCase member }}", + style: true + }, + { + libraryName: "./src/bar", + customName: "./src/bar/{{ kebabCase member }}", + style: `{{ member }}/style.css` + } + ] + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/index.jsx new file mode 100644 index 00000000000..1063695cabb --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/index.jsx @@ -0,0 +1,5 @@ +function component () { + return
+} + +it('react refresh false should run', () => {}) diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/webpack.config.js new file mode 100644 index 00000000000..12de81331dc --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/react-refresh-false/webpack.config.js @@ -0,0 +1,17 @@ +module.exports = { + module: { + rules: [ + { + test: /\.js$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + react: { + refresh: false + } + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/index.jsx new file mode 100644 index 00000000000..37406fa8adc --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/index.jsx @@ -0,0 +1,5 @@ +const element =
; + +it("react automatic", () => { + expect(element.type).toBe("div"); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/webpack.config.js new file mode 100644 index 00000000000..a8957fc0beb --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-automic/webpack.config.js @@ -0,0 +1,17 @@ +module.exports = { + module: { + rules: [ + { + test: /\.js$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + react: { + runtime: "automatic" + } + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/index.jsx new file mode 100644 index 00000000000..00639c94454 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/index.jsx @@ -0,0 +1,7 @@ +import React from 'react'; + +const element =
; + +it("react classic", () => { + expect(element.type).toBe("div"); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/webpack.config.js new file mode 100644 index 00000000000..f260c6f55f4 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/react-runtime-classic/webpack.config.js @@ -0,0 +1,17 @@ +module.exports = { + module: { + rules: [ + { + test: /\.js$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + react: { + runtime: "classic" + } + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/custom/MyComponent.graphql.ts b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/custom/MyComponent.graphql.ts new file mode 100644 index 00000000000..bd816eaba4c --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/custom/MyComponent.graphql.ts @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/index.jsx new file mode 100644 index 00000000000..428e75ae807 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/index.jsx @@ -0,0 +1,11 @@ +const { graphql } = require("react-relay"); + +it("graphql", () => { + const query = graphql` + fragment MyComponent on Type { + field + } + `; + + expect(query).toBe(1); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/webpack.config.js new file mode 100644 index 00000000000..b602accac2c --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-custom/webpack.config.js @@ -0,0 +1,18 @@ +module.exports = { + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + relay: { + language: "typescript", + artifactDirectory: "./custom" + } + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/custom/MyComponent.graphql.ts b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/custom/MyComponent.graphql.ts new file mode 100644 index 00000000000..bd816eaba4c --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/custom/MyComponent.graphql.ts @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/index.jsx new file mode 100644 index 00000000000..428e75ae807 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/index.jsx @@ -0,0 +1,11 @@ +const { graphql } = require("react-relay"); + +it("graphql", () => { + const query = graphql` + fragment MyComponent on Type { + field + } + `; + + expect(query).toBe(1); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/relay.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/relay.config.js new file mode 100644 index 00000000000..cb368f17caf --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/relay.config.js @@ -0,0 +1,4 @@ +module.exports = { + language: "typescript", + artifactDirectory: "./custom" +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/webpack.config.js new file mode 100644 index 00000000000..9a0f39cf0a4 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-js/webpack.config.js @@ -0,0 +1,15 @@ +module.exports = { + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + relay: true + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/custom/MyComponent.graphql.ts b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/custom/MyComponent.graphql.ts new file mode 100644 index 00000000000..bd816eaba4c --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/custom/MyComponent.graphql.ts @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/index.jsx new file mode 100644 index 00000000000..428e75ae807 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/index.jsx @@ -0,0 +1,11 @@ +const { graphql } = require("react-relay"); + +it("graphql", () => { + const query = graphql` + fragment MyComponent on Type { + field + } + `; + + expect(query).toBe(1); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/relay.config.json b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/relay.config.json new file mode 100644 index 00000000000..a80d534946b --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/relay.config.json @@ -0,0 +1,4 @@ +{ + "language": "typescript", + "artifactDirectory": "./custom" +} diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/webpack.config.js new file mode 100644 index 00000000000..9a0f39cf0a4 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-config-json/webpack.config.js @@ -0,0 +1,15 @@ +module.exports = { + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + relay: true + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/__generated__/MyComponent.graphql.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/__generated__/MyComponent.graphql.js new file mode 100644 index 00000000000..bd816eaba4c --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/__generated__/MyComponent.graphql.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/index.jsx new file mode 100644 index 00000000000..428e75ae807 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/index.jsx @@ -0,0 +1,11 @@ +const { graphql } = require("react-relay"); + +it("graphql", () => { + const query = graphql` + fragment MyComponent on Type { + field + } + `; + + expect(query).toBe(1); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/webpack.config.js new file mode 100644 index 00000000000..9a0f39cf0a4 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-default/webpack.config.js @@ -0,0 +1,15 @@ +module.exports = { + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + relay: true + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/__generated__/MyComponent.graphql.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/__generated__/MyComponent.graphql.js new file mode 100644 index 00000000000..bd816eaba4c --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/__generated__/MyComponent.graphql.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/index.jsx b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/index.jsx new file mode 100644 index 00000000000..428e75ae807 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/index.jsx @@ -0,0 +1,11 @@ +const { graphql } = require("react-relay"); + +it("graphql", () => { + const query = graphql` + fragment MyComponent on Type { + field + } + `; + + expect(query).toBe(1); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/package.json b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/package.json new file mode 100644 index 00000000000..c7b2e5c1852 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/package.json @@ -0,0 +1,5 @@ +{ + "relay": { + "language": "javascript" + } +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/webpack.config.js new file mode 100644 index 00000000000..9a0f39cf0a4 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-json/webpack.config.js @@ -0,0 +1,15 @@ +module.exports = { + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + relay: true + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/custom/MyComponent.graphql.ts b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/custom/MyComponent.graphql.ts new file mode 100644 index 00000000000..0e35d0cbb39 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/custom/MyComponent.graphql.ts @@ -0,0 +1 @@ +module.exports = new Error("should never resolved here"); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/index.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/index.js new file mode 100644 index 00000000000..8449c845709 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/index.js @@ -0,0 +1,15 @@ +const { graphql } = require("react-relay"); + +it("graphql", () => { + // https://github.com/web-infra-dev/rspack/issues/2440 + // This will transformed to require('./custom/MyComponent.graphql.ts'), + // If succeed, `require` would become `__webpack_require__`, + // Then it will resolved to mock.js + const mock = graphql` + fragment MyComponent on Type { + field + } + `; + + expect(mock.found).toBeTruthy(); +}); diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/mock.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/mock.js new file mode 100644 index 00000000000..7911b6106eb --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/mock.js @@ -0,0 +1,3 @@ +module.exports = { + found: true +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/relay.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/relay.config.js new file mode 100644 index 00000000000..5ec440eecb7 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/relay.config.js @@ -0,0 +1,4 @@ +module.exports = { + language: "typescript", + artifactDirectory: "custom" +}; diff --git a/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/webpack.config.js b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/webpack.config.js new file mode 100644 index 00000000000..399dc2be1f9 --- /dev/null +++ b/packages/rspack/tests/configCases/builtin-swc-loader/relay-webpack-require/webpack.config.js @@ -0,0 +1,25 @@ +const { resolve } = require("path"); + +module.exports = { + resolve: { + alias: { + [resolve(__dirname, "./custom/MyComponent.graphql.ts")]: resolve( + __dirname, + "./mock.js" + ) + } + }, + module: { + rules: [ + { + test: /\.jsx?$/, + loader: "builtin:swc-loader", + options: { + rspackExperiments: { + relay: true + } + } + } + ] + } +}; diff --git a/packages/rspack/tests/configCases/builtins/minify-keep-classnames/index.js b/packages/rspack/tests/configCases/builtins/minify-keep-classnames/index.js new file mode 100644 index 00000000000..eff2802807b --- /dev/null +++ b/packages/rspack/tests/configCases/builtins/minify-keep-classnames/index.js @@ -0,0 +1,6 @@ +class KeepClass {} + +it("should keep class names", () => { + const name = KeepClass.name; + expect(name).toBe("KeepClass"); +}); diff --git a/packages/rspack/tests/configCases/builtins/minify-keep-classnames/webpack.config.js b/packages/rspack/tests/configCases/builtins/minify-keep-classnames/webpack.config.js new file mode 100644 index 00000000000..5d6e446a18d --- /dev/null +++ b/packages/rspack/tests/configCases/builtins/minify-keep-classnames/webpack.config.js @@ -0,0 +1,10 @@ +module.exports = { + builtins: { + minifyOptions: { + keepClassNames: true + } + }, + optimization: { + minimize: true + } +}; diff --git a/packages/rspack/tests/configCases/builtins/minify-keep-fn-names/index.js b/packages/rspack/tests/configCases/builtins/minify-keep-fn-names/index.js new file mode 100644 index 00000000000..482fb5f2b60 --- /dev/null +++ b/packages/rspack/tests/configCases/builtins/minify-keep-fn-names/index.js @@ -0,0 +1,6 @@ +function main() {} + +it("should keep fn names", () => { + const name = main.name; + expect(name).toBe("main"); +}); diff --git a/packages/rspack/tests/configCases/builtins/minify-keep-fn-names/webpack.config.js b/packages/rspack/tests/configCases/builtins/minify-keep-fn-names/webpack.config.js new file mode 100644 index 00000000000..22a5f464775 --- /dev/null +++ b/packages/rspack/tests/configCases/builtins/minify-keep-fn-names/webpack.config.js @@ -0,0 +1,10 @@ +module.exports = { + builtins: { + minifyOptions: { + keepFnNames: true + } + }, + optimization: { + minimize: true + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/alias/.gitignore b/packages/rspack/tests/configCases/resolve-new/alias/.gitignore new file mode 100644 index 00000000000..736e8ae58ad --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/alias/.gitignore @@ -0,0 +1 @@ +!node_modules \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/alias/a.js b/packages/rspack/tests/configCases/resolve-new/alias/a.js new file mode 100644 index 00000000000..6cd1d0075d4 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/alias/a.js @@ -0,0 +1 @@ +module.exports = "a"; diff --git a/packages/rspack/tests/configCases/resolve-new/alias/index.js b/packages/rspack/tests/configCases/resolve-new/alias/index.js new file mode 100644 index 00000000000..bf155eea171 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/alias/index.js @@ -0,0 +1,11 @@ +import value from "@b"; +import value2 from "xx"; +import value3 from "alias"; +import value4 from "ignored"; + +it("alias should work", () => { + expect(value).toBe("a"); + expect(value2).toBe("a"); + expect(value3).toBe("a"); + expect(value4).toStrictEqual({}); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/alias/node_modules/alias/index.js b/packages/rspack/tests/configCases/resolve-new/alias/node_modules/alias/index.js new file mode 100644 index 00000000000..62288897e28 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/alias/node_modules/alias/index.js @@ -0,0 +1 @@ +module.exports = require('@b'); \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/alias/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/alias/webpack.config.js new file mode 100644 index 00000000000..3f4ca322084 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/alias/webpack.config.js @@ -0,0 +1,28 @@ +const path = require("path"); +module.exports = { + entry: "./index.js", + resolve: { + alias: { + "@b": path.resolve(__dirname, "./a"), + xx: path.resolve(__dirname, "./a"), + ignored: path.resolve(__dirname, "./a") + } + }, + module: { + rules: [ + { + test: /\.js$/, + resolve: { + alias: { + ignored: false + } + } + } + ] + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/by-dependency/baz.js b/packages/rspack/tests/configCases/resolve-new/by-dependency/baz.js new file mode 100644 index 00000000000..c55720d996d --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/by-dependency/baz.js @@ -0,0 +1 @@ +export default "baz"; diff --git a/packages/rspack/tests/configCases/resolve-new/by-dependency/foo.bar b/packages/rspack/tests/configCases/resolve-new/by-dependency/foo.bar new file mode 100644 index 00000000000..d02ba545bd3 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/by-dependency/foo.bar @@ -0,0 +1 @@ +export default 'foo'; diff --git a/packages/rspack/tests/configCases/resolve-new/by-dependency/index.js b/packages/rspack/tests/configCases/resolve-new/by-dependency/index.js new file mode 100644 index 00000000000..ce785c88dc1 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/by-dependency/index.js @@ -0,0 +1,10 @@ +import foo from "./foo"; +import baz from "./baz"; + +it("should resolve 'foo.bar', byDependency '.bar' extension works", function () { + expect(foo).toBe("foo"); +}); + +it("should resolve 'baz.js', byDependency '...' extensions works", function () { + expect(baz).toBe("baz"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/by-dependency/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/by-dependency/webpack.config.js new file mode 100644 index 00000000000..4d9b6a85f17 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/by-dependency/webpack.config.js @@ -0,0 +1,15 @@ +module.exports = { + mode: "development", + resolve: { + byDependency: { + esm: { + extensions: [".bar", "..."] + } + } + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/.gitignore b/packages/rspack/tests/configCases/resolve-new/condition-exports/.gitignore new file mode 100644 index 00000000000..736e8ae58ad --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/.gitignore @@ -0,0 +1 @@ +!node_modules \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/index.js b/packages/rspack/tests/configCases/resolve-new/condition-exports/index.js new file mode 100644 index 00000000000..eaba5bcdec3 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/index.js @@ -0,0 +1,13 @@ +import esmImport from "exports-conditional"; +import esmImport2 from "exports-conditional-2"; + +const cjsImport = require("exports-conditional"); +const cjsImport2 = require("exports-conditional-2"); + +it("conditional exports should works", () => { + expect(esmImport).toBe("esm"); + expect(cjsImport).toBe("cjs"); + + expect(esmImport2).toBe("esm"); + expect(cjsImport2).toBe("cjs"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.cjs b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.cjs new file mode 100644 index 00000000000..55bf5b1d7ed --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.cjs @@ -0,0 +1 @@ +module.exports = "cjs" \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.js b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.js new file mode 100644 index 00000000000..389852fd434 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/lib.js @@ -0,0 +1 @@ +export default "esm" \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/package.json b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/package.json new file mode 100644 index 00000000000..5779f368dec --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional-2/package.json @@ -0,0 +1,10 @@ +{ + "name": "exports-conditional-2", + "version": "1.0.0", + "exports": { + "import": "./lib.js", + "require": "./lib.cjs" + }, + "type": "module", + "sideEffects": false +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.cjs b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.cjs new file mode 100644 index 00000000000..55bf5b1d7ed --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.cjs @@ -0,0 +1 @@ +module.exports = "cjs" \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.mjs b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.mjs new file mode 100644 index 00000000000..389852fd434 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/lib.mjs @@ -0,0 +1 @@ +export default "esm" \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/package.json b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/package.json new file mode 100644 index 00000000000..f2cfa71d52e --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/node_modules/exports-conditional/package.json @@ -0,0 +1,9 @@ +{ + "name": "exports-conditional", + "version": "1.0.0", + "exports": { + "import": "./lib.mjs", + "require": "./lib.cjs" + }, + "sideEffects": false +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/condition-exports/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/condition-exports/webpack.config.js new file mode 100644 index 00000000000..00c6f11bf8b --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/condition-exports/webpack.config.js @@ -0,0 +1,8 @@ +module.exports = { + entry: "./index.js", + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/.gitignore b/packages/rspack/tests/configCases/resolve-new/conditions/.gitignore new file mode 100644 index 00000000000..736e8ae58ad --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/.gitignore @@ -0,0 +1 @@ +!node_modules \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/index.js b/packages/rspack/tests/configCases/resolve-new/conditions/index.js new file mode 100644 index 00000000000..7cdec16e52a --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/index.js @@ -0,0 +1,9 @@ +import mappedValue from "exports-field/dist/main"; +import entryValue from "exports-field"; + +it("conditionNames should works", () => { + expect(mappedValue).toBe("lib/lib2/main"); + expect(entryValue).toBe("x"); + const pkgValue = require("exports-field/package.json"); + expect(pkgValue.name).toBe("exports-field"); +}); diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/other.css b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/index.js similarity index 100% rename from packages/rspack/tests/runtimeCases/runtime/split-css-chunk/other.css rename to packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/index.js diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/lib2/main.js b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/lib2/main.js new file mode 100644 index 00000000000..898c9cad72c --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/lib2/main.js @@ -0,0 +1 @@ +module.exports = "lib/lib2/main"; diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/main.js b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/main.js new file mode 100644 index 00000000000..010786ab1db --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/lib/main.js @@ -0,0 +1 @@ +module.exports = "lib/main"; diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/package.json b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/package.json new file mode 100644 index 00000000000..87941561f6c --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/package.json @@ -0,0 +1,19 @@ +{ + "name": "exports-field", + "version": "1.0.0", + "main": "./main.js", + "exports": { + ".": "./x.js", + "./dist/": { + "pack": [ + "./lib/lib2/", + "./lib/" + ], + "node": "./lib/", + "default": "./lib/" + }, + "./dist/a.js": "./../../a.js", + "./package.json": "./package.json" + }, + "sideEffects": false +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/x.js b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/x.js new file mode 100644 index 00000000000..7a5b647aa23 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/node_modules/exports-field/x.js @@ -0,0 +1 @@ +module.exports = "x" \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/conditions/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/conditions/webpack.config.js new file mode 100644 index 00000000000..0305b7b748e --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/conditions/webpack.config.js @@ -0,0 +1,11 @@ +module.exports = { + entry: "./index.js", + resolve: { + conditionNames: ["pack"] + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/exports-fields/.gitignore b/packages/rspack/tests/configCases/resolve-new/exports-fields/.gitignore new file mode 100644 index 00000000000..736e8ae58ad --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/exports-fields/.gitignore @@ -0,0 +1 @@ +!node_modules \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/exports-fields/index.js b/packages/rspack/tests/configCases/resolve-new/exports-fields/index.js new file mode 100644 index 00000000000..56297d031f1 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/exports-fields/index.js @@ -0,0 +1,4 @@ +it("should resolve both alternatives", () => { + const b = require("exports-field"); + expect(b).toBe("b"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/b.js b/packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/b.js new file mode 100644 index 00000000000..b855beca739 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/b.js @@ -0,0 +1 @@ +module.exports = 'b' diff --git a/packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/package.json b/packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/package.json new file mode 100644 index 00000000000..9f235c44802 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/exports-fields/node_modules/exports-field/package.json @@ -0,0 +1,5 @@ +{ + "name": "exports-field", + "version": "1.0.0", + "b": "./b.js" +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/exports-fields/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/exports-fields/webpack.config.js new file mode 100644 index 00000000000..d259bce369a --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/exports-fields/webpack.config.js @@ -0,0 +1,11 @@ +module.exports = { + entry: "./index.js", + resolve: { + exportsFields: ["a", "b"] + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/extension-alias/index.js b/packages/rspack/tests/configCases/resolve-new/extension-alias/index.js new file mode 100644 index 00000000000..634bb57d084 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/extension-alias/index.js @@ -0,0 +1,5 @@ +import value from "./src/index.mjs"; + +it("extension-alias should work", () => { + expect(value).toBe("in ts"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/extension-alias/src/index.mts b/packages/rspack/tests/configCases/resolve-new/extension-alias/src/index.mts new file mode 100644 index 00000000000..258a6f0a1ae --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/extension-alias/src/index.mts @@ -0,0 +1 @@ +module.exports = "in ts" diff --git a/packages/rspack/tests/configCases/resolve-new/extension-alias/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/extension-alias/webpack.config.js new file mode 100644 index 00000000000..b04b755aa55 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/extension-alias/webpack.config.js @@ -0,0 +1,13 @@ +module.exports = { + entry: "./index.js", + resolve: { + extensionAlias: { + ".mjs": [".mts"] + } + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/fallback/#/a.js b/packages/rspack/tests/configCases/resolve-new/fallback/#/a.js new file mode 100644 index 00000000000..bd816eaba4c --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/fallback/#/a.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/resolve-new/fallback/a/1.js b/packages/rspack/tests/configCases/resolve-new/fallback/a/1.js new file mode 100644 index 00000000000..bd816eaba4c --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/fallback/a/1.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/resolve-new/fallback/a/2.js b/packages/rspack/tests/configCases/resolve-new/fallback/a/2.js new file mode 100644 index 00000000000..66a141057e0 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/fallback/a/2.js @@ -0,0 +1 @@ +module.exports = "not 2"; diff --git a/packages/rspack/tests/configCases/resolve-new/fallback/b/2.js b/packages/rspack/tests/configCases/resolve-new/fallback/b/2.js new file mode 100644 index 00000000000..4bbffde1044 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/fallback/b/2.js @@ -0,0 +1 @@ +module.exports = 2; diff --git a/packages/rspack/tests/configCases/resolve-new/fallback/index.js b/packages/rspack/tests/configCases/resolve-new/fallback/index.js new file mode 100644 index 00000000000..846ed16deb8 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/fallback/index.js @@ -0,0 +1,14 @@ +it("ignores the fallback if an existing module is present", () => { + const two = require("./b/2"); + expect(two).toBe(2); +}); + +it("can fallback if the module does not exist", () => { + const one = require("./b/1"); + expect(one).toBe(1); +}); + +it("# alias should work", () => { + const one = require("#/a"); + expect(one).toBe(1); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/fallback/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/fallback/webpack.config.js new file mode 100644 index 00000000000..34f93ea3b9d --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/fallback/webpack.config.js @@ -0,0 +1,17 @@ +const path = require("path"); +/** @type {import("@rspack/core").Configuration} */ +module.exports = { + resolve: { + alias: { + "#": path.resolve(__dirname, "#") + }, + fallback: { + "./b": path.resolve(__dirname, "a") + } + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/modules/a/foo/a.js b/packages/rspack/tests/configCases/resolve-new/modules/a/foo/a.js new file mode 100644 index 00000000000..e94fef18587 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/modules/a/foo/a.js @@ -0,0 +1 @@ +export default "a"; diff --git a/packages/rspack/tests/configCases/resolve-new/modules/a/foo/package.json b/packages/rspack/tests/configCases/resolve-new/modules/a/foo/package.json new file mode 100644 index 00000000000..fd262b44d2d --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/modules/a/foo/package.json @@ -0,0 +1,5 @@ +{ + "name": "a-foo", + "version": "1.0.0", + "module": "./a.js" +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/modules/b/foo/b.js b/packages/rspack/tests/configCases/resolve-new/modules/b/foo/b.js new file mode 100644 index 00000000000..eff703ff465 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/modules/b/foo/b.js @@ -0,0 +1 @@ +export default "b"; diff --git a/packages/rspack/tests/configCases/resolve-new/modules/b/foo/package.json b/packages/rspack/tests/configCases/resolve-new/modules/b/foo/package.json new file mode 100644 index 00000000000..cea8dde66c0 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/modules/b/foo/package.json @@ -0,0 +1,5 @@ +{ + "name": "b-foo", + "version": "1.0.0", + "module": "./b.js" +} \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve-new/modules/index.js b/packages/rspack/tests/configCases/resolve-new/modules/index.js new file mode 100644 index 00000000000..c8d6b184314 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/modules/index.js @@ -0,0 +1,7 @@ +import a from "foo/a"; +import b from "foo/b"; + +it("resolve modules should work fine", async () => { + expect(a).toEqual("a"); + expect(b).toEqual("b"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/modules/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/modules/webpack.config.js new file mode 100644 index 00000000000..e1dea989664 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/modules/webpack.config.js @@ -0,0 +1,12 @@ +const path = require("path"); + +module.exports = { + resolve: { + modules: [path.resolve(__dirname, "a"), path.resolve(__dirname, "b")] + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/multi-alias/a/1.js b/packages/rspack/tests/configCases/resolve-new/multi-alias/a/1.js new file mode 100644 index 00000000000..bd816eaba4c --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/multi-alias/a/1.js @@ -0,0 +1 @@ +module.exports = 1; diff --git a/packages/rspack/tests/configCases/resolve-new/multi-alias/b/2.js b/packages/rspack/tests/configCases/resolve-new/multi-alias/b/2.js new file mode 100644 index 00000000000..4bbffde1044 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/multi-alias/b/2.js @@ -0,0 +1 @@ +module.exports = 2; diff --git a/packages/rspack/tests/configCases/resolve-new/multi-alias/index.js b/packages/rspack/tests/configCases/resolve-new/multi-alias/index.js new file mode 100644 index 00000000000..b64161dbec4 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/multi-alias/index.js @@ -0,0 +1,6 @@ +it("should resolve both alternatives", () => { + const one = require("_/1"); + const two = require("_/2"); + expect(one).toBe(1); + expect(two).toBe(2); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/multi-alias/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/multi-alias/webpack.config.js new file mode 100644 index 00000000000..8cc9115cfe0 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/multi-alias/webpack.config.js @@ -0,0 +1,14 @@ +const path = require("path"); +/** @type {import("@rspack/core").Configuration} */ +module.exports = { + resolve: { + alias: { + _: [path.resolve(__dirname, "a"), path.resolve(__dirname, "b")] + } + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/bar.mjs b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/bar.mjs new file mode 100644 index 00000000000..4548a26ba14 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/bar.mjs @@ -0,0 +1 @@ +export default 'bar' diff --git a/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/baz.js b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/baz.js new file mode 100644 index 00000000000..c55720d996d --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/baz.js @@ -0,0 +1 @@ +export default "baz"; diff --git a/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/foo.bar b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/foo.bar new file mode 100644 index 00000000000..c44fd9f2345 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/foo.bar @@ -0,0 +1,3 @@ +import bar from './bar' + +export default bar; diff --git a/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/index.js b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/index.js new file mode 100644 index 00000000000..72cd80bba74 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/index.js @@ -0,0 +1,10 @@ +import foo from "./foo"; +import baz from "./baz"; + +it("should resolve 'foo.bar', byDependency '.bar' extension works", function () { + expect(foo).toBe("bar"); +}); + +it("should resolve 'baz.js', byDependency '...' extensions works", function () { + expect(baz).toBe("baz"); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/webpack.config.js new file mode 100644 index 00000000000..0f7c56ec7af --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/nested-by-dependency/webpack.config.js @@ -0,0 +1,31 @@ +module.exports = { + mode: "development", + resolve: { + byDependency: { + esm: { + extensions: [".bar", "..."] // enable resolve .bar + } + } + }, + module: { + rules: [ + { + test: /\.bar$/, + resolve: { + byDependency: { + // dependencyType of import is esm + esm: { + extensions: [".mjs", "..."], // enable resolve .mjs in .bar + fullySpecified: false // resolve .mjs without fullySpecified in .bar + } + } + } + } + ] + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/index.js b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/index.js new file mode 100644 index 00000000000..8ab1d3fdbec --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/index.js @@ -0,0 +1,19 @@ +import fakeValue from "fake"; +import fileValue from "@/file"; +import baseUrlFileValue from "src/file"; +import fileValueWithQuery1 from "@/file?a=b"; +import fileValueWithQuery2 from "@/file?a=c"; + +it("should require real files by alias tsconfig paths", () => { + expect(fakeValue).toEqual("real"); + expect(fileValue).toStrictEqual({}); +}); + +it("absolute path relative base url should works", () => { + expect(baseUrlFileValue).toStrictEqual({}); + expect(baseUrlFileValue === fileValue).toBe(true); + expect(fileValueWithQuery1).toStrictEqual({}); + expect(fileValueWithQuery2).toStrictEqual({}); + expect(fileValueWithQuery1 !== fileValueWithQuery2).toBe(true); + expect(fileValueWithQuery1 !== baseUrlFileValue).toBe(true); +}); diff --git a/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/real.js b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/real.js new file mode 100644 index 00000000000..c7854517557 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/real.js @@ -0,0 +1 @@ +module.exports = "real"; diff --git a/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/src/file.js b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/src/file.js new file mode 100644 index 00000000000..ff8b4c56321 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/src/file.js @@ -0,0 +1 @@ +export default {}; diff --git a/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/tsconfig.json b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/tsconfig.json new file mode 100644 index 00000000000..d289e24a3f1 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "paths": { + "fake": ["./real.js"], + "@/*": ["./src/*"] + } + } +} diff --git a/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/webpack.config.js b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/webpack.config.js new file mode 100644 index 00000000000..b56c8cc0646 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve-new/tsconfig-paths-map/webpack.config.js @@ -0,0 +1,15 @@ +const path = require("path"); + +module.exports = { + entry: { + main: "./index.js" + }, + resolve: { + tsConfigPath: path.resolve(__dirname, "./tsconfig.json") + }, + experiments: { + rspackFuture: { + newResolver: true + } + } +}; diff --git a/packages/rspack/tests/configCases/split-chunks/max-size-casing/webpack.config.js b/packages/rspack/tests/configCases/split-chunks/max-size-casing/webpack.config.js index 30a479d8b64..780a4088dfc 100644 --- a/packages/rspack/tests/configCases/split-chunks/max-size-casing/webpack.config.js +++ b/packages/rspack/tests/configCases/split-chunks/max-size-casing/webpack.config.js @@ -8,7 +8,7 @@ module.exports = { }, optimization: { splitChunks: { - hidePathInfo: false, + // hidePathInfo: false, minSize: 50, maxSize: 100 } diff --git a/packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile1.txt b/packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile1.txt new file mode 100644 index 00000000000..b3c55476180 --- /dev/null +++ b/packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile1.txt @@ -0,0 +1 @@ +file1contents \ No newline at end of file diff --git a/packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile2.txt b/packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile2.txt new file mode 100644 index 00000000000..26ebc2e98bd --- /dev/null +++ b/packages/rspack/tests/copyPlugin/fixtures/watch/_t2/directory/tempfile2.txt @@ -0,0 +1 @@ +file2contents \ No newline at end of file diff --git a/packages/rspack/tests/fixtures/abc-query.js b/packages/rspack/tests/fixtures/abc-query.js new file mode 100644 index 00000000000..eb18ea7bf12 --- /dev/null +++ b/packages/rspack/tests/fixtures/abc-query.js @@ -0,0 +1,3 @@ +exports.a = require("./a?a=1"); +// exports.b = require("./b?b=2"); +exports.c = require("./c?c=3"); diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.css b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.css new file mode 100644 index 00000000000..c4845173e30 --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.css @@ -0,0 +1,3 @@ +#dynamic { + background: red; +} \ No newline at end of file diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.js new file mode 100644 index 00000000000..3b529e8a100 --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/common.js @@ -0,0 +1 @@ +import "./common.css"; diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index1.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index1.js new file mode 100644 index 00000000000..47d51f29001 --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index1.js @@ -0,0 +1,10 @@ +it("should load css chunk", function (done) { + import("./share").then(module => { + expect(module.value).toBe(1); + // test is only for css loading + if (__webpack_require__.f.css) { + expect(document.getElementsByTagName("link").length).toBe(2); + } + done(); + }); +}); diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index2.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index2.js new file mode 100644 index 00000000000..a8a18775ed3 --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index2.js @@ -0,0 +1 @@ +import "./common"; diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index3.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index3.js new file mode 100644 index 00000000000..57e149a1413 --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/index3.js @@ -0,0 +1 @@ +import "./share"; diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/share.css b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/share.css new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/share.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/share.js new file mode 100644 index 00000000000..8dff76c666a --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/share.js @@ -0,0 +1,4 @@ +import "./share.css"; +import "./common.css"; + +export const value = 1; diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/webpack.config.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/webpack.config.js new file mode 100644 index 00000000000..c45ae6cace9 --- /dev/null +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk-async/webpack.config.js @@ -0,0 +1,29 @@ +module.exports = { + entry: { + main: './index1.js', + main2: './index2.js', + main3: './index3.js' + }, + output: { + chunkFilename: '[id].[contenthash].js' + }, + optimization: { + splitChunks: { + cacheGroups: { + common: { + chunks: 'all', + test: /common/, + enforce: true, + name: 'common' + }, + share: { + chunks: 'all', + test: /share/, + enforce: true, + name: 'share' + } + } + }, + runtimeChunk: 'single' + } +} diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/common.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/common.js index 3b529e8a100..0fc1dc430c4 100644 --- a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/common.js +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/common.js @@ -1 +1,3 @@ import "./common.css"; + +export const value = 1; diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index1.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index1.js index d8511e1e619..d056a758b98 100644 --- a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index1.js +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index1.js @@ -1,5 +1,6 @@ it("should load css chunk", function (done) { import("./common").then(module => { + expect(module.value).toBe(1); // test is only for css loading if (__webpack_require__.f.css) { expect(document.getElementsByTagName("link").length).toBe(1); @@ -7,7 +8,3 @@ it("should load css chunk", function (done) { done(); }); }); - -import "./common.css"; - -// ./common.css is initial chunks and also be async chunks. diff --git a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index2.js b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index2.js index 77130c9452f..a8a18775ed3 100644 --- a/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index2.js +++ b/packages/rspack/tests/runtimeCases/runtime/split-css-chunk/index2.js @@ -1,3 +1 @@ import "./common"; - -import("./other.css"); diff --git a/packages/rspack/tests/statsCases/ignore-warning/index.js b/packages/rspack/tests/statsCases/ignore-warning/index.js new file mode 100644 index 00000000000..9724af41230 --- /dev/null +++ b/packages/rspack/tests/statsCases/ignore-warning/index.js @@ -0,0 +1 @@ +require("./index.scss"); diff --git a/packages/rspack/tests/statsCases/ignore-warning/index.scss b/packages/rspack/tests/statsCases/ignore-warning/index.scss new file mode 100644 index 00000000000..c15e450fd09 --- /dev/null +++ b/packages/rspack/tests/statsCases/ignore-warning/index.scss @@ -0,0 +1,3 @@ +.c { + width: (12px/4px); +} \ No newline at end of file diff --git a/packages/rspack/tests/statsCases/ignore-warning/webpack.config.js b/packages/rspack/tests/statsCases/ignore-warning/webpack.config.js new file mode 100644 index 00000000000..14a197b5eb7 --- /dev/null +++ b/packages/rspack/tests/statsCases/ignore-warning/webpack.config.js @@ -0,0 +1,13 @@ +module.exports = { + stats: "errors-warnings", + ignoreWarnings: [/Using \/ for division outside/], + module: { + rules: [ + { + test: /\.s[ac]ss$/i, + use: [{ loader: "sass-loader" }], + type: "css" + } + ] + } +}; diff --git a/packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/index.ts b/packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/index.ts new file mode 100644 index 00000000000..4ebce347842 --- /dev/null +++ b/packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/index.ts @@ -0,0 +1 @@ +export error; diff --git a/packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/webpack.config.js b/packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/webpack.config.js new file mode 100644 index 00000000000..7a90b69b704 --- /dev/null +++ b/packages/rspack/tests/statsCases/parse-error-builtin-swc-loader/webpack.config.js @@ -0,0 +1,18 @@ +module.exports = { + stats: "errors-warnings", + module: { + rules: [ + { + test: /\.ts$/, + loader: "builtin:swc-loader", + options: { + jsc: { + parser: { + syntax: "typescript" + } + } + } + } + ] + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8ee021190c6..98e19cce8ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -879,6 +879,7 @@ importers: '@rspack/core': workspace:* '@rspack/dev-client': workspace:* '@rspack/dev-server': workspace:* + '@rspack/plugin-react-refresh': workspace:* '@types/fs-extra': 11.0.1 fs-extra: 11.1.1 postcss: ^8.4.21 @@ -886,12 +887,15 @@ importers: react: ^18.2.0 react-dom: ^18.2.0 tailwindcss: ^3.3.0 + vue: 3.2.47 + vue-loader: ^17.2.2 ws: 8.8.1 devDependencies: '@playwright/test': 1.35.0 '@rspack/core': link:../rspack '@rspack/dev-client': link:../rspack-dev-client '@rspack/dev-server': link:../rspack-dev-server + '@rspack/plugin-react-refresh': link:../rspack-plugin-react-refresh '@types/fs-extra': 11.0.1 fs-extra: 11.1.1 postcss: 8.4.21 @@ -899,25 +903,26 @@ importers: react: 18.2.0 react-dom: 18.2.0_react@18.2.0 tailwindcss: 3.3.1_postcss@8.4.21 + vue: 3.2.47 + vue-loader: 17.2.2_vue@3.2.47 ws: 8.8.1 packages/rspack: specifiers: '@rspack/binding': workspace:* '@rspack/core': workspace:* - '@rspack/dev-client': workspace:* '@rspack/plugin-minify': workspace:^ '@rspack/plugin-node-polyfill': workspace:^ '@swc/helpers': 0.5.1 '@types/watchpack': ^2.4.0 '@types/webpack-sources': 3.2.0 '@types/ws': 8.5.3 - ajv: ^8.12.0 babel-loader: ^9.1.0 babel-plugin-import: ^1.13.5 browserslist: ^4.21.3 compare-versions: 6.0.0-rc.1 copy-webpack-plugin: '5' + cross-env: ^7.0.3 enhanced-resolve: 5.12.0 file-loader: ^6.2.0 graceful-fs: 4.2.10 @@ -945,7 +950,6 @@ importers: zod-validation-error: 1.2.0 dependencies: '@rspack/binding': link:../../crates/node_binding - '@rspack/dev-client': link:../rspack-dev-client '@swc/helpers': 0.5.1 browserslist: 4.21.4 compare-versions: 6.0.0-rc.1 @@ -968,10 +972,10 @@ importers: '@types/watchpack': 2.4.0 '@types/webpack-sources': 3.2.0 '@types/ws': 8.5.3 - ajv: 8.12.0 babel-loader: 9.1.2 babel-plugin-import: 1.13.5 copy-webpack-plugin: 5.1.2 + cross-env: 7.0.3 file-loader: 6.2.0 jest-serializer-path: 0.1.15 less: 4.1.3 @@ -1027,20 +1031,18 @@ importers: packages/rspack-dev-client: specifiers: - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10 + '@rspack/plugin-react-refresh': workspace:* react-refresh: 0.14.0 - webpack: 5.76.0 dependencies: - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10_kpkvch5jhx6s5tusmqrn4u64d4 + '@rspack/plugin-react-refresh': link:../rspack-plugin-react-refresh devDependencies: react-refresh: 0.14.0 - webpack: 5.76.0 packages/rspack-dev-server: specifiers: '@rspack/core': workspace:* - '@rspack/dev-client': workspace:* '@rspack/dev-server': workspace:* + '@rspack/plugin-react-refresh': workspace:* '@types/connect-history-api-fallback': 1.3.5 '@types/express': 4.17.14 '@types/mime-types': 2.1.1 @@ -1057,8 +1059,8 @@ importers: webpack-dev-server: 4.13.1 ws: 8.8.1 dependencies: - '@rspack/dev-client': link:../rspack-dev-client '@rspack/dev-server': 'link:' + '@rspack/plugin-react-refresh': link:../rspack-plugin-react-refresh chokidar: 3.5.3 connect-history-api-fallback: 2.0.0 express: 4.18.1 @@ -1170,6 +1172,15 @@ importers: util: 0.12.5 vm-browserify: 1.1.2 + packages/rspack-plugin-react-refresh: + specifiers: + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10 + react-refresh: 0.14.0 + dependencies: + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10_react-refresh@0.14.0 + devDependencies: + react-refresh: 0.14.0 + scripts: specifiers: '@actions/core': 1.10.0 @@ -1190,7 +1201,6 @@ importers: specifiers: '@rspack/binding': workspace:* '@rspack/core': workspace:* - '@rspack/dev-client': workspace:* '@rspack/plugin-minify': workspace:^ '@rspack/plugin-node-polyfill': workspace:^ '@swc/helpers': 0.5.1 @@ -1240,7 +1250,6 @@ importers: webpack-sources: 3.2.3 dependencies: '@rspack/binding': link:../crates/node_binding - '@rspack/dev-client': link:../packages/rspack-dev-client '@swc/helpers': 0.5.1 browserslist: 4.21.4 enhanced-resolve: 5.12.0 @@ -1507,7 +1516,7 @@ packages: optional: true dependencies: ajv: 8.12.0 - ajv-formats: 2.1.1_ajv@8.12.0 + ajv-formats: 2.1.1 jsonc-parser: 3.2.0 rxjs: 7.8.1 source-map: 0.7.4 @@ -1523,7 +1532,7 @@ packages: optional: true dependencies: ajv: 8.12.0 - ajv-formats: 2.1.1_ajv@8.12.0 + ajv-formats: 2.1.1 chokidar: 3.5.3 jsonc-parser: 3.2.0 rxjs: 7.8.1 @@ -1987,10 +1996,10 @@ packages: '@babel/generator': 7.21.5 '@babel/helper-module-transforms': 7.21.5 '@babel/helpers': 7.21.0 - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.5 '@babel/template': 7.20.7 '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 convert-source-map: 1.8.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -2151,7 +2160,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-explode-assignable-expression': 7.18.6 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/helper-compilation-targets/7.20.7: @@ -2458,7 +2467,7 @@ packages: resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/helper-function-name/7.21.0: @@ -2466,7 +2475,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.20.7 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 /@babel/helper-function-name/7.22.5: resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} @@ -2480,7 +2489,7 @@ packages: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 /@babel/helper-hoist-variables/7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} @@ -2493,14 +2502,14 @@ packages: resolution: {integrity: sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/helper-member-expression-to-functions/7.21.5: resolution: {integrity: sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/helper-member-expression-to-functions/7.22.5: @@ -2580,7 +2589,7 @@ packages: resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/helper-optimise-call-expression/7.22.5: @@ -2617,7 +2626,7 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2632,7 +2641,7 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2647,7 +2656,7 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2660,7 +2669,7 @@ packages: '@babel/helper-member-expression-to-functions': 7.18.9 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/traverse': 7.21.2 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2674,7 +2683,7 @@ packages: '@babel/helper-optimise-call-expression': 7.18.6 '@babel/template': 7.20.7 '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2697,7 +2706,7 @@ packages: resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 /@babel/helper-simple-access/7.21.5: resolution: {integrity: sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==} @@ -2717,7 +2726,7 @@ packages: resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/helper-skip-transparent-expression-wrappers/7.22.5: @@ -2781,7 +2790,7 @@ packages: '@babel/helper-function-name': 7.21.0 '@babel/template': 7.20.7 '@babel/traverse': 7.21.2 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -4760,7 +4769,7 @@ packages: '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.21.5 - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -4775,7 +4784,7 @@ packages: '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.21.5 - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.22.5 transitivePeerDependencies: - supports-color dev: true @@ -5036,7 +5045,7 @@ packages: '@babel/helper-module-imports': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.21.0 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@babel/plugin-transform-react-pure-annotations/7.18.6_@babel+core@7.21.0: @@ -7728,49 +7737,10 @@ packages: find-up: 5.0.0 html-entities: 2.3.3 loader-utils: 2.0.4 - schema-utils: 3.1.1 + schema-utils: 3.1.2 source-map: 0.7.4 dev: true - /@pmmmwh/react-refresh-webpack-plugin/0.5.10_kpkvch5jhx6s5tusmqrn4u64d4: - resolution: {integrity: sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==} - engines: {node: '>= 10.13'} - peerDependencies: - '@types/webpack': 4.x || 5.x - react-refresh: '>=0.10.0 <1.0.0' - sockjs-client: ^1.4.0 - type-fest: '>=0.17.0 <4.0.0' - webpack: '>=4.43.0 <6.0.0' - webpack-dev-server: 3.x || 4.x - webpack-hot-middleware: 2.x - webpack-plugin-serve: 0.x || 1.x - peerDependenciesMeta: - '@types/webpack': - optional: true - sockjs-client: - optional: true - type-fest: - optional: true - webpack-dev-server: - optional: true - webpack-hot-middleware: - optional: true - webpack-plugin-serve: - optional: true - dependencies: - ansi-html-community: 0.0.8 - common-path-prefix: 3.0.0 - core-js-pure: 3.26.1 - error-stack-parser: 2.1.4 - find-up: 5.0.0 - html-entities: 2.3.3 - loader-utils: 2.0.4 - react-refresh: 0.14.0 - schema-utils: 3.1.1 - source-map: 0.7.4 - webpack: 5.76.0 - dev: false - /@pmmmwh/react-refresh-webpack-plugin/0.5.10_react-refresh@0.11.0: resolution: {integrity: sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==} engines: {node: '>= 10.13'} @@ -7805,7 +7775,7 @@ packages: html-entities: 2.3.3 loader-utils: 2.0.4 react-refresh: 0.11.0 - schema-utils: 3.1.1 + schema-utils: 3.1.2 source-map: 0.7.4 dev: true @@ -7843,9 +7813,8 @@ packages: html-entities: 2.3.3 loader-utils: 2.0.4 react-refresh: 0.14.0 - schema-utils: 3.1.1 + schema-utils: 3.1.2 source-map: 0.7.4 - dev: true /@pmmmwh/react-refresh-webpack-plugin/0.5.10_rywehdfw6hkk2xngu2c3zrnyba: resolution: {integrity: sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==} @@ -7881,7 +7850,7 @@ packages: html-entities: 2.3.3 loader-utils: 2.0.4 react-refresh: 0.14.0 - schema-utils: 3.1.1 + schema-utils: 3.1.2 source-map: 0.7.4 webpack-hot-middleware: 2.25.3 dev: true @@ -9850,7 +9819,7 @@ packages: resolution: {integrity: sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==} engines: {node: '>=10'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 entities: 4.4.0 dev: true @@ -11132,8 +11101,8 @@ packages: /@types/babel__core/7.1.19: resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} dependencies: - '@babel/parser': 7.21.8 - '@babel/types': 7.21.5 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.18.1 @@ -11142,20 +11111,20 @@ packages: /@types/babel__generator/7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@types/babel__template/7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.21.8 - '@babel/types': 7.21.5 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 dev: true /@types/babel__traverse/7.18.1: resolution: {integrity: sha512-FSdLaZh2UxaMuLp9lixWaHq/golWTRWOnRsAXzDTDSDOQLuZb1nsdCt6pJSPWSEQt2eFZ2YVk3oYhn+1kLMeMA==} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /@types/body-parser/1.19.2: @@ -11667,7 +11636,7 @@ packages: /@vue/compiler-core/3.2.47: resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==} dependencies: - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.5 '@vue/shared': 3.2.47 estree-walker: 2.0.2 source-map: 0.6.1 @@ -11709,7 +11678,7 @@ packages: /@vue/compiler-sfc/3.2.47: resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==} dependencies: - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.5 '@vue/compiler-core': 3.2.47 '@vue/compiler-dom': 3.2.47 '@vue/compiler-ssr': 3.2.47 @@ -11813,7 +11782,7 @@ packages: /@vue/reactivity-transform/3.2.47: resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==} dependencies: - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.5 '@vue/compiler-core': 3.2.47 '@vue/shared': 3.2.47 estree-walker: 2.0.2 @@ -12273,10 +12242,8 @@ packages: ajv: 6.12.6 dev: true - /ajv-formats/2.1.1_ajv@8.12.0: + /ajv-formats/2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 peerDependenciesMeta: ajv: optional: true @@ -12820,7 +12787,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/template': 7.20.7 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 '@types/babel__core': 7.1.19 '@types/babel__traverse': 7.18.1 dev: true @@ -13032,7 +12999,7 @@ packages: resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==} engines: {node: '>= 10.0.0'} dependencies: - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 dev: true /bail/1.0.5: @@ -14441,8 +14408,8 @@ packages: /constantinople/4.0.1: resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} dependencies: - '@babel/parser': 7.21.8 - '@babel/types': 7.21.5 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 dev: true /constants-browserify/1.0.0: @@ -16143,7 +16110,7 @@ packages: engines: {node: '>=8.3.0'} dependencies: '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 c8: 7.13.0 transitivePeerDependencies: - supports-color @@ -19322,7 +19289,7 @@ packages: '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.21.0 '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.21.0 '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 '@jest/expect-utils': 29.5.0 '@jest/transform': 29.5.0 '@jest/types': 29.5.0 @@ -19483,7 +19450,7 @@ packages: '@babel/preset-env': ^7.1.6 dependencies: '@babel/core': 7.21.0 - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.5 '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.21.0 '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.21.0 '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.21.0 @@ -19513,7 +19480,7 @@ packages: '@babel/preset-env': ^7.1.6 dependencies: '@babel/core': 7.21.0 - '@babel/parser': 7.21.8 + '@babel/parser': 7.22.5 '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.21.0 '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.21.0 '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.21.0 @@ -23323,7 +23290,7 @@ packages: dependencies: '@babel/core': 7.21.0 '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 + '@babel/types': 7.22.5 '@types/babel__traverse': 7.18.1 '@types/doctrine': 0.0.5 '@types/resolve': 1.20.2 @@ -24518,6 +24485,7 @@ packages: '@types/json-schema': 7.0.11 ajv: 6.12.6 ajv-keywords: 3.5.2_ajv@6.12.6 + dev: true /schema-utils/3.1.2: resolution: {integrity: sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==} @@ -24533,7 +24501,7 @@ packages: dependencies: '@types/json-schema': 7.0.11 ajv: 8.12.0 - ajv-formats: 2.1.1_ajv@8.12.0 + ajv-formats: 2.1.1 ajv-keywords: 5.1.0_ajv@8.12.0 /schema-utils/4.0.1: @@ -24542,7 +24510,7 @@ packages: dependencies: '@types/json-schema': 7.0.11 ajv: 8.12.0 - ajv-formats: 2.1.1_ajv@8.12.0 + ajv-formats: 2.1.1 ajv-keywords: 5.1.0_ajv@8.12.0 /scroll-into-view-if-needed/2.2.20: @@ -28298,8 +28266,8 @@ packages: resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==} engines: {node: '>= 10.0.0'} dependencies: - '@babel/parser': 7.21.8 - '@babel/types': 7.21.5 + '@babel/parser': 7.22.5 + '@babel/types': 7.22.5 assert-never: 1.2.1 babel-walk: 3.0.0-canary-5 dev: true diff --git a/webpack-test/package.json b/webpack-test/package.json index c22b75f5a03..38c2c6d0a58 100644 --- a/webpack-test/package.json +++ b/webpack-test/package.json @@ -55,7 +55,6 @@ }, "dependencies": { "@rspack/binding": "workspace:*", - "@rspack/dev-client": "workspace:*", "@swc/helpers": "0.5.1", "browserslist": "^4.21.3", "enhanced-resolve": "5.12.0",