diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33bfe186f0686f..656ce7cd5f1d7d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -147,7 +147,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: taiki-e/checkout-action@v1 - - uses: crate-ci/typos@v1.27.3 + - uses: crate-ci/typos@v1.28.2 with: files: . @@ -285,11 +285,12 @@ jobs: - uses: Boshen/setup-rust@main if: steps.filter.outputs.src == 'true' with: - save-cache: ${{ github.ref_name == 'main' }} cache-key: warm - uses: ./.github/actions/pnpm if: steps.filter.outputs.src == 'true' - - run: pnpm run build + - run: pnpm run build-dev if: steps.filter.outputs.src == 'true' - run: pnpm run test if: steps.filter.outputs.src == 'true' + - run: git diff --exit-code # Must commit everything + if: steps.filter.outputs.src == 'true' diff --git a/.github/workflows/deny.yml b/.github/workflows/deny.yml index d0448fb95f9a03..99deaca20a5492 100644 --- a/.github/workflows/deny.yml +++ b/.github/workflows/deny.yml @@ -6,11 +6,15 @@ on: types: [opened, synchronize] paths: - "Cargo.lock" + - "deny.toml" + - ".github/workflows/deny.yml" push: branches: - main paths: - "Cargo.lock" + - "deny.toml" + - ".github/workflows/deny.yml" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} diff --git a/Cargo.lock b/Cargo.lock index efac304fc7e683..d32247277fa1a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anes" @@ -169,9 +169,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" dependencies = [ "memchr", "serde", @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.7.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cast" @@ -209,9 +209,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.30" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" dependencies = [ "shlex", ] @@ -325,9 +325,9 @@ checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -403,9 +403,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", "syn", @@ -532,12 +532,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -548,9 +548,9 @@ checksum = "bb10ed0f8a3dca52477be37ac0fb8f9d1fd4cd8d311b4484bdd45c1c56e0c9ec" [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "fixedbitset" @@ -763,9 +763,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hashlink" @@ -776,12 +776,6 @@ dependencies = [ "hashbrown 0.14.5", ] -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "httparse" version = "1.9.5" @@ -960,12 +954,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.2", "serde", ] @@ -1000,9 +994,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "javascript_globals" @@ -1067,15 +1061,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.164" +version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -1201,11 +1195,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi", "libc", "wasi", "windows-sys 0.52.0", @@ -1213,9 +1206,9 @@ dependencies = [ [[package]] name = "napi" -version = "3.0.0-alpha.14" +version = "3.0.0-alpha.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b27505341e98aa6126eb9f56a6ea5d8608959f19f124b9d1f1a694633641e5a" +checksum = "04aea9dbe75cd1a1abecf8a66fba1e694c12ce6e6e4e11824dc274e141a6c251" dependencies = [ "bitflags 2.6.0", "ctor", @@ -1232,9 +1225,9 @@ checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a" [[package]] name = "napi-derive" -version = "3.0.0-alpha.14" +version = "3.0.0-alpha.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c98bc3e1aef12e58d9ec48325790838a736f8428ae562716c4df1893b65be22" +checksum = "c12428d113f2b64cf827a144dddaf2df50c4d93d655d57d83745c2a281e6ec62" dependencies = [ "convert_case", "napi-derive-backend", @@ -1245,23 +1238,22 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "2.0.0-alpha.14" +version = "2.0.0-alpha.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7fbd5478c5ac30408e4599af0b532726477e347e45fc8b20d95ab1b589057" +checksum = "7a5122d26b6f849e524f1b92107364f2b4e9a2e8d41a77b3d6c5b3af75801c60" dependencies = [ "convert_case", "proc-macro2", "quote", - "regex", "semver", "syn", ] [[package]] name = "napi-sys" -version = "2.4.0" +version = "3.0.0-alpha.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3" +checksum = "ab9d950ea3a522a8cb9e9237ba7cf977eeca1fadaec182163be6b0feebfc7361" dependencies = [ "libloading", ] @@ -1382,9 +1374,8 @@ checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" [[package]] name = "oxc" -version = "0.38.0" +version = "0.39.0" dependencies = [ - "napi", "oxc_allocator", "oxc_ast", "oxc_cfg", @@ -1445,7 +1436,7 @@ dependencies = [ [[package]] name = "oxc_allocator" -version = "0.38.0" +version = "0.39.0" dependencies = [ "allocator-api2", "bumpalo", @@ -1455,7 +1446,7 @@ dependencies = [ [[package]] name = "oxc_ast" -version = "0.38.0" +version = "0.39.0" dependencies = [ "bitflags 2.6.0", "cow-utils", @@ -1473,7 +1464,7 @@ dependencies = [ [[package]] name = "oxc_ast_macros" -version = "0.38.0" +version = "0.39.0" dependencies = [ "proc-macro2", "quote", @@ -1521,7 +1512,7 @@ dependencies = [ [[package]] name = "oxc_cfg" -version = "0.38.0" +version = "0.39.0" dependencies = [ "bitflags 2.6.0", "itertools", @@ -1534,7 +1525,7 @@ dependencies = [ [[package]] name = "oxc_codegen" -version = "0.38.0" +version = "0.39.0" dependencies = [ "assert-unchecked", "base64", @@ -1595,14 +1586,14 @@ dependencies = [ [[package]] name = "oxc_data_structures" -version = "0.38.0" +version = "0.39.0" dependencies = [ "assert-unchecked", ] [[package]] name = "oxc_diagnostics" -version = "0.38.0" +version = "0.39.0" dependencies = [ "oxc-miette", "rustc-hash", @@ -1610,7 +1601,7 @@ dependencies = [ [[package]] name = "oxc_ecmascript" -version = "0.38.0" +version = "0.39.0" dependencies = [ "num-bigint", "num-traits", @@ -1621,23 +1612,23 @@ dependencies = [ [[package]] name = "oxc_estree" -version = "0.38.0" +version = "0.39.0" dependencies = [ "serde", ] [[package]] name = "oxc_index" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe15d5f5b96fa820c5ba2709eceabf395c4a93a578c792bdd65da6e88a77e14" +checksum = "f004e73d346bec03a428ca26cf2df245d08563f1d3268b7bcbd5554fc1db05c5" dependencies = [ "serde", ] [[package]] name = "oxc_isolated_declarations" -version = "0.38.0" +version = "0.39.0" dependencies = [ "bitflags 2.6.0", "insta", @@ -1737,7 +1728,7 @@ dependencies = [ [[package]] name = "oxc_mangler" -version = "0.38.0" +version = "0.39.0" dependencies = [ "itertools", "oxc_ast", @@ -1748,7 +1739,7 @@ dependencies = [ [[package]] name = "oxc_minifier" -version = "0.38.0" +version = "0.39.0" dependencies = [ "cow-utils", "insta", @@ -1794,23 +1785,9 @@ dependencies = [ "rustc-hash", ] -[[package]] -name = "oxc_napi" -version = "0.38.0" -dependencies = [ - "napi", - "napi-derive", - "oxc_isolated_declarations", - "oxc_sourcemap", - "oxc_span", - "oxc_syntax", - "oxc_transformer", - "rustc-hash", -] - [[package]] name = "oxc_parser" -version = "0.38.0" +version = "0.39.0" dependencies = [ "assert-unchecked", "bitflags 2.6.0", @@ -1839,7 +1816,7 @@ dependencies = [ "napi-build", "napi-derive", "oxc", - "oxc_napi", + "rustc-hash", "serde_json", ] @@ -1886,7 +1863,7 @@ dependencies = [ [[package]] name = "oxc_regular_expression" -version = "0.38.0" +version = "0.39.0" dependencies = [ "oxc_allocator", "oxc_ast_macros", @@ -1920,7 +1897,7 @@ dependencies = [ [[package]] name = "oxc_semantic" -version = "0.38.0" +version = "0.39.0" dependencies = [ "assert-unchecked", "indexmap", @@ -1945,15 +1922,16 @@ dependencies = [ [[package]] name = "oxc_sourcemap" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f15cc100c3a8070aa20420a6e1eafa612ebfc3d86db01cb92d928abf082d9696" +checksum = "2fe680bb40eebfdfde9d2be8a38cfbfb2e43113648dc5d215ba9930b1009af54" dependencies = [ "base64-simd", "cfg-if", "cow-utils", "napi", "napi-derive", + "rayon", "rustc-hash", "serde", "serde_json", @@ -1961,7 +1939,7 @@ dependencies = [ [[package]] name = "oxc_span" -version = "0.38.0" +version = "0.39.0" dependencies = [ "compact_str", "oxc-miette", @@ -1974,7 +1952,7 @@ dependencies = [ [[package]] name = "oxc_syntax" -version = "0.38.0" +version = "0.39.0" dependencies = [ "assert-unchecked", "bitflags 2.6.0", @@ -2032,19 +2010,19 @@ dependencies = [ [[package]] name = "oxc_transform_napi" -version = "0.38.0" +version = "0.39.0" dependencies = [ "napi", "napi-build", "napi-derive", "oxc", - "oxc_napi", "oxc_sourcemap", + "rustc-hash", ] [[package]] name = "oxc_transformer" -version = "0.38.0" +version = "0.39.0" dependencies = [ "base64", "compact_str", @@ -2077,7 +2055,7 @@ dependencies = [ [[package]] name = "oxc_traverse" -version = "0.38.0" +version = "0.39.0" dependencies = [ "compact_str", "itoa", @@ -2155,9 +2133,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.13" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", "thiserror", @@ -2166,9 +2144,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.13" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" dependencies = [ "pest", "pest_generator", @@ -2176,9 +2154,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.13" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" dependencies = [ "pest", "pest_meta", @@ -2189,9 +2167,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.13" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" dependencies = [ "once_cell", "pest", @@ -2278,9 +2256,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -2386,9 +2364,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2461,9 +2439,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustix" @@ -2480,9 +2458,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.14" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "log", "once_cell", @@ -2495,9 +2473,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" @@ -2766,9 +2744,9 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2815,9 +2793,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -2861,18 +2839,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -2921,9 +2899,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.41.1" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -3023,9 +3001,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -3034,9 +3012,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -3045,9 +3023,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -3066,9 +3044,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -3138,9 +3116,9 @@ checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-linebreak" @@ -3174,11 +3152,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.10.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +checksum = "b30e6f97efe1fa43535ee241ee76967d3ff6ff3953ebb430d8d55c5393029e7b" dependencies = [ "base64", + "litemap", "log", "once_cell", "rustls", @@ -3187,6 +3166,8 @@ dependencies = [ "serde_json", "url", "webpki-roots", + "yoke", + "zerofrom", ] [[package]] @@ -3249,9 +3230,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.96" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21d3b25c3ea1126a2ad5f4f9068483c2af1e64168f847abe863a526b8dbfe00b" +checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" dependencies = [ "cfg-if", "once_cell", @@ -3260,9 +3241,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.96" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52857d4c32e496dc6537646b5b117081e71fd2ff06de792e3577a150627db283" +checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" dependencies = [ "bumpalo", "log", @@ -3275,9 +3256,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.96" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "920b0ffe069571ebbfc9ddc0b36ba305ef65577c94b06262ed793716a1afd981" +checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3285,9 +3266,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.96" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf59002391099644be3524e23b781fa43d2be0c5aa0719a18c0731b9d195cab6" +checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" dependencies = [ "proc-macro2", "quote", @@ -3298,9 +3279,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.96" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5047c5392700766601942795a436d7d2599af60dcc3cc1248c9120bfb0827b0" +checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "web-sys" @@ -3314,9 +3295,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -3547,9 +3528,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", @@ -3588,9 +3569,9 @@ dependencies = [ [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index c3c248e1a4da99..8aee27568f7220 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,28 +78,27 @@ doc_lazy_continuation = "allow" # FIXME [workspace.dependencies] # publish = true -oxc = { version = "0.38.0", path = "crates/oxc" } -oxc_allocator = { version = "0.38.0", path = "crates/oxc_allocator" } -oxc_ast = { version = "0.38.0", path = "crates/oxc_ast" } -oxc_ast_macros = { version = "0.38.0", path = "crates/oxc_ast_macros" } -oxc_cfg = { version = "0.38.0", path = "crates/oxc_cfg" } -oxc_codegen = { version = "0.38.0", path = "crates/oxc_codegen" } -oxc_data_structures = { version = "0.38.0", path = "crates/oxc_data_structures" } -oxc_diagnostics = { version = "0.38.0", path = "crates/oxc_diagnostics" } -oxc_ecmascript = { version = "0.38.0", path = "crates/oxc_ecmascript" } -oxc_estree = { version = "0.38.0", path = "crates/oxc_estree" } -oxc_isolated_declarations = { version = "0.38.0", path = "crates/oxc_isolated_declarations" } -oxc_mangler = { version = "0.38.0", path = "crates/oxc_mangler" } -oxc_minifier = { version = "0.38.0", path = "crates/oxc_minifier" } -oxc_napi = { version = "0.38.0", path = "crates/oxc_napi" } -oxc_parser = { version = "0.38.0", path = "crates/oxc_parser" } -oxc_regular_expression = { version = "0.38.0", path = "crates/oxc_regular_expression" } -oxc_semantic = { version = "0.38.0", path = "crates/oxc_semantic" } -oxc_span = { version = "0.38.0", path = "crates/oxc_span" } -oxc_syntax = { version = "0.38.0", path = "crates/oxc_syntax" } -oxc_transform_napi = { version = "0.38.0", path = "napi/transform" } -oxc_transformer = { version = "0.38.0", path = "crates/oxc_transformer" } -oxc_traverse = { version = "0.38.0", path = "crates/oxc_traverse" } +oxc = { version = "0.39.0", path = "crates/oxc" } +oxc_allocator = { version = "0.39.0", path = "crates/oxc_allocator" } +oxc_ast = { version = "0.39.0", path = "crates/oxc_ast" } +oxc_ast_macros = { version = "0.39.0", path = "crates/oxc_ast_macros" } +oxc_cfg = { version = "0.39.0", path = "crates/oxc_cfg" } +oxc_codegen = { version = "0.39.0", path = "crates/oxc_codegen" } +oxc_data_structures = { version = "0.39.0", path = "crates/oxc_data_structures" } +oxc_diagnostics = { version = "0.39.0", path = "crates/oxc_diagnostics" } +oxc_ecmascript = { version = "0.39.0", path = "crates/oxc_ecmascript" } +oxc_estree = { version = "0.39.0", path = "crates/oxc_estree" } +oxc_isolated_declarations = { version = "0.39.0", path = "crates/oxc_isolated_declarations" } +oxc_mangler = { version = "0.39.0", path = "crates/oxc_mangler" } +oxc_minifier = { version = "0.39.0", path = "crates/oxc_minifier" } +oxc_parser = { version = "0.39.0", path = "crates/oxc_parser" } +oxc_regular_expression = { version = "0.39.0", path = "crates/oxc_regular_expression" } +oxc_semantic = { version = "0.39.0", path = "crates/oxc_semantic" } +oxc_span = { version = "0.39.0", path = "crates/oxc_span" } +oxc_syntax = { version = "0.39.0", path = "crates/oxc_syntax" } +oxc_transform_napi = { version = "0.39.0", path = "napi/transform" } +oxc_transformer = { version = "0.39.0", path = "crates/oxc_transformer" } +oxc_traverse = { version = "0.39.0", path = "crates/oxc_traverse" } # publish = false oxc_linter = { path = "crates/oxc_linter" } @@ -109,9 +108,9 @@ oxc_tasks_common = { path = "tasks/common" } oxc_tasks_transform_checker = { path = "tasks/transform_checker" } # Relaxed version so the user can decide which version to use. -napi = "3.0.0-alpha" +napi = "3.0.0-alpha.11" napi-build = "2.1.3" -napi-derive = "3.0.0-alpha" +napi-derive = "3.0.0-alpha.11" # Relaxed version so the user can decide which version to use. proc-macro2 = "1" @@ -120,12 +119,12 @@ syn = { version = "2", default-features = false } unicode-id-start = "1" oxc-browserslist = "1.1.0" -oxc_index = "1.0.0" -oxc_resolver = "2.0.0" -oxc_sourcemap = "1.0.2" +oxc_index = "1.0.1" +oxc_resolver = "2.1.1" +oxc_sourcemap = "1.0.3" aho-corasick = "1.1.3" -allocator-api2 = "0.2.18" +allocator-api2 = "0.2.21" assert-unchecked = "0.1.2" base64 = "0.22.1" bitflags = "2.6.0" @@ -138,21 +137,21 @@ convert_case = "0.6.0" cow-utils = "0.1.3" criterion2 = { version = "2.0.0", default-features = false } dashmap = "6.1.0" -encoding_rs = "0.8.34" +encoding_rs = "0.8.35" encoding_rs_io = "0.1.7" env_logger = { version = "0.11.5", default-features = false } fast-glob = "0.4.0" -flate2 = "1.0.34" +flate2 = "1.0.35" futures = "0.3.31" glob = "0.3.1" globset = "0.4.15" -handlebars = "6.1.0" +handlebars = "6.2.0" humansize = "2.1.3" ignore = "0.4.23" -indexmap = "2.6.0" -insta = "1.40.0" +indexmap = "2.7.0" +insta = "1.41.1" itertools = "0.13.0" -itoa = "1.0.11" +itoa = "1.0.14" jemallocator = "0.5.4" json-strip-comments = "1.0.4" language-tags = "0.3.2" @@ -169,10 +168,10 @@ num-traits = "0.2.19" petgraph = "0.6.5" phf = "0.11.2" pico-args = "0.5.0" -prettyplease = "0.2.22" +prettyplease = "0.2.25" project-root = "0.2.2" rayon = "1.10.0" -regex = "1.11.0" +regex = "1.11.1" ropey = "1.6.1" rust-lapper = "1.1.0" rustc-hash = "2.*" @@ -180,21 +179,21 @@ ryu-js = "1.0.1" saphyr = "0.0.3" schemars = "0.8.21" seq-macro = "0.3.5" -serde = "1.0.210" +serde = "1.0.215" serde-wasm-bindgen = "0.6.5" -serde_json = "1.0.128" +serde_json = "1.0.133" sha1 = "0.10.6" simdutf8 = { version = "0.1.5", features = ["aarch64_neon"] } similar = "2.6.0" -tempfile = "3.13.0" -tokio = "1.40.0" +tempfile = "3.14.0" +tokio = "1.42.0" tower-lsp = "0.20.0" -tracing-subscriber = "0.3.18" +tracing-subscriber = "0.3.19" tsify = "0.4.5" -ureq = { version = "2.10.1", default-features = false } -url = "2.5.2" +ureq = { version = "2.11.0", default-features = false } +url = "2.5.4" walkdir = "2.5.0" -wasm-bindgen = "0.2.96" +wasm-bindgen = "0.2.97" [workspace.metadata.cargo-shear] ignored = ["napi", "oxc_transform_napi", "prettyplease"] diff --git a/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-eslint.json b/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-eslint.json new file mode 100644 index 00000000000000..77325b77be8334 --- /dev/null +++ b/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-eslint.json @@ -0,0 +1,8 @@ +{ + "categories": { + "correctness": "off" + }, + "rules": { + "no-magic-numbers": "error" + } +} \ No newline at end of file diff --git a/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-typescript.json b/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-typescript.json new file mode 100644 index 00000000000000..cb7d205cf37c91 --- /dev/null +++ b/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-typescript.json @@ -0,0 +1,9 @@ +{ + "plugins": ["typescript"], + "categories": { + "correctness": "off" + }, + "rules": { + "typescript/no-magic-numbers": "error" + } +} \ No newline at end of file diff --git a/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/test.js b/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/test.js new file mode 100644 index 00000000000000..43867fe87af8c9 --- /dev/null +++ b/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/test.js @@ -0,0 +1,6 @@ + + +const price = 200; +const price_with_tax = price * 0.19; // taxes are expensive + +console.debug(price_with_tax); \ No newline at end of file diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index b81846ff7fa3e9..ffe842a8613742 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -819,4 +819,27 @@ mod test { assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 1); } + + #[test] + fn test_eslint_and_typescript_alias_rules() { + let args = &[ + "-c", + "fixtures/eslint_and_typescript_alias_rules/oxlint-eslint.json", + "fixtures/eslint_and_typescript_alias_rules/test.js", + ]; + let result = test(args); + assert_eq!(result.number_of_files, 1); + assert_eq!(result.number_of_warnings, 0); + assert_eq!(result.number_of_errors, 1); + + let args = &[ + "-c", + "fixtures/eslint_and_typescript_alias_rules/oxlint-typescript.json", + "fixtures/eslint_and_typescript_alias_rules/test.js", + ]; + let result = test(args); + assert_eq!(result.number_of_files, 1); + assert_eq!(result.number_of_warnings, 0); + assert_eq!(result.number_of_errors, 1); + } } diff --git a/crates/oxc/CHANGELOG.md b/crates/oxc/CHANGELOG.md index a4a5b0e267af4d..7f3afcb0f5916f 100644 --- a/crates/oxc/CHANGELOG.md +++ b/crates/oxc/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +- 8a788b8 parser: [**BREAKING**] Build `ModuleRecord` directly in parser (#7546) (Boshen) + +### Features + +- 40792b4 napi/parser: Change parse API to accept mandatory `filename` and optional `lang` (#7605) (Boshen) +- 7c62a33 napi/parser: Return esm info (#7602) (Boshen) +- 5864352 napi/transform: Add `TransformerOptions::assumptions` (#7601) (翠 / green) +- 771c698 oxc: Remove `oxc_napi` crate (#7634) (Boshen) +- bd977cf oxc: Add `oxc_napi` crate (#7612) (Boshen) + ## [0.38.0] - 2024-11-26 - 5d65656 oxc_index: [**BREAKING**] Move to own repo github.com/oxc-project/oxc-index-vec (#7464) (Boshen) diff --git a/crates/oxc/Cargo.toml b/crates/oxc/Cargo.toml index be2e600cb7a8a2..7926610e9fc67f 100644 --- a/crates/oxc/Cargo.toml +++ b/crates/oxc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true @@ -42,8 +42,6 @@ oxc_span = { workspace = true } oxc_syntax = { workspace = true } oxc_transformer = { workspace = true, optional = true } -napi = { workspace = true, optional = true, features = ["async"] } - [features] full = [ "codegen", diff --git a/crates/oxc_allocator/CHANGELOG.md b/crates/oxc_allocator/CHANGELOG.md index c68b355bdc5264..2e22a97bdc1134 100644 --- a/crates/oxc_allocator/CHANGELOG.md +++ b/crates/oxc_allocator/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +### Bug Fixes + +- 896ff86 minifier: Do not fold if statement block with lexical declaration (#7519) (Boshen) + ## [0.37.0] - 2024-11-21 ### Features diff --git a/crates/oxc_allocator/Cargo.toml b/crates/oxc_allocator/Cargo.toml index 2fbad6e6b47b88..cb4d1b07065d0f 100644 --- a/crates/oxc_allocator/Cargo.toml +++ b/crates/oxc_allocator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_allocator" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_ast/CHANGELOG.md b/crates/oxc_ast/CHANGELOG.md index b45582089d2891..47c88ea6a71866 100644 --- a/crates/oxc_ast/CHANGELOG.md +++ b/crates/oxc_ast/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +- b0e1c03 ast: [**BREAKING**] Add `StringLiteral::raw` field (#7393) (Boshen) + +### Features + + ## [0.38.0] - 2024-11-26 ### Features diff --git a/crates/oxc_ast/Cargo.toml b/crates/oxc_ast/Cargo.toml index cff291869b78f7..f8d733e77cb83b 100644 --- a/crates/oxc_ast/Cargo.toml +++ b/crates/oxc_ast/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_ast" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index 9a4f86e899f5e4..31968f3f4d4d45 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -502,6 +502,7 @@ impl<'a> ComputedMemberExpression<'a> { { Some(lit.quasis[0].value.raw.clone()) } + Expression::RegExpLiteral(lit) => Some(Atom::from(lit.raw)), _ => None, } } diff --git a/crates/oxc_ast_macros/Cargo.toml b/crates/oxc_ast_macros/Cargo.toml index ed131dc93b6c5e..a57fd4b7f960c5 100644 --- a/crates/oxc_ast_macros/Cargo.toml +++ b/crates/oxc_ast_macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_ast_macros" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_cfg/Cargo.toml b/crates/oxc_cfg/Cargo.toml index 4514743f7c7287..d78e306966a223 100644 --- a/crates/oxc_cfg/Cargo.toml +++ b/crates/oxc_cfg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_cfg" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_codegen/CHANGELOG.md b/crates/oxc_codegen/CHANGELOG.md index 33cc69a25f917d..da29ca12c61b8c 100644 --- a/crates/oxc_codegen/CHANGELOG.md +++ b/crates/oxc_codegen/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +### Bug Fixes + +- e787e9d codegen: Print parentheses correctly for ClassHeritage (#7637) (Boshen) + ## [0.38.0] - 2024-11-26 ### Bug Fixes diff --git a/crates/oxc_codegen/Cargo.toml b/crates/oxc_codegen/Cargo.toml index 70d05f89b2b0f7..df6c02b1af3042 100644 --- a/crates/oxc_codegen/Cargo.toml +++ b/crates/oxc_codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_codegen" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_codegen/examples/codegen.rs b/crates/oxc_codegen/examples/codegen.rs index 0249323ec1bcd0..5755d891ba311d 100644 --- a/crates/oxc_codegen/examples/codegen.rs +++ b/crates/oxc_codegen/examples/codegen.rs @@ -1,5 +1,5 @@ #![allow(clippy::print_stdout)] -use std::{env, path::Path}; +use std::path::Path; use oxc_allocator::Allocator; use oxc_codegen::{CodeGenerator, CodegenOptions}; @@ -8,14 +8,16 @@ use oxc_span::SourceType; use pico_args::Arguments; // Instruction: -// 1. create a `test.js` -// 2. run `cargo run -p oxc_codegen --example codegen` or `just example codegen` +// create a `test.js`, +// run `cargo run -p oxc_codegen --example codegen` +// or `cargo watch -x "run -p oxc_codegen --example codegen"` fn main() -> std::io::Result<()> { let mut args = Arguments::from_env(); - let name = env::args().nth(1).unwrap_or_else(|| "test.js".to_string()); + let twice = args.contains("--twice"); let minify = args.contains("--minify"); + let name = args.free_from_str().unwrap_or_else(|_| "test.js".to_string()); let path = Path::new(&name); let source_text = std::fs::read_to_string(path)?; diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 68beb55714ebd3..787c1532b51fa5 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -726,12 +726,14 @@ impl Gen for ImportDeclaration<'_> { fn gen(&self, p: &mut Codegen, ctx: Context) { p.add_source_mapping(self.span); p.print_indent(); - p.print_str("import "); + p.print_space_before_identifier(); + p.print_str("import"); if self.import_kind.is_type() { - p.print_str("type "); + p.print_str(" type"); } if let Some(specifiers) = &self.specifiers { if specifiers.is_empty() { + p.print_soft_space(); p.print_str("{}"); p.print_soft_space(); p.print_str("from"); @@ -755,23 +757,33 @@ impl Gen for ImportDeclaration<'_> { p.print_soft_space(); p.print_str("},"); in_block = false; - } else if index != 0 { + } else if index == 0 { + p.print_hard_space(); + } else { p.print_comma(); p.print_soft_space(); } spec.local.print(p, ctx); + if index == specifiers.len() - 1 { + p.print_hard_space(); + } } ImportDeclarationSpecifier::ImportNamespaceSpecifier(spec) => { if in_block { p.print_soft_space(); p.print_str("},"); in_block = false; - } else if index != 0 { + } else if index == 0 { + p.print_soft_space(); + } else { p.print_comma(); p.print_soft_space(); } - p.print_str("* as "); + p.print_ascii_byte(b'*'); + p.print_soft_space(); + p.print_str("as "); spec.local.print(p, ctx); + p.print_hard_space(); } ImportDeclarationSpecifier::ImportSpecifier(spec) => { if in_block { @@ -780,9 +792,9 @@ impl Gen for ImportDeclaration<'_> { } else { if index != 0 { p.print_comma(); - p.print_soft_space(); } in_block = true; + p.print_soft_space(); p.print_ascii_byte(b'{'); p.print_soft_space(); } @@ -804,12 +816,14 @@ impl Gen for ImportDeclaration<'_> { if in_block { p.print_soft_space(); p.print_ascii_byte(b'}'); + p.print_soft_space(); } - p.print_str(" from "); + p.print_str("from"); } + p.print_soft_space(); self.source.print(p, ctx); if let Some(with_clause) = &self.with_clause { - p.print_hard_space(); + p.print_soft_space(); with_clause.print(p, ctx); } p.add_source_mapping_end(self.span); @@ -822,9 +836,15 @@ impl Gen for WithClause<'_> { p.add_source_mapping(self.span); self.attributes_keyword.print(p, ctx); p.print_soft_space(); - p.print_block_start(self.span); - p.print_sequence(&self.with_entries, ctx); - p.print_block_end(self.span); + p.add_source_mapping(self.span); + p.print_ascii_byte(b'{'); + if !self.with_entries.is_empty() { + p.print_soft_space(); + p.print_list(&self.with_entries, ctx); + p.print_soft_space(); + } + p.add_source_mapping_end(self.span); + p.print_ascii_byte(b'}'); } } @@ -864,11 +884,12 @@ impl Gen for ExportNamedDeclaration<'_> { _ => {} }; } - p.print_str("export "); + p.print_str("export"); if self.export_kind.is_type() { - p.print_str("type "); + p.print_str(" type "); } if let Some(decl) = &self.declaration { + p.print_hard_space(); match decl { Declaration::VariableDeclaration(decl) => decl.print(p, ctx), Declaration::FunctionDeclaration(decl) => decl.print(p, ctx), @@ -891,6 +912,7 @@ impl Gen for ExportNamedDeclaration<'_> { p.needs_semicolon = false; } } else { + p.print_soft_space(); p.print_ascii_byte(b'{'); if !self.specifiers.is_empty() { p.print_soft_space(); @@ -969,18 +991,25 @@ impl Gen for ExportAllDeclaration<'_> { fn gen(&self, p: &mut Codegen, ctx: Context) { p.add_source_mapping(self.span); p.print_indent(); - p.print_str("export "); + p.print_str("export"); if self.export_kind.is_type() { - p.print_str("type "); + p.print_str(" type "); + } else { + p.print_soft_space(); } p.print_ascii_byte(b'*'); if let Some(exported) = &self.exported { - p.print_str(" as "); + p.print_soft_space(); + p.print_str("as "); exported.print(p, ctx); + p.print_hard_space(); + } else { + p.print_soft_space(); } - p.print_str(" from "); + p.print_str("from"); + p.print_soft_space(); self.source.print(p, ctx); if let Some(with_clause) = &self.with_clause { p.print_hard_space(); @@ -2185,7 +2214,7 @@ impl Gen for Class<'_> { } if let Some(super_class) = self.super_class.as_ref() { p.print_str(" extends "); - super_class.print_expr(p, Precedence::Call, Context::empty()); + super_class.print_expr(p, Precedence::Postfix, Context::empty()); if let Some(super_type_parameters) = &self.super_type_parameters { super_type_parameters.print(p, ctx); } diff --git a/crates/oxc_codegen/src/lib.rs b/crates/oxc_codegen/src/lib.rs index ae5a4ee1625b79..0b6f9054d107db 100644 --- a/crates/oxc_codegen/src/lib.rs +++ b/crates/oxc_codegen/src/lib.rs @@ -382,13 +382,6 @@ impl<'a> Codegen<'a> { self.print_ascii_byte(b'='); } - fn print_sequence(&mut self, items: &[T], ctx: Context) { - for item in items { - item.print(self, ctx); - self.print_comma(); - } - } - fn print_curly_braces(&mut self, span: Span, single_line: bool, op: F) { self.add_source_mapping(span); self.print_ascii_byte(b'{'); diff --git a/crates/oxc_codegen/tests/integration/esbuild.rs b/crates/oxc_codegen/tests/integration/esbuild.rs index 7dc42d806eceac..9ae5e68b17ecce 100644 --- a/crates/oxc_codegen/tests/integration/esbuild.rs +++ b/crates/oxc_codegen/tests/integration/esbuild.rs @@ -615,6 +615,8 @@ fn test_class() { test("class Foo { static foo() {} }", "class Foo {\n\tstatic foo() {}\n}\n"); test("class Foo { static get foo() {} }", "class Foo {\n\tstatic get foo() {}\n}\n"); test("class Foo { static set foo(x) {} }", "class Foo {\n\tstatic set foo(x) {}\n}\n"); + + test("class Foo extends foo() {}", "class Foo extends foo() {}\n"); } #[test] diff --git a/crates/oxc_codegen/tests/integration/snapshots/minify.snap b/crates/oxc_codegen/tests/integration/snapshots/minify.snap new file mode 100644 index 00000000000000..106343bda71388 --- /dev/null +++ b/crates/oxc_codegen/tests/integration/snapshots/minify.snap @@ -0,0 +1,199 @@ +--- +source: crates/oxc_codegen/tests/integration/main.rs +snapshot_kind: text +--- +########## 0 +let x: string = `\x01`; +---------- +let x:string=`\x01`; +########## 1 +function foo(x: T, y: string, ...restOfParams: Omit): T { + return x; +} +---------- +function foo(x:T,y:string,...restOfParams:Omit): T{return x} +########## 2 +let x: string[] = ['abc', 'def', 'ghi']; +---------- +let x:string[]=['abc','def','ghi']; +########## 3 +let x: Array = ['abc', 'def', 'ghi',]; +---------- +let x:Array=['abc','def','ghi']; +########## 4 +let x: [string, number] = ['abc', 123]; +---------- +let x:[string,number]=['abc',123]; +########## 5 +let x: string | number = 'abc'; +---------- +let x:string|number='abc'; +########## 6 +let x: string & number = 'abc'; +---------- +let x:string&number='abc'; +########## 7 +let x: typeof String = 'string'; +---------- +let x:typeof String='string'; +########## 8 +let x: keyof string = 'length'; +---------- +let x:keyof string='length'; +########## 9 +let x: keyof typeof String = 'length'; +---------- +let x:keyof typeof String='length'; +########## 10 +let x: string['length'] = 123; +---------- +let x:string['length']=123; +########## 11 +function isString(value: unknown): asserts value is string { + if (typeof value !== 'string') { + throw new Error('Not a string'); + } + } +---------- +function isString(value:unknown): asserts value is string{if(typeof value!=='string'){throw new Error('Not a string')}} +########## 12 +import type { Foo } from 'foo'; +---------- +import type{Foo}from'foo'; +########## 13 +import { Foo, type Bar } from 'foo'; +---------- +import{Foo,type Bar}from'foo'; +########## 14 +export { Foo, type Bar } from 'foo'; +---------- +export{Foo,type Bar}from'foo'; +########## 15 +type A = { [K in keyof T as K extends string ? B : K ]: T[K] } +---------- +type A={[K in keyof T as K extends string ? B : K]:T[K]}; +########## 16 +class A {readonly type = 'frame'} +---------- +class A{readonly type='frame'} +########## 17 +let foo: { (t: T): void } +---------- +let foo:{(t:T):void}; +########## 18 +let foo: { new (t: T): void } +---------- +let foo:{new (t:T):void}; +########## 19 +function (){} +---------- +function(){} +########## 20 +class A {m?(): void} +---------- +class A{m?():void;} +########## 21 +class A {constructor(public readonly a: number) {}} +---------- +class A{constructor(public readonly a:number){}} +########## 22 +abstract class A {private abstract static m() {}} +---------- +abstract class A{private abstract static m(){}} +########## 23 +abstract class A {private abstract static readonly prop: string} +---------- +abstract class A{private abstract static readonly prop:string} +########## 24 +a = x!; +---------- +a=x! ; +########## 25 +b = (x as y); +---------- +b=x as y; +########## 26 +c = foo; +---------- +c=foo ; +########## 27 +d = x satisfies y; +---------- +d=((x) satisfies y); +########## 28 +export @x declare abstract class C {} +---------- +export @x declare abstract class C{} +########## 29 +div`` +---------- +div``; +########## 30 +export type Component = Foo; +---------- +export type Component=Foo; +########## 31 + +export type Component< + Props = any, + RawBindings = any, + D = any, + C extends ComputedOptions = ComputedOptions, + M extends MethodOptions = MethodOptions, + E extends EmitsOptions | Record = {}, + S extends Record = any, +> = + | ConcreteComponent + | ComponentPublicInstanceConstructor + +---------- +export type Component = {},S extends Record = any>=ConcreteComponent|ComponentPublicInstanceConstructor; +########## 32 +(a || b) as any +---------- +(a||b) as any; +########## 33 +(a ** b) as any +---------- +(a**b) as any; +########## 34 +(function g() {}) as any +---------- +(function g(){}) as any; +########## 35 + +import defaultExport from "module-name"; +import * as name from "module-name"; +import { export1 } from "module-name"; +import { export1 as alias1 } from "module-name"; +import { default as alias } from "module-name"; +import { export1, export2 } from "module-name"; +import { export1, export2 as alias2, /* … */ } from "module-name"; +import { "string name" as alias } from "module-name"; +import defaultExport, { export1, /* … */ } from "module-name"; +import defaultExport, * as name from "module-name"; +import "module-name"; +import {} from 'mod'; + +export let name1, name2/*, … */; // also var +export const name3 = 1, name4 = 2/*, … */; // also var, let +export function functionName() { /* … */ } +export class ClassName { /* … */ } +export function* generatorFunctionName() { /* … */ } +export const { name5, name2: bar } = o; +export const [ name6, name7 ] = array; + +export { name8, /* …, */ name81 }; +export { variable1 as name9, variable2 as name10, /* …, */ name82 }; +export { variable1 as "string name" }; +export { name1 as default1 /*, … */ }; + +export * from "module-name"; +export * as name11 from "module-name"; +export { name12, /* …, */ nameN } from "module-name"; +export { import1 as name13, import2 as name14, /* …, */ name15 } from "module-name"; +export { default, /* …, */ } from "module-name"; +export { default as name16 } from "module-name"; + +---------- +import defaultExport from'module-name';import*as name from'module-name';import{export1}from'module-name';import{export1 as alias1}from'module-name';import{default as alias}from'module-name';import{export1,export2}from'module-name';import{export1,export2 as alias2}from'module-name';import{'string name' as alias}from'module-name';import defaultExport,{export1}from'module-name';import defaultExport,*as name from'module-name';import'module-name';import{}from"mod";export let name1,name2;export const name3=1,name4=2;export function functionName(){}export class ClassName{}export function*generatorFunctionName(){}export const {name5,name2:bar}=o;export const [name6,name7]=array;export{name8,name81};export{variable1 as name9,variable2 as name10,name82};export{variable1 as 'string name'};export{name1 as default1};export*from'module-name';export*as name11 from'module-name';export{name12,nameN}from'module-name';export{import1 as name13,import2 as name14,name15}from'module-name';export{default}from'module-name';export{default as name16}from'module-name'; diff --git a/crates/oxc_codegen/tests/integration/snapshots/ts.snap b/crates/oxc_codegen/tests/integration/snapshots/ts.snap index 727c54bba09520..87588c2a2f6f77 100644 --- a/crates/oxc_codegen/tests/integration/snapshots/ts.snap +++ b/crates/oxc_codegen/tests/integration/snapshots/ts.snap @@ -1,5 +1,6 @@ --- source: crates/oxc_codegen/tests/integration/main.rs +snapshot_kind: text --- ########## 0 let x: string = `\x01`; @@ -225,3 +226,69 @@ export type Component< (function g() {}) as any ---------- (function g() {}) as any; + +########## 35 + +import defaultExport from "module-name"; +import * as name from "module-name"; +import { export1 } from "module-name"; +import { export1 as alias1 } from "module-name"; +import { default as alias } from "module-name"; +import { export1, export2 } from "module-name"; +import { export1, export2 as alias2, /* … */ } from "module-name"; +import { "string name" as alias } from "module-name"; +import defaultExport, { export1, /* … */ } from "module-name"; +import defaultExport, * as name from "module-name"; +import "module-name"; +import {} from 'mod'; + +export let name1, name2/*, … */; // also var +export const name3 = 1, name4 = 2/*, … */; // also var, let +export function functionName() { /* … */ } +export class ClassName { /* … */ } +export function* generatorFunctionName() { /* … */ } +export const { name5, name2: bar } = o; +export const [ name6, name7 ] = array; + +export { name8, /* …, */ name81 }; +export { variable1 as name9, variable2 as name10, /* …, */ name82 }; +export { variable1 as "string name" }; +export { name1 as default1 /*, … */ }; + +export * from "module-name"; +export * as name11 from "module-name"; +export { name12, /* …, */ nameN } from "module-name"; +export { import1 as name13, import2 as name14, /* …, */ name15 } from "module-name"; +export { default, /* …, */ } from "module-name"; +export { default as name16 } from "module-name"; + +---------- +import defaultExport from 'module-name'; +import * as name from 'module-name'; +import { export1 } from 'module-name'; +import { export1 as alias1 } from 'module-name'; +import { default as alias } from 'module-name'; +import { export1, export2 } from 'module-name'; +import { export1, export2 as alias2 } from 'module-name'; +import { 'string name' as alias } from 'module-name'; +import defaultExport, { export1 } from 'module-name'; +import defaultExport, * as name from 'module-name'; +import 'module-name'; +import {} from "mod"; +export let name1, name2; +export const name3 = 1, name4 = 2; +export function functionName() {} +export class ClassName {} +export function* generatorFunctionName() {} +export const { name5, name2: bar } = o; +export const [name6, name7] = array; +export { name8, name81 }; +export { variable1 as name9, variable2 as name10, name82 }; +export { variable1 as 'string name' }; +export { name1 as default1 }; +export * from 'module-name'; +export * as name11 from 'module-name'; +export { name12, nameN } from 'module-name'; +export { import1 as name13, import2 as name14, name15 } from 'module-name'; +export { default } from 'module-name'; +export { default as name16 } from 'module-name'; diff --git a/crates/oxc_codegen/tests/integration/ts.rs b/crates/oxc_codegen/tests/integration/ts.rs index 69c805f7e1b7f8..b036da602d720a 100644 --- a/crates/oxc_codegen/tests/integration/ts.rs +++ b/crates/oxc_codegen/tests/integration/ts.rs @@ -1,4 +1,5 @@ -use crate::snapshot; +use crate::{snapshot, snapshot_options}; +use oxc_codegen::CodegenOptions; #[test] fn ts() { @@ -54,7 +55,46 @@ export type Component< "(a || b) as any", "(a ** b) as any", "(function g() {}) as any", +r#" +import defaultExport from "module-name"; +import * as name from "module-name"; +import { export1 } from "module-name"; +import { export1 as alias1 } from "module-name"; +import { default as alias } from "module-name"; +import { export1, export2 } from "module-name"; +import { export1, export2 as alias2, /* … */ } from "module-name"; +import { "string name" as alias } from "module-name"; +import defaultExport, { export1, /* … */ } from "module-name"; +import defaultExport, * as name from "module-name"; +import "module-name"; +import {} from 'mod'; + +export let name1, name2/*, … */; // also var +export const name3 = 1, name4 = 2/*, … */; // also var, let +export function functionName() { /* … */ } +export class ClassName { /* … */ } +export function* generatorFunctionName() { /* … */ } +export const { name5, name2: bar } = o; +export const [ name6, name7 ] = array; + +export { name8, /* …, */ name81 }; +export { variable1 as name9, variable2 as name10, /* …, */ name82 }; +export { variable1 as "string name" }; +export { name1 as default1 /*, … */ }; + +export * from "module-name"; +export * as name11 from "module-name"; +export { name12, /* …, */ nameN } from "module-name"; +export { import1 as name13, import2 as name14, /* …, */ name15 } from "module-name"; +export { default, /* …, */ } from "module-name"; +export { default as name16 } from "module-name"; +"# ]; snapshot("ts", &cases); + snapshot_options( + "minify", + &cases, + &CodegenOptions { minify: true, ..CodegenOptions::default() }, + ); } diff --git a/crates/oxc_codegen/tests/integration/unit.rs b/crates/oxc_codegen/tests/integration/unit.rs index dae518dd5bcab2..cb38a9608cae9e 100644 --- a/crates/oxc_codegen/tests/integration/unit.rs +++ b/crates/oxc_codegen/tests/integration/unit.rs @@ -3,9 +3,9 @@ use crate::tester::{test, test_minify, test_without_source}; #[test] fn module_decl() { test("export * as foo from 'foo'", "export * as foo from \"foo\";\n"); - test("import x from './foo.js' with {}", "import x from \"./foo.js\" with {\n};\n"); - test("import {} from './foo.js' with {}", "import {} from \"./foo.js\" with {\n};\n"); - test("export * from './foo.js' with {}", "export * from \"./foo.js\" with {\n};\n"); + test("import x from './foo.js' with {}", "import x from \"./foo.js\" with {};\n"); + test("import {} from './foo.js' with {}", "import {} from \"./foo.js\" with {};\n"); + test("export * from './foo.js' with {}", "export * from \"./foo.js\" with {};\n"); } #[test] diff --git a/crates/oxc_data_structures/CHANGELOG.md b/crates/oxc_data_structures/CHANGELOG.md index 2e8900d203fd22..09846e6ea90b4d 100644 --- a/crates/oxc_data_structures/CHANGELOG.md +++ b/crates/oxc_data_structures/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +### Features + +- defaf4b data_structures: Add `SparseStack::last_mut` method (#7528) (overlookmotel) + ## [0.37.0] - 2024-11-21 ### Features diff --git a/crates/oxc_data_structures/Cargo.toml b/crates/oxc_data_structures/Cargo.toml index 282025ba5f22ef..6f9bc0af26ab30 100644 --- a/crates/oxc_data_structures/Cargo.toml +++ b/crates/oxc_data_structures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_data_structures" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_diagnostics/Cargo.toml b/crates/oxc_diagnostics/Cargo.toml index 49e79fa1350da2..e5aa770d9bdf1a 100644 --- a/crates/oxc_diagnostics/Cargo.toml +++ b/crates/oxc_diagnostics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_diagnostics" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_ecmascript/CHANGELOG.md b/crates/oxc_ecmascript/CHANGELOG.md index 25eb514511b6b1..8257e23c0bd52d 100644 --- a/crates/oxc_ecmascript/CHANGELOG.md +++ b/crates/oxc_ecmascript/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +- b0e1c03 ast: [**BREAKING**] Add `StringLiteral::raw` field (#7393) (Boshen) + +### Features + +- 24189f2 ecma: Implement array join method (#6936) (7086cmd) + +### Testing + +- 9d6e14b ecmascript: Move tests to `oxc_minifier` due to cyclic dependency with `oxc_parser` (#7542) (Boshen) + ## [0.35.0] - 2024-11-04 ### Bug Fixes diff --git a/crates/oxc_ecmascript/Cargo.toml b/crates/oxc_ecmascript/Cargo.toml index 65e59de4bcfc44..30c8a5d946ca3e 100644 --- a/crates/oxc_ecmascript/Cargo.toml +++ b/crates/oxc_ecmascript/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_ecmascript" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_estree/Cargo.toml b/crates/oxc_estree/Cargo.toml index 0ee08d35eb02a5..91db22e0758483 100644 --- a/crates/oxc_estree/Cargo.toml +++ b/crates/oxc_estree/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_estree" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_isolated_declarations/CHANGELOG.md b/crates/oxc_isolated_declarations/CHANGELOG.md index 5351793e6751bc..45805b1acec645 100644 --- a/crates/oxc_isolated_declarations/CHANGELOG.md +++ b/crates/oxc_isolated_declarations/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +- b0e1c03 ast: [**BREAKING**] Add `StringLiteral::raw` field (#7393) (Boshen) + +### Features + + ## [0.38.0] - 2024-11-26 ### Refactor diff --git a/crates/oxc_isolated_declarations/Cargo.toml b/crates/oxc_isolated_declarations/Cargo.toml index 918b406b2ecdca..1a6272e429376f 100644 --- a/crates/oxc_isolated_declarations/Cargo.toml +++ b/crates/oxc_isolated_declarations/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_isolated_declarations" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_linter/src/config/rules.rs b/crates/oxc_linter/src/config/rules.rs index cdd1c68c28556a..a4084f93d81c6e 100644 --- a/crates/oxc_linter/src/config/rules.rs +++ b/crates/oxc_linter/src/config/rules.rs @@ -11,7 +11,7 @@ use serde::{ use crate::{ rules::{RuleEnum, RULES}, - utils::is_jest_rule_adapted_to_vitest, + utils::{is_eslint_rule_adapted_to_typescript, is_jest_rule_adapted_to_vitest}, AllowWarnDeny, RuleWithSeverity, }; @@ -155,6 +155,10 @@ fn transform_rule_and_plugin_name<'a>( return (rule_name, "jest"); } + if plugin_name == "typescript" && is_eslint_rule_adapted_to_typescript(rule_name) { + return (rule_name, "eslint"); + } + (rule_name, plugin_name) } diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 6fbabcb0566f05..11cdfc70de79ab 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -123,6 +123,7 @@ mod eslint { pub mod no_unsafe_finally; pub mod no_unsafe_negation; pub mod no_unsafe_optional_chaining; + pub mod no_unused_expressions; pub mod no_unused_labels; pub mod no_unused_private_class_members; pub mod no_unused_vars; @@ -148,6 +149,7 @@ mod eslint { pub mod unicode_bom; pub mod use_isnan; pub mod valid_typeof; + pub mod yoda; } mod typescript { @@ -181,7 +183,7 @@ mod typescript { pub mod no_unnecessary_type_constraint; pub mod no_unsafe_declaration_merging; pub mod no_unsafe_function_type; - pub mod no_unused_expressions; + pub mod no_useless_empty_export; pub mod no_var_requires; pub mod no_wrapper_object_types; @@ -562,12 +564,12 @@ oxc_macros::declare_all_lint_rules! { eslint::no_dupe_else_if, eslint::no_dupe_keys, eslint::no_duplicate_case, - eslint::no_empty, + eslint::no_else_return, eslint::no_empty_character_class, eslint::no_empty_function, eslint::no_empty_pattern, eslint::no_empty_static_block, - eslint::no_else_return, + eslint::no_empty, eslint::no_eq_null, eslint::no_eval, eslint::no_ex_assign, @@ -585,10 +587,10 @@ oxc_macros::declare_all_lint_rules! { eslint::no_loss_of_precision, eslint::no_magic_numbers, eslint::no_multi_str, - eslint::no_new, eslint::no_new_func, eslint::no_new_native_nonconstructor, eslint::no_new_wrappers, + eslint::no_new, eslint::no_nonoctal_decimal_escape, eslint::no_obj_calls, eslint::no_plusplus, @@ -615,6 +617,7 @@ oxc_macros::declare_all_lint_rules! { eslint::no_unsafe_finally, eslint::no_unsafe_negation, eslint::no_unsafe_optional_chaining, + eslint::no_unused_expressions, eslint::no_unused_labels, eslint::no_unused_private_class_members, eslint::no_unused_vars, @@ -640,6 +643,7 @@ oxc_macros::declare_all_lint_rules! { eslint::unicode_bom, eslint::use_isnan, eslint::valid_typeof, + eslint::yoda, import::default, import::export, import::first, @@ -857,7 +861,6 @@ oxc_macros::declare_all_lint_rules! { typescript::consistent_type_definitions, typescript::consistent_type_imports, typescript::explicit_function_return_type, - typescript::no_unused_expressions, typescript::no_inferrable_types, typescript::no_confusing_non_null_assertion, typescript::no_duplicate_enum_values, diff --git a/crates/oxc_linter/src/rules/typescript/no_unused_expressions.rs b/crates/oxc_linter/src/rules/eslint/no_unused_expressions.rs similarity index 100% rename from crates/oxc_linter/src/rules/typescript/no_unused_expressions.rs rename to crates/oxc_linter/src/rules/eslint/no_unused_expressions.rs diff --git a/crates/oxc_linter/src/rules/eslint/yoda.rs b/crates/oxc_linter/src/rules/eslint/yoda.rs new file mode 100644 index 00000000000000..cac2bf819d052a --- /dev/null +++ b/crates/oxc_linter/src/rules/eslint/yoda.rs @@ -0,0 +1,1161 @@ +use oxc_ast::{ + ast::{ + BinaryExpression, BinaryOperator, Expression, LogicalExpression, LogicalOperator, + UnaryOperator, + }, + AstKind, +}; +use oxc_diagnostics::OxcDiagnostic; +use oxc_ecmascript::ToBigInt; +use oxc_macros::declare_oxc_lint; +use oxc_span::{GetSpan, Span}; +use regex::Regex; + +use crate::{context::LintContext, rule::Rule, utils::is_same_reference, AstNode}; + +fn yoda_diagnostic(span: Span, never: bool, operator: &str) -> OxcDiagnostic { + let expected_side = if never { "right" } else { "left" }; + OxcDiagnostic::warn("Require or disallow \"Yoda\" conditions") + .with_help(format!("Expected literal to be on the {expected_side} side of {operator}.")) + .with_label(span) +} + +#[derive(Debug, Default, Clone)] +pub struct Yoda { + never: bool, + except_range: bool, + only_equality: bool, +} + +declare_oxc_lint!( + /// ## What it does + /// + /// Require or disallow "Yoda" conditions. + /// This rule aims to enforce consistent style of conditions which compare a variable to a literal value. + /// + /// ## Why is this bad? + /// + /// Yoda conditions are so named because the literal value of the condition comes first while the variable comes second. For example, the following is a Yoda condition: + /// ```js + /// if ("red" === color) { + // // ... + /// } + /// ``` + /// This is called a Yoda condition because it reads as, "if red equals the color", similar to the way the Star Wars character Yoda speaks. Compare to the other way of arranging the operands: + /// ```js + /// if (color === "red") { + /// // ... + /// } + /// ``` + /// This typically reads, "if the color equals red", which is arguably a more natural way to describe the comparison. + /// Proponents of Yoda conditions highlight that it is impossible to mistakenly use `=` instead of `==` because you cannot assign to a literal value. Doing so will cause a syntax error and you will be informed of the mistake early on. This practice was therefore very common in early programming where tools were not yet available. + /// Opponents of Yoda conditions point out that tooling has made us better programmers because tools will catch the mistaken use of `=` instead of `==` (ESLint will catch this for you). Therefore, they argue, the utility of the pattern doesn't outweigh the readability hit the code takes while using Yoda conditions. + /// + /// ## Options + /// This rule can take a string option: + /// * If it is the default `"never"`, then comparisons must never be Yoda conditions. + /// * If it is `"always"`, then the literal value must always come first. + /// The default `"never"` option can have exception options in an object literal: + /// * If the `"exceptRange"` property is `true`, the rule *allows* yoda conditions in range comparisons which are wrapped directly in parentheses, including the parentheses of an `if` or `while` condition. The default value is `false`. A *range* comparison tests whether a variable is inside or outside the range between two literal values. + /// * If the `"onlyEquality"` property is `true`, the rule reports yoda conditions *only* for the equality operators `==` and `===`. The default value is `false`. + /// The `onlyEquality` option allows a superset of the exceptions which `exceptRange` allows, thus both options are not useful together. + /// + /// ### never + /// Examples of **incorrect** code for the default `"never"` option: + /// ```js + /// if ("red" === color) { + /// // ... + /// } + /// if (`red` === color) { + /// // ... + /// } + /// if (`red` === `${color}`) { + /// // ... + /// } + /// + /// if (true == flag) { + /// // ... + /// } + /// + // if (5 > count) { + // // ... + // } + /// + // if (-1 < str.indexOf(substr)) { + // // ... + // } + /// + /// if (0 <= x && x < 1) { + /// // ... + /// } + /// ``` + /// + /// Examples of **correct** code for the default `"never"` option: + /// + /// ```js + /// if (5 & value) { + /// // ... + /// } + /// + /// if (value === "red") { + /// // ... + /// } + /// + /// if (value === `red`) { + /// // ... + /// } + /// + /// if (`${value}` === `red`) { + /// + /// } + /// ``` + /// + /// ### exceptRange + /// + /// Examples of **correct** code for the `"never", { "exceptRange": true }` options: + /// + /// ```js + /// function isReddish(color) { + /// return (color.hue < 60 || 300 < color.hue); + /// } + /// + /// if (x < -1 || 1 < x) { + /// // ... + /// } + /// + /// if (count < 10 && (0 <= rand && rand < 1)) { + /// // ... + /// } + /// + /// if (`blue` < x && x < `green`) { + /// // ... + /// } + /// + /// function howLong(arr) { + /// return (0 <= arr.length && arr.length < 10) ? "short" : "long"; + /// } + /// ``` + /// + /// ### onlyEquality + /// + /// Examples of **correct** code for the `"never", { "onlyEquality": true }` options: + /// + /// ```js + /// if (x < -1 || 9 < x) { + /// } + /// + /// if (x !== 'foo' && 'bar' != x) { + /// } + /// + /// if (x !== `foo` && `bar` != x) { + /// } + /// ``` + /// + /// ### always + /// + /// Examples of **incorrect** code for the `"always"` option: + /// + /// ```js + /// if (color == "blue") { + /// // ... + /// } + /// + /// if (color == `blue`) { + /// // ... + /// } + /// ``` + /// + /// Examples of **correct** code for the `"always"` option: + /// + /// ```js + /// if ("blue" == value) { + /// // ... + /// } + /// + /// if (`blue` == value) { + /// // ... + /// } + /// + /// if (`blue` == `${value}`) { + /// // ... + /// } + /// + /// if (-1 < str.indexOf(substr)) { + /// // ... + /// } + /// ``` + Yoda, + style, + fix +); + +impl Rule for Yoda { + fn from_configuration(value: serde_json::Value) -> Self { + let mut config = Self { never: true, except_range: false, only_equality: false }; + + let Some(arr) = value.as_array() else { + return config; + }; + + let option1 = arr.first().and_then(serde_json::Value::as_str); + let option2 = arr.get(1).and_then(serde_json::Value::as_object); + + if option1 == Some("always") { + config.never = false; + } + + if let Some(option2) = option2 { + if option2.get("exceptRange").and_then(serde_json::Value::as_bool) == Some(true) { + config.except_range = true; + } + if option2.get("onlyEquality").and_then(serde_json::Value::as_bool) == Some(true) { + config.only_equality = true; + } + } + + config + } + + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + let AstKind::BinaryExpression(expr) = node.kind() else { + return; + }; + + if let Some(parent_node) = ctx.nodes().parent_node(node.id()) { + if let AstKind::LogicalExpression(logical_expr) = parent_node.kind() { + let parent_logical_expr = ctx.nodes().parent_node(parent_node.id()); + + if self.except_range + && parent_logical_expr.is_some_and(|e| is_parenthesized(e)) + && is_range(logical_expr, ctx) + { + return; + } + } + }; + + if !expr.operator.is_equality() && !expr.operator.is_compare() { + return; + } + + if self.only_equality && !is_equality(expr) { + return; + } + + // never + if self.never && is_yoda(expr) { + do_diagnostic_with_fix(expr, ctx, self.never); + } + + // always + if !self.never && is_not_yoda(expr) { + do_diagnostic_with_fix(expr, ctx, self.never); + } + } +} + +fn is_yoda(expr: &BinaryExpression) -> bool { + is_literal_or_simple_template_literal(expr.left.get_inner_expression()) + && !is_literal_or_simple_template_literal(expr.right.get_inner_expression()) +} + +fn is_not_yoda(expr: &BinaryExpression) -> bool { + !is_literal_or_simple_template_literal(expr.left.get_inner_expression()) + && is_literal_or_simple_template_literal(expr.right.get_inner_expression()) +} + +fn do_diagnostic_with_fix(expr: &BinaryExpression, ctx: &LintContext, never: bool) { + ctx.diagnostic_with_fix(yoda_diagnostic(expr.span, never, expr.operator.as_str()), |fix| { + let flipped_operator = flip_operator(expr.operator); + + let left_str = ctx.source_range(expr.left.span()); + let right_str = ctx.source_range(expr.right.span()); + let flipped_operator_str = flipped_operator.as_str(); + + let operator_str = expr.operator.as_str(); + let source_str = ctx.source_range(expr.span); + let regex = Regex::new(operator_str).unwrap(); + let mut operator_position_start: u32 = 0; + let mut operator_position_end: u32 = 0; + for mat in regex.find_iter(source_str) { + let start = u32::try_from(mat.start()).unwrap(); + let end = u32::try_from(mat.end()).unwrap(); + + let is_inside_comments = ctx.comments().iter().any(|c| { + c.span.start <= start + expr.span.start && end + expr.span.start <= c.span.end + }); + + if !is_inside_comments { + operator_position_start = start + expr.span.start; + operator_position_end = end + expr.span.start; + break; + } + } + + let str_between_left_and_operator = + ctx.source_range(Span::new(expr.left.span().end, operator_position_start)); + let str_between_operator_and_right = + ctx.source_range(Span::new(operator_position_end, expr.right.span().start)); + + let left_start = expr.left.span().start; + let left_prev_token = if left_start > 0 && (expr.right.is_literal() || expr.right.is_identifier_reference() ) { + let token = ctx.source_range(Span::new(left_start - 1, left_start)); + match_token(token) + } else { + "" + }; + + let right_end = expr.right.span().end; + let source_size = u32::try_from(ctx.source_text().len()).unwrap(); + let right_next_token = if right_end < source_size && (expr.left.is_literal() || expr.left.is_identifier_reference()) { + let token = ctx.source_range(Span::new(right_end, right_end + 1)); + match_token(token) + } else { + "" + }; + + let replacement = format!( + "{left_prev_token}{right_str}{str_between_left_and_operator}{flipped_operator_str}{str_between_operator_and_right}{left_str}{right_next_token}" + ); + + fix.replace(expr.span, replacement) + }); +} + +fn match_token(token: &str) -> &str { + match token { + " " | "(" | ")" | "/" | "=" | ";" => "", + _ => " ", + } +} + +fn flip_operator(operator: BinaryOperator) -> BinaryOperator { + match operator { + BinaryOperator::LessThan => BinaryOperator::GreaterThan, + BinaryOperator::LessEqualThan => BinaryOperator::GreaterEqualThan, + BinaryOperator::GreaterThan => BinaryOperator::LessThan, + BinaryOperator::GreaterEqualThan => BinaryOperator::LessEqualThan, + _ => operator, + } +} + +fn is_equality(expr: &BinaryExpression) -> bool { + expr.operator == BinaryOperator::Equality || expr.operator == BinaryOperator::StrictEquality +} + +fn is_parenthesized(parent_logical_expr: &AstNode) -> bool { + let kind = parent_logical_expr.kind(); + + matches!(kind, AstKind::ParenthesizedExpression(_)) + || matches!(kind, AstKind::IfStatement(_)) + || matches!(kind, AstKind::WhileStatement(_)) + || matches!(kind, AstKind::DoWhileStatement(_)) +} + +fn is_range(expr: &LogicalExpression, ctx: &LintContext) -> bool { + let Expression::BinaryExpression(left) = &expr.left else { + return false; + }; + let Expression::BinaryExpression(right) = &expr.right else { + return false; + }; + + match left.operator { + BinaryOperator::LessThan | BinaryOperator::LessEqualThan => {} + _ => return false, + } + + match right.operator { + BinaryOperator::LessThan | BinaryOperator::LessEqualThan => {} + _ => return false, + } + + if expr.operator == LogicalOperator::And { + if !is_same_reference(&left.right, &right.left, ctx) { + return false; + } + + let left_left = &left.left; + let right_right = &right.right; + + let is_left_left_target_literal = is_target_literal(left_left); + let is_right_right_target_literal = is_target_literal(right_right); + + if !is_left_left_target_literal && !is_right_right_target_literal { + return false; + } + + if !is_left_left_target_literal || !is_right_right_target_literal { + return true; + } + + if let (Some(left_left), Some(right_right)) = + (get_string_literal(left_left), get_string_literal(right_right)) + { + return left_left <= right_right; + } + + if let (Some(left_left), Some(right_right)) = + (get_number(left_left), get_number(right_right)) + { + return left_left <= right_right; + } + + return false; + } + + if expr.operator == LogicalOperator::Or { + if !is_same_reference(&left.left, &right.right, ctx) { + return false; + } + + let left_right = &left.right; + let right_left = &right.left; + + let is_left_right_target_literal = is_target_literal(left_right); + let is_right_left_target_literal = is_target_literal(right_left); + + if !is_left_right_target_literal && !is_right_left_target_literal { + return false; + } + + if !is_left_right_target_literal || !is_right_left_target_literal { + return true; + } + + if let (Some(left_right), Some(right_left)) = + (get_string_literal(left_right), get_string_literal(right_left)) + { + return left_right <= right_left; + } + + if let (Some(left_right), Some(right_left)) = + (get_number(left_right), get_number(right_left)) + { + return left_right <= right_left; + } + + return false; + } + + false +} + +fn is_simple_template_literal(expr: &Expression) -> bool { + match expr { + Expression::TemplateLiteral(template) => template.quasis.len() == 1, + _ => false, + } +} + +fn is_literal_or_simple_template_literal(expr: &Expression) -> bool { + expr.is_literal() || is_number(expr) || is_simple_template_literal(expr) +} + +fn is_target_literal(expr: &Expression) -> bool { + get_string_literal(expr).is_some() || is_number(expr) +} + +fn get_string_literal<'a>(expr: &'a Expression) -> Option<&'a str> { + match expr { + Expression::StringLiteral(string) => Some(&string.value), + Expression::TemplateLiteral(template) => { + if template.quasis.len() != 1 { + return None; + } + + template.quasis.first().map(|e| e.value.raw.as_str()) + } + _ => None, + } +} + +fn is_number(expr: &Expression) -> bool { + match expr { + Expression::NumericLiteral(_) | Expression::BigIntLiteral(_) => true, + Expression::UnaryExpression(unary) => { + if unary.operator == UnaryOperator::UnaryNegation { + return is_number(&unary.argument); + } + false + } + _ => false, + } +} + +fn get_number(expr: &Expression) -> Option { + match expr { + Expression::NumericLiteral(numeric) => Some(numeric.value), + Expression::BigIntLiteral(big_int) => { + let big_int = big_int.to_big_int()?; + + let Ok(big_int) = big_int.to_string().parse::() else { + return None; + }; + + Some(big_int) + } + Expression::UnaryExpression(unary) => { + if unary.operator == UnaryOperator::UnaryNegation { + return get_number(&unary.argument).map(|num| -num); + } + + None + } + _ => None, + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + (r#"if (value === "red") {}"#, Some(serde_json::json!(["never"]))), + ("if (value === value) {}", Some(serde_json::json!(["never"]))), + ("if (value != 5) {}", Some(serde_json::json!(["never"]))), + ("if (5 & foo) {}", Some(serde_json::json!(["never"]))), + ("if (5 === 4) {}", Some(serde_json::json!(["never"]))), + ("if (value === `red`) {}", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + ("if (`red` === `red`) {}", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + ("if (`${foo}` === `red`) {}", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + (r#"if (`${""}` === `red`) {}"#, Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + (r#"if (`${"red"}` === foo) {}"#, Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + ("if (b > `a` && b > `a`) {}", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + (r#"if (`b` > `a` && "b" > "a") {}"#, Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + (r#"if ("blue" === value) {}"#, Some(serde_json::json!(["always"]))), + ("if (value === value) {}", Some(serde_json::json!(["always"]))), + ("if (4 != value) {}", Some(serde_json::json!(["always"]))), + ("if (foo & 4) {}", Some(serde_json::json!(["always"]))), + ("if (5 === 4) {}", Some(serde_json::json!(["always"]))), + ("if (`red` === value) {}", Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + ("if (`red` === `red`) {}", Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + ("if (`red` === `${foo}`) {}", Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + (r#"if (`red` === `${""}`) {}"#, Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + (r#"if (foo === `${"red"}`) {}"#, Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + ("if (`a` > b && `a` > b) {}", Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + (r#"if (`b` > `a` && "b" > "a") {}"#, Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + ( + r#"if ("a" < x && x < MAX ) {}"#, + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ("if (1 < x && x < MAX ) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ( + "if ('a' < x && x < MAX ) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (x < `x` || `x` <= x) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ("if (0 < x && x <= 1) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ("if (0 <= x && x < 1) {}", Some(serde_json::json!(["always", { "exceptRange": true }]))), + ( + "if ('blue' < x.y && x.y < 'green') {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 < x[``] && x[``] < 100) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "if (0 < x[''] && x[``] < 100) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "if (a < 4 || (b[c[0]].d['e'] < 0 || 1 <= b[c[0]].d['e'])) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= x['y'] && x['y'] <= 100) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (a < 0 && (0 < b && b < 1)) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if ((0 < a && a < 1) && b < 0) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ("if (-1 < x && x < 0) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ( + "if (0 <= this.prop && this.prop <= 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= index && index < list.length) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (ZERO <= index && index < 100) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (value <= MIN || 10 < value) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (value <= 0 || MAX < value) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + r#"if (0 <= a.b && a["b"] <= 100) {}"#, + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a.b && a[`b`] <= 100) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ("if (-1n < x && x <= 1n) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), // { "ecmaVersion": 2020 }, + ( + "if (-1n <= x && x < 1n) {}", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), // { "ecmaVersion": 2020 }, + ( + "if (x < `1` || `1` < x) {}", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), // { "ecmaVersion": 2020 }, + ( + "if (1 <= a['/(?0)/'] && a[/(?0)/] <= 100) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2018 }, + ( + "if (x <= `bar` || `foo` < x) {}", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "if ('a' < x && x < MAX ) {}", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ("if ('a' < x && x < MAX ) {}", Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + ( + "if (MIN < x && x < 'a' ) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ("if (MIN < x && x < 'a' ) {}", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + ( + "if (`blue` < x.y && x.y < `green`) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "if (0 <= x[`y`] && x[`y`] <= 100) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ( + r#"if (0 <= x[`y`] && x["y"] <= 100) {}"#, + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "if ('a' <= x && x < 'b') {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ("if (x < -1n || 1n <= x) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), // { "ecmaVersion": 2020 }, + ( + "if (x < -1n || 1n <= x) {}", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), // { "ecmaVersion": 2020 }, + ("if (1 < a && a <= 2) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ("if (x < -1 || 1 < x) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ( + "if (x <= 'bar' || 'foo' < x) {}", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), + ("if (x < 0 || 1 <= x) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ("if('a' <= x && x < MAX) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ( + "if (0 <= obj?.a && obj?.a < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2020 }, + ("if (0 < x && x <= 1) {}", Some(serde_json::json!(["never", { "onlyEquality": true }]))), + ( + "if (x !== 'foo' && 'foo' !== x) {}", + Some(serde_json::json!(["never", { "onlyEquality": true }])), + ), + ( + "if (x < 2 && x !== -3) {}", + Some(serde_json::json!(["always", { "onlyEquality": true }])), + ), + ( + "if (x !== `foo` && `foo` !== x) {}", + Some(serde_json::json!(["never", { "onlyEquality": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "if (x < `2` && x !== `-3`) {}", + Some(serde_json::json!(["always", { "onlyEquality": true }])), + ), // { "ecmaVersion": 2015 } + ]; + + let fail = vec![ + ( + "if (x <= 'foo' || 'bar' < x) {}", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), + (r#"if ("red" == value) {}"#, Some(serde_json::json!(["never"]))), + ("if (true === value) {}", Some(serde_json::json!(["never"]))), + ("if (5 != value) {}", Some(serde_json::json!(["never"]))), + ("if (5n != value) {}", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2020 }, + ("if (null !== value) {}", Some(serde_json::json!(["never"]))), + (r#"if ("red" <= value) {}"#, Some(serde_json::json!(["never"]))), + ("if (`red` <= value) {}", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + ("if (`red` <= `${foo}`) {}", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + (r#"if (`red` <= `${"red"}`) {}"#, Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + ("if (true >= value) {}", Some(serde_json::json!(["never"]))), + ("var foo = (5 < value) ? true : false", Some(serde_json::json!(["never"]))), + ("function foo() { return (null > value); }", Some(serde_json::json!(["never"]))), + ("if (-1 < str.indexOf(substr)) {}", Some(serde_json::json!(["never"]))), + (r#"if (value == "red") {}"#, Some(serde_json::json!(["always"]))), + ("if (value == `red`) {}", Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + ("if (value === true) {}", Some(serde_json::json!(["always"]))), + ("if (value === 5n) {}", Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2020 }, + (r#"if (`${"red"}` <= `red`) {}"#, Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + ( + "if (a < 0 && 0 <= b && b < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a && a < 1 && b < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ("if (1 < a && a < 0) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ("0 < a && a < 1", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ("var a = b < 0 || 1 <= b;", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ("if (0 <= x && x < -1) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ( + "var a = (b < 0 && 0 <= b);", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), + ( + "var a = (b < `0` && `0` <= b);", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "if (`green` < x.y && x.y < `blue`) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "if (0 <= a[b] && a['b'] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[b] && a[`b`] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "if (`0` <= a[b] && a[`b`] < `1`) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "if (0 <= a[b] && a.b < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[''] && a.b < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[''] && a[' '] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[''] && a[null] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[``] && a[null] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "if (0 <= a[''] && a[b] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[''] && a[b()] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[``] && a[b()] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ( + "if (0 <= a[b()] && a[b()] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a.null && a[/(?0)/] <= 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), // { "ecmaVersion": 2018 }, + ("if (3 == a) {}", Some(serde_json::json!(["never", { "onlyEquality": true }]))), + ("foo(3 === a);", Some(serde_json::json!(["never", { "onlyEquality": true }]))), + ("foo(a === 3);", Some(serde_json::json!(["always", { "onlyEquality": true }]))), + ("foo(a === `3`);", Some(serde_json::json!(["always", { "onlyEquality": true }]))), // { "ecmaVersion": 2015 }, + ("if (0 <= x && x < 1) {}", None), + ("if ( /* a */ 0 /* b */ < /* c */ foo /* d */ ) {}", Some(serde_json::json!(["never"]))), + ("if ( /* a */ foo /* b */ > /* c */ 0 /* d */ ) {}", Some(serde_json::json!(["always"]))), + ("if (foo()===1) {}", Some(serde_json::json!(["always"]))), + ("if (foo() === 1) {}", Some(serde_json::json!(["always"]))), + ("while (0 === (a));", Some(serde_json::json!(["never"]))), + ("while (0 === (a = b));", Some(serde_json::json!(["never"]))), + ("while ((a) === 0);", Some(serde_json::json!(["always"]))), + ("while ((a = b) === 0);", Some(serde_json::json!(["always"]))), + ("if (((((((((((foo)))))))))) === ((((((5)))))));", Some(serde_json::json!(["always"]))), + ("function *foo() { yield(1) < a }", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + ("function *foo() { yield((1)) < a }", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + ("function *foo() { yield 1 < a }", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + ("function *foo() { yield/**/1 < a }", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + ("function *foo() { yield(1) < ++a }", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + ("function *foo() { yield(1) < (a) }", Some(serde_json::json!(["never"]))), // { "ecmaVersion": 2015 }, + ("x=1 < a", Some(serde_json::json!(["never"]))), + ("function *foo() { yield++a < 1 }", Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + ("function *foo() { yield(a) < 1 }", Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + ("function *foo() { yield a < 1 }", Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + ("function *foo() { yield/**/a < 1 }", Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + ("function *foo() { yield++a < (1) }", Some(serde_json::json!(["always"]))), // { "ecmaVersion": 2015 }, + ("x=a < 1", Some(serde_json::json!(["always"]))), + ("0 < f()in obj", None), + ("1 > x++instanceof foo", Some(serde_json::json!(["never"]))), + ("x < ('foo')in bar", Some(serde_json::json!(["always"]))), + ("false <= ((x))in foo", Some(serde_json::json!(["never"]))), + ("x >= (1)instanceof foo", Some(serde_json::json!(["always"]))), + ("false <= ((x)) in foo", Some(serde_json::json!(["never"]))), + ("x >= 1 instanceof foo", Some(serde_json::json!(["always"]))), + ("x >= 1/**/instanceof foo", Some(serde_json::json!(["always"]))), + ("(x >= 1)instanceof foo", Some(serde_json::json!(["always"]))), + ("(x) >= (1)instanceof foo", Some(serde_json::json!(["always"]))), + ("1 > x===foo", Some(serde_json::json!(["never"]))), + ("1 > x", Some(serde_json::json!(["never"]))), + ( + "if (`green` < x.y && x.y < `blue`) {}", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), // { "ecmaVersion": 2015 }, + ("if('a' <= x && x < 'b') {}", Some(serde_json::json!(["always"]))), + ( + "if ('b' <= x && x < 'a') {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ("if('a' <= x && x < 1) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ("if (0 < a && b < max) {}", Some(serde_json::json!(["never", { "exceptRange": true }]))), + ]; + + let fix = vec![ + ( + "if (x <= 'foo' || 'bar' < x) {}", + "if ('foo' >= x || 'bar' < x) {}", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), + ( + r#"if ("red" == value) {}"#, + r#"if (value == "red") {}"#, + Some(serde_json::json!(["never"])), + ), + ("if (true === value) {}", "if (value === true) {}", Some(serde_json::json!(["never"]))), + ("if (5 != value) {}", "if (value != 5) {}", Some(serde_json::json!(["never"]))), + ("if (5n != value) {}", "if (value != 5n) {}", Some(serde_json::json!(["never"]))), + ("if (null !== value) {}", "if (value !== null) {}", Some(serde_json::json!(["never"]))), + ( + r#"if ("red" <= value) {}"#, + r#"if (value >= "red") {}"#, + Some(serde_json::json!(["never"])), + ), + ("if (`red` <= value) {}", "if (value >= `red`) {}", Some(serde_json::json!(["never"]))), + ( + "if (`red` <= `${foo}`) {}", + "if (`${foo}` >= `red`) {}", + Some(serde_json::json!(["never"])), + ), + ( + r#"if (`red` <= `${"red"}`) {}"#, + r#"if (`${"red"}` >= `red`) {}"#, + Some(serde_json::json!(["never"])), + ), + ("if (true >= value) {}", "if (value <= true) {}", Some(serde_json::json!(["never"]))), + ( + "var foo = (5 < value) ? true : false", + "var foo = (value > 5) ? true : false", + Some(serde_json::json!(["never"])), + ), + ( + "function foo() { return (null > value); }", + "function foo() { return (value < null); }", + Some(serde_json::json!(["never"])), + ), + ( + "if (-1 < str.indexOf(substr)) {}", + "if (str.indexOf(substr) > -1) {}", + Some(serde_json::json!(["never"])), + ), + ( + r#"if (value == "red") {}"#, + r#"if ("red" == value) {}"#, + Some(serde_json::json!(["always"])), + ), + ("if (value == `red`) {}", "if (`red` == value) {}", Some(serde_json::json!(["always"]))), + ("if (value === true) {}", "if (true === value) {}", Some(serde_json::json!(["always"]))), + ("if (value === 5n) {}", "if (5n === value) {}", Some(serde_json::json!(["always"]))), + ( + r#"if (`${"red"}` <= `red`) {}"#, + r#"if (`red` >= `${"red"}`) {}"#, + Some(serde_json::json!(["always"])), + ), + ( + "if (a < 0 && 0 <= b && b < 1) {}", + "if (a < 0 && b >= 0 && b < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a && a < 1 && b < 1) {}", + "if (a >= 0 && a < 1 && b < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (1 < a && a < 0) {}", + "if (a > 1 && a < 0) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "0 < a && a < 1", + "a > 0 && a < 1", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "var a = b < 0 || 1 <= b;", + "var a = b < 0 || b >= 1;", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= x && x < -1) {}", + "if (x >= 0 && x < -1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "var a = (b < 0 && 0 <= b);", + "var a = (0 > b && 0 <= b);", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), + ( + "var a = (b < `0` && `0` <= b);", + "var a = (`0` > b && `0` <= b);", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), + ( + "if (`green` < x.y && x.y < `blue`) {}", + "if (x.y > `green` && x.y < `blue`) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[b] && a['b'] < 1) {}", + "if (a[b] >= 0 && a['b'] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[b] && a[`b`] < 1) {}", + "if (a[b] >= 0 && a[`b`] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (`0` <= a[b] && a[`b`] < `1`) {}", + "if (a[b] >= `0` && a[`b`] < `1`) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[b] && a.b < 1) {}", + "if (a[b] >= 0 && a.b < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[''] && a.b < 1) {}", + "if (a[''] >= 0 && a.b < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[''] && a[' '] < 1) {}", + "if (a[''] >= 0 && a[' '] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[''] && a[null] < 1) {}", + "if (a[''] >= 0 && a[null] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[``] && a[null] < 1) {}", + "if (a[``] >= 0 && a[null] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[''] && a[b] < 1) {}", + "if (a[''] >= 0 && a[b] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[''] && a[b()] < 1) {}", + "if (a[''] >= 0 && a[b()] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[``] && a[b()] < 1) {}", + "if (a[``] >= 0 && a[b()] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a[b()] && a[b()] < 1) {}", + "if (a[b()] >= 0 && a[b()] < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 <= a.null && a[/(?0)/] <= 1) {}", + "if (a.null >= 0 && a[/(?0)/] <= 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (3 == a) {}", + "if (a == 3) {}", + Some(serde_json::json!(["never", { "onlyEquality": true }])), + ), + ( + "foo(3 === a);", + "foo(a === 3);", + Some(serde_json::json!(["never", { "onlyEquality": true }])), + ), + ( + "foo(a === 3);", + "foo(3 === a);", + Some(serde_json::json!(["always", { "onlyEquality": true }])), + ), + ( + "foo(a === `3`);", + "foo(`3` === a);", + Some(serde_json::json!(["always", { "onlyEquality": true }])), + ), + ("if (0 <= x && x < 1) {}", "if (x >= 0 && x < 1) {}", None), + ( + "if ( /* a */ 0 /* b */ < /* c */ foo /* d */ ) {}", + "if ( /* a */ foo /* b */ > /* c */ 0 /* d */ ) {}", + Some(serde_json::json!(["never"])), + ), + ( + "if ( /* a */ 0 /* < */ < /* < */ foo /* d */ ) {}", + "if ( /* a */ foo /* < */ > /* < */ 0 /* d */ ) {}", + Some(serde_json::json!(["never"])), + ), + ( + "if ( /* a */ foo /* b */ > /* c */ 0 /* d */ ) {}", + "if ( /* a */ 0 /* b */ < /* c */ foo /* d */ ) {}", + Some(serde_json::json!(["always"])), + ), + ("if (foo()===1) {}", "if (1===foo()) {}", Some(serde_json::json!(["always"]))), + ("if (foo() === 1) {}", "if (1 === foo()) {}", Some(serde_json::json!(["always"]))), + ("while (0 === (a));", "while ((a) === 0);", Some(serde_json::json!(["never"]))), + ("while (0 === (a = b));", "while ((a = b) === 0);", Some(serde_json::json!(["never"]))), + ("while ((a) === 0);", "while (0 === (a));", Some(serde_json::json!(["always"]))), + ("while ((a = b) === 0);", "while (0 === (a = b));", Some(serde_json::json!(["always"]))), + ( + "if (((((((((((foo)))))))))) === ((((((5)))))));", + "if (((((((5)))))) === ((((((((((foo)))))))))));", + Some(serde_json::json!(["always"])), + ), + ( + "function *foo() { yield(1) < a }", + "function *foo() { yield a > (1) }", + Some(serde_json::json!(["never"])), + ), + ( + "function *foo() { yield((1)) < a }", + "function *foo() { yield a > ((1)) }", + Some(serde_json::json!(["never"])), + ), + ( + "function *foo() { yield 1 < a }", + "function *foo() { yield a > 1 }", + Some(serde_json::json!(["never"])), + ), + ( + "function *foo() { yield/**/1 < a }", + "function *foo() { yield/**/a > 1 }", + Some(serde_json::json!(["never"])), + ), + ( + "function *foo() { yield(1) < ++a }", + "function *foo() { yield++a > (1) }", + Some(serde_json::json!(["never"])), + ), + ( + "function *foo() { yield(1) < (a) }", + "function *foo() { yield(a) > (1) }", + Some(serde_json::json!(["never"])), + ), + ("x=1 < a", "x=a > 1", Some(serde_json::json!(["never"]))), + ( + "function *foo() { yield++a < 1 }", + "function *foo() { yield 1 > ++a }", + Some(serde_json::json!(["always"])), + ), + ( + "function *foo() { yield(a) < 1 }", + "function *foo() { yield 1 > (a) }", + Some(serde_json::json!(["always"])), + ), + ( + "function *foo() { yield a < 1 }", + "function *foo() { yield 1 > a }", + Some(serde_json::json!(["always"])), + ), + ( + "function *foo() { yield/**/a < 1 }", + "function *foo() { yield/**/1 > a }", + Some(serde_json::json!(["always"])), + ), + ( + "function *foo() { yield++a < (1) }", + "function *foo() { yield(1) > ++a }", + Some(serde_json::json!(["always"])), + ), + ("x=a < 1", "x=1 > a", Some(serde_json::json!(["always"]))), + ("0 < f()in obj", "f() > 0 in obj", None), + ("1 > x++instanceof foo", "x++ < 1 instanceof foo", Some(serde_json::json!(["never"]))), + ("x < ('foo')in bar", "('foo') > x in bar", Some(serde_json::json!(["always"]))), + ("false <= ((x))in foo", "((x)) >= false in foo", Some(serde_json::json!(["never"]))), + ("x >= (1)instanceof foo", "(1) <= x instanceof foo", Some(serde_json::json!(["always"]))), + ("false <= ((x)) in foo", "((x)) >= false in foo", Some(serde_json::json!(["never"]))), + ("x >= 1 instanceof foo", "1 <= x instanceof foo", Some(serde_json::json!(["always"]))), + ( + "x >= 1/**/instanceof foo", + "1 <= x/**/instanceof foo", + Some(serde_json::json!(["always"])), + ), + ("(x >= 1)instanceof foo", "(1 <= x)instanceof foo", Some(serde_json::json!(["always"]))), + ( + "(x) >= (1)instanceof foo", + "(1) <= (x)instanceof foo", + Some(serde_json::json!(["always"])), + ), + ("1 > x===foo", "x < 1===foo", Some(serde_json::json!(["never"]))), + ("1 > x", "x < 1", Some(serde_json::json!(["never"]))), + ( + "if (`green` < x.y && x.y < `blue`) {}", + "if (`green` < x.y && `blue` > x.y) {}", + Some(serde_json::json!(["always", { "exceptRange": true }])), + ), + ( + "if('a' <= x && x < 'b') {}", + "if('a' <= x && 'b' > x) {}", + Some(serde_json::json!(["always"])), + ), + ( + "if ('b' <= x && x < 'a') {}", + "if (x >= 'b' && x < 'a') {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if('a' <= x && x < 1) {}", + "if(x >= 'a' && x < 1) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ( + "if (0 < a && b < max) {}", + "if (a > 0 && b < max) {}", + Some(serde_json::json!(["never", { "exceptRange": true }])), + ), + ]; + Tester::new(Yoda::NAME, Yoda::CATEGORY, pass, fail).expect_fix(fix).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/rules/oxc/const_comparisons.rs b/crates/oxc_linter/src/rules/oxc/const_comparisons.rs index 38ea39c1ff1499..010e163e3103fe 100644 --- a/crates/oxc_linter/src/rules/oxc/const_comparisons.rs +++ b/crates/oxc_linter/src/rules/oxc/const_comparisons.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; use oxc_ast::{ - ast::{Expression, NumericLiteral}, + ast::{Expression, LogicalExpression, NumericLiteral}, AstKind, }; use oxc_diagnostics::OxcDiagnostic; @@ -37,6 +37,22 @@ fn impossible(span: Span, span1: Span, x2: &str, x3: &str, x4: &str) -> OxcDiagn ]) } +fn constant_comparison_diagnostic(span: Span, evaluates_to: bool, help: String) -> OxcDiagnostic { + OxcDiagnostic::warn(format!("This comparison will always evaluate to {evaluates_to}")) + .with_help(help) + .with_label(span) +} + +fn identical_expressions_logical_operator(left_span: Span, right_span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("Both sides of the logical operator are the same") + .with_help("This logical expression will always evaluate to the same value as the expression itself.") + .with_labels([ + left_span.label("If this expression evaluates to true"), + right_span + .label("This expression will always evaluate to true"), + ]) +} + /// /// #[derive(Debug, Default, Clone)] @@ -45,13 +61,16 @@ pub struct ConstComparisons; declare_oxc_lint!( /// ### What it does /// - /// Checks for redundant comparisons between constants: - /// - Checks for ineffective double comparisons against constants. - /// - Checks for impossible comparisons against constants. + /// Checks for redundant or logically impossible comparisons. This includes: + /// - Ineffective double comparisons against constants. + /// - Impossible comparisons involving constants. + /// - Redundant comparisons where both operands are the same (e.g., a < a). /// /// ### Why is this bad? /// - /// Only one of the comparisons has any effect on the result, the programmer probably intended to flip one of the comparison operators, or compare a different value entirely. + /// Such comparisons can lead to confusing or incorrect logic in the program. In many cases: + /// - Only one of the comparisons has any effect on the result, suggesting that the programmer might have made a mistake, such as flipping one of the comparison operators or using the wrong variable. + /// - Comparisons like a < a or a >= a are always false or true respectively, making the logic redundant and potentially misleading. /// /// ### Example /// @@ -60,6 +79,8 @@ declare_oxc_lint!( /// status_code <= 400 && status_code > 500; /// status_code < 200 && status_code <= 299; /// status_code > 500 && status_code >= 500; + /// a < a; // Always false + /// a >= a; // Always true /// ``` /// /// Examples of **correct** code for this rule: @@ -67,6 +88,8 @@ declare_oxc_lint!( /// status_code >= 400 && status_code < 500; /// 500 <= status_code && 600 > status_code; /// 500 <= status_code && status_code <= 600; + /// a < b; + /// a <= b; /// ``` ConstComparisons, correctness @@ -74,22 +97,39 @@ declare_oxc_lint!( impl Rule for ConstComparisons { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + Self::check_logical_expression(node, ctx); + Self::check_binary_expression(node, ctx); + } +} + +impl ConstComparisons { + fn check_logical_expression<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) { let AstKind::LogicalExpression(logical_expr) = node.kind() else { return; }; + Self::check_logical_expression_const_literal_comparison(logical_expr, ctx); + Self::check_redundant_logical_expression(logical_expr, ctx); + } +} +impl ConstComparisons { + // checks for `x < 42 && x < 42` and `x < 42 && x > 42` + fn check_logical_expression_const_literal_comparison<'a>( + logical_expr: &LogicalExpression<'a>, + ctx: &LintContext<'a>, + ) { if logical_expr.operator != LogicalOperator::And { return; } let Some((right_cmp_op, right_expr, right_const_expr, _)) = - comparison_to_const(logical_expr.right.without_parentheses()) + comparison_to_const(logical_expr.right.get_inner_expression()) else { return; }; for (left_cmp_op, left_expr, left_const_expr, left_span) in - all_and_comparison_to_const(logical_expr.left.without_parentheses()) + all_and_comparison_to_const(logical_expr.left.get_inner_expression()) { let Some(ordering) = left_const_expr.value.partial_cmp(&right_const_expr.value) else { return; @@ -138,7 +178,7 @@ impl Rule for ConstComparisons { ctx.diagnostic(impossible( left_span, - logical_expr.right.without_parentheses().span(), + logical_expr.right.get_inner_expression().span(), &format!( "`{} {} {}` ", expr_str, @@ -156,6 +196,67 @@ impl Rule for ConstComparisons { } } } + + // checks for `a === b && a === b` and `a === b && a !== b` + fn check_redundant_logical_expression<'a>( + logical_expr: &LogicalExpression<'a>, + ctx: &LintContext<'a>, + ) { + if !matches!(logical_expr.operator, LogicalOperator::And | LogicalOperator::Or) { + return; + } + + if is_same_reference( + logical_expr.left.get_inner_expression(), + logical_expr.right.get_inner_expression(), + ctx, + ) { + ctx.diagnostic(identical_expressions_logical_operator( + logical_expr.left.span(), + logical_expr.right.span(), + )); + } + } + + fn check_binary_expression<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) { + let AstKind::BinaryExpression(bin_expr) = node.kind() else { + return; + }; + + if matches!( + bin_expr.operator, + BinaryOperator::LessEqualThan + | BinaryOperator::GreaterEqualThan + | BinaryOperator::LessThan + | BinaryOperator::GreaterThan + ) && is_same_reference( + bin_expr.left.get_inner_expression(), + bin_expr.right.get_inner_expression(), + ctx, + ) { + let is_const_truthy = matches!( + bin_expr.operator, + BinaryOperator::LessEqualThan | BinaryOperator::GreaterEqualThan + ); + + ctx.diagnostic(constant_comparison_diagnostic( + bin_expr.span, + is_const_truthy, + format!( + "Because `{}` will {} be {} itself", + bin_expr.left.span().source_text(ctx.source_text()), + if is_const_truthy { "always" } else { "never" }, + match bin_expr.operator { + BinaryOperator::GreaterEqualThan | BinaryOperator::LessEqualThan => + "equal to", + BinaryOperator::LessThan => "less then", + BinaryOperator::GreaterThan => "greater than", + _ => unreachable!(), + }, + ), + )); + } + } } // Extract a comparison between a const and non-const @@ -165,7 +266,7 @@ fn comparison_to_const<'a, 'b>( ) -> Option<(CmpOp, &'b Expression<'a>, &'b NumericLiteral<'a>, Span)> { if let Expression::BinaryExpression(bin_expr) = expr { if let Ok(cmp_op) = CmpOp::try_from(bin_expr.operator) { - match (&bin_expr.left.without_parentheses(), &bin_expr.right.without_parentheses()) { + match (&bin_expr.left.get_inner_expression(), &bin_expr.right.get_inner_expression()) { (Expression::NumericLiteral(lit), _) => { return Some((cmp_op.reverse(), &bin_expr.right, lit, bin_expr.span)); } @@ -187,8 +288,8 @@ fn all_and_comparison_to_const<'a, 'b>( Expression::LogicalExpression(logical_expr) if logical_expr.operator == LogicalOperator::And => { - let left_iter = all_and_comparison_to_const(logical_expr.left.without_parentheses()); - let right_iter = all_and_comparison_to_const(logical_expr.right.without_parentheses()); + let left_iter = all_and_comparison_to_const(logical_expr.left.get_inner_expression()); + let right_iter = all_and_comparison_to_const(logical_expr.right.get_inner_expression()); Box::new(left_iter.chain(right_iter)) } _ => { @@ -312,6 +413,11 @@ fn test() { "styleCodes.length >= 5 && styleCodes[2] >= 0", "status_code <= 400 || foo() && status_code > 500;", "status_code > 500 && foo() && bar || status_code < 400;", + // oxc specific + "a < b", + "a <= b", + "a > b", + "a >= b", ]; let fail = vec![ @@ -389,6 +495,15 @@ fn test() { "status_code <= 500 && response && status_code < 500;", // Useless right "status_code < 500 && response && status_code <= 500;", + // Oxc specific + "a < a", + "a <= a", + "a > a", + "a >= a", + "a == b && a == b", + "a == b || a == b", + "!foo && !foo", + "!foo || !foo", ]; Tester::new(ConstComparisons::NAME, ConstComparisons::CATEGORY, pass, fail).test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/react/exhaustive_deps.rs b/crates/oxc_linter/src/rules/react/exhaustive_deps.rs index e678663ca37e41..826598796ea41f 100644 --- a/crates/oxc_linter/src/rules/react/exhaustive_deps.rs +++ b/crates/oxc_linter/src/rules/react/exhaustive_deps.rs @@ -238,7 +238,7 @@ impl Rule for ExhaustiveDeps { Argument::SpreadElement(_) => { ctx.diagnostic(unknown_dependencies_diagnostic( hook_name.as_str(), - callback_node.span(), + call_expr.callee.span(), )); None } @@ -324,7 +324,7 @@ impl Rule for ExhaustiveDeps { _ => { ctx.diagnostic(unknown_dependencies_diagnostic( hook_name.as_str(), - callback_node.span(), + call_expr.callee.span(), )); None } @@ -407,7 +407,10 @@ impl Rule for ExhaustiveDeps { }) }); - if has_write_reference { + if has_write_reference + || get_declaration_from_reference_id(ident.reference_id(), ctx.semantic()) + .is_some_and(|decl| decl.scope_id() != component_scope_id) + { continue; } } @@ -528,7 +531,7 @@ impl Rule for ExhaustiveDeps { ctx.diagnostic(unnecessary_dependency_diagnostic( hook_name, &b.to_string(), - b.span, + dependencies_node.span, )); } }); @@ -541,7 +544,7 @@ impl Rule for ExhaustiveDeps { ctx.diagnostic(unnecessary_dependency_diagnostic( hook_name, &dep.to_string(), - dep.span, + dependencies_node.span, )); } } @@ -550,7 +553,10 @@ impl Rule for ExhaustiveDeps { let Some(symbol_id) = dep.symbol_id else { continue }; if dep.chain.is_empty() && is_symbol_declaration_referentially_unique(symbol_id, ctx) { - ctx.diagnostic(dependency_changes_on_every_render_diagnostic(hook_name, dep.span)); + ctx.diagnostic(dependency_changes_on_every_render_diagnostic( + hook_name, + dependencies_node.span, + )); } } } @@ -1032,22 +1038,30 @@ impl<'a> Visit<'a> for ExhaustiveDepsVisitor<'a, '_> { return; } + let is_parent_call_expr = self + .stack + .get(self.stack.len() - 2) + .is_some_and(|kind| matches!(kind, AstKind::CallExpression(_))); + match analyze_property_chain(&it.object, self.semantic) { Ok(source) => { if let Some(source) = source { - let new_chain = Vec::from([it.property.name.clone()]); - - self.found_dependencies.insert(Dependency { - name: source.name.clone(), - reference_id: source.reference_id, - span: source.span, - chain: [source.chain, new_chain].concat(), - symbol_id: self - .semantic - .symbols() - .get_reference(source.reference_id) - .symbol_id(), - }); + if is_parent_call_expr { + self.found_dependencies.insert(source); + } else { + let new_chain = Vec::from([it.property.name.clone()]); + self.found_dependencies.insert(Dependency { + name: source.name.clone(), + reference_id: source.reference_id, + span: source.span, + chain: [source.chain.clone(), new_chain].concat(), + symbol_id: self + .semantic + .symbols() + .get_reference(source.reference_id) + .symbol_id(), + }); + } } let cur_skip_reporting_dependency = self.skip_reporting_dependency; @@ -1612,16 +1626,15 @@ fn test() { }, []); return
; }", - // TODO: fix, this shouldn't report because `myRef` comes from props - // r"function useMyThing(myRef) { - // useEffect(() => { - // const handleMove = () => {}; - // myRef.current = {}; - // return () => { - // console.log(myRef.current.toString()) - // }; - // }, [myRef]); - // }", + r"function useMyThing(myRef) { + useEffect(() => { + const handleMove = () => {}; + myRef.current = {}; + return () => { + console.log(myRef.current.toString()) + }; + }, [myRef]); + }", r"function MyComponent() { const myRef = useRef(); useEffect(() => { @@ -2097,6 +2110,21 @@ fn test() { }); }, [options]); }", + "export function useCanvasZoomOrScroll() { + useEffect(() => { + let wheelStopTimeoutId: { current: number | undefined } = { current: undefined }; + + wheelStopTimeoutId = requestAnimationFrameTimeout(() => { + setLastInteraction?.(null); + }, 300); + + return () => { + if (wheelStopTimeoutId.current !== undefined) { + console.log('h1'); + } + }; + }, []); + }", ]; let fail = vec![ @@ -2631,14 +2659,14 @@ fn test() { } }, []); }", - // r"function MyComponent(props) { - // const [skillsCount] = useState(); - // useEffect(() => { - // if (skillsCount === 0 && !props.isEditMode) { - // props.toggleEditMode(); - // } - // }, [skillsCount, props.isEditMode, props.toggleEditMode]); - // }", + r"function MyComponent(props) { + const [skillsCount] = useState(); + useEffect(() => { + if (skillsCount === 0 && !props.isEditMode) { + props.toggleEditMode(); + } + }, [skillsCount, props.isEditMode, props.toggleEditMode]); + }", r"function MyComponent(props) { const [skillsCount] = useState(); useEffect(() => { diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_negative_index.rs b/crates/oxc_linter/src/rules/unicorn/prefer_negative_index.rs index 87f6effe1eed38..b45bd8bd8c8754 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_negative_index.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_negative_index.rs @@ -299,8 +299,6 @@ fn test() { "foo.slice(foo.length - 1 / 1)", "[1, 2, 3].slice([1, 2, 3].length - 1)", "foo[bar++].slice(foo[bar++].length - 1)", - "foo[a + b].slice(foo[a + b].length - 1)", - "foo[`${bar}`].slice(foo[`${bar}`].length - 1)", "function foo() {return [].slice.apply(arguments);}", "String.prototype.toSpliced.call(foo, foo.length - 1)", "String.prototype.with.call(foo, foo.length - 1)", diff --git a/crates/oxc_linter/src/snapshots/typescript_no_unused_expressions.snap b/crates/oxc_linter/src/snapshots/eslint_no_unused_expressions.snap similarity index 72% rename from crates/oxc_linter/src/snapshots/typescript_no_unused_expressions.snap rename to crates/oxc_linter/src/snapshots/eslint_no_unused_expressions.snap index 39fda8439bca97..71af5bbb6c1151 100644 --- a/crates/oxc_linter/src/snapshots/typescript_no_unused_expressions.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_unused_expressions.snap @@ -1,245 +1,245 @@ --- source: crates/oxc_linter/src/tester.rs --- - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ 0 · ─ ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ a · ─ ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ f(), 0 · ────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:2] 1 │ {0} · ─ ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ [] · ── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ a && b(); · ───────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ a() || false · ──────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ a || (b = c) · ──────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ a ? b() || (c = d) : e · ────────────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ `untagged template literal` · ─────────────────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ tag`tagged template literal` · ──────────────────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ a && b() · ──────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ a ? b() : c() · ───────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ a || b · ────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ a() && b · ──────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ a ? b : 0 · ───────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ a ? b : c() · ─────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ foo.bar; · ──────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ !a · ── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ +a · ── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:23] 1 │ "directive one"; f(); "directive two"; · ──────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:39] 1 │ function foo() {"directive one"; f(); "directive two"; } · ──────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:10] 1 │ if (0) { "not a directive"; f(); } · ────────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:34] 1 │ function foo() { var foo = true; "use strict"; } · ───────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:35] 1 │ var foo = () => { var foo = true; "use strict"; } · ───────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ `untagged template literal` · ─────────────────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ `untagged template literal` · ─────────────────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ tag`tagged template literal` · ──────────────────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ obj?.foo · ──────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ obj?.foo.bar · ──────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ obj?.foo().bar · ────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │
· ─────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ <> · ───── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:20] 1 │ class C { static { 'use strict'; } } · ───────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:4] 1 │ class C { static { 2 │ 'foo' @@ -248,7 +248,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:3:4] 2 │ 'foo' 3 │ 'bar' @@ -257,7 +257,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:11] 1 │ 2 │ if (0) 0; @@ -266,7 +266,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:4] 1 │ 2 │ f(0), {}; @@ -275,7 +275,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:4] 1 │ 2 │ a, b(); @@ -284,7 +284,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:4] 1 │ 2 │ ╭─▶ a() && @@ -295,7 +295,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:4] 1 │ 2 │ a?.b; @@ -304,7 +304,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:4] 1 │ 2 │ (a?.b).c; @@ -313,7 +313,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:4] 1 │ 2 │ a?.['b']; @@ -322,7 +322,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:4] 1 │ 2 │ (a?.['b']).c; @@ -331,7 +331,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:4] 1 │ 2 │ a?.b()?.c; @@ -340,7 +340,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:4] 1 │ 2 │ (a?.b()).c; @@ -349,7 +349,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:4] 1 │ 2 │ one[2]?.[3][4]; @@ -358,7 +358,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:2:4] 1 │ 2 │ one.two?.three.four; @@ -367,7 +367,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:4:6] 3 │ const foo = true; 4 │ 'use strict'; @@ -376,7 +376,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:6:6] 5 │ 6 │ 'use strict'; @@ -385,7 +385,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:5:6] 4 │ 5 │ 'use strict'; @@ -394,21 +394,21 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ foo && foo?.bar; · ──────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ foo ? foo?.bar : bar.baz; · ───────────────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:3:4] 2 │ class Foo {} 3 │ Foo; @@ -417,14 +417,14 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:1] 1 │ Map; · ──────────────────── ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:3:4] 2 │ declare const foo: number | undefined; 3 │ foo; @@ -433,7 +433,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:3:4] 2 │ declare const foo: number | undefined; 3 │ foo as any; @@ -448,7 +448,7 @@ source: crates/oxc_linter/src/tester.rs 4 │ ╰──── - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:3:4] 2 │ declare const foo: number | undefined; 3 │ foo!; @@ -457,7 +457,7 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Consider removing this expression - ⚠ typescript-eslint(no-unused-expressions): Disallow unused expressions + ⚠ eslint(no-unused-expressions): Disallow unused expressions ╭─[no_unused_expressions.tsx:1:36] 1 │ const _func = (value: number) => { value + 1; } · ────────── diff --git a/crates/oxc_linter/src/snapshots/eslint_yoda.snap b/crates/oxc_linter/src/snapshots/eslint_yoda.snap new file mode 100644 index 00000000000000..f7968eb66ad6da --- /dev/null +++ b/crates/oxc_linter/src/snapshots/eslint_yoda.snap @@ -0,0 +1,599 @@ +--- +source: crates/oxc_linter/src/tester.rs +assertion_line: 356 +snapshot_kind: text +--- + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (x <= 'foo' || 'bar' < x) {} + · ────────── + ╰──── + help: Expected literal to be on the left side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if ("red" == value) {} + · ────────────── + ╰──── + help: Expected literal to be on the right side of ==. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (true === value) {} + · ────────────── + ╰──── + help: Expected literal to be on the right side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (5 != value) {} + · ────────── + ╰──── + help: Expected literal to be on the right side of !=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (5n != value) {} + · ─────────── + ╰──── + help: Expected literal to be on the right side of !=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (null !== value) {} + · ────────────── + ╰──── + help: Expected literal to be on the right side of !==. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if ("red" <= value) {} + · ────────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (`red` <= value) {} + · ────────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (`red` <= `${foo}`) {} + · ───────────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (`red` <= `${"red"}`) {} + · ─────────────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (true >= value) {} + · ───────────── + ╰──── + help: Expected literal to be on the right side of >=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:12] + 1 │ var foo = (5 < value) ? true : false + · ───────── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:26] + 1 │ function foo() { return (null > value); } + · ──────────── + ╰──── + help: Expected literal to be on the right side of >. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (-1 < str.indexOf(substr)) {} + · ──────────────────────── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (value == "red") {} + · ────────────── + ╰──── + help: Expected literal to be on the left side of ==. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (value == `red`) {} + · ────────────── + ╰──── + help: Expected literal to be on the left side of ==. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (value === true) {} + · ────────────── + ╰──── + help: Expected literal to be on the left side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (value === 5n) {} + · ──────────── + ╰──── + help: Expected literal to be on the left side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (`${"red"}` <= `red`) {} + · ─────────────────── + ╰──── + help: Expected literal to be on the left side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:14] + 1 │ if (a < 0 && 0 <= b && b < 1) {} + · ────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a && a < 1 && b < 1) {} + · ────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (1 < a && a < 0) {} + · ───── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:1] + 1 │ 0 < a && a < 1 + · ───── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:18] + 1 │ var a = b < 0 || 1 <= b; + · ────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= x && x < -1) {} + · ────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:10] + 1 │ var a = (b < 0 && 0 <= b); + · ───── + ╰──── + help: Expected literal to be on the left side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:10] + 1 │ var a = (b < `0` && `0` <= b); + · ─────── + ╰──── + help: Expected literal to be on the left side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (`green` < x.y && x.y < `blue`) {} + · ───────────── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a[b] && a['b'] < 1) {} + · ───────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a[b] && a[`b`] < 1) {} + · ───────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (`0` <= a[b] && a[`b`] < `1`) {} + · ─────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a[b] && a.b < 1) {} + · ───────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a[''] && a.b < 1) {} + · ────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a[''] && a[' '] < 1) {} + · ────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a[''] && a[null] < 1) {} + · ────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a[``] && a[null] < 1) {} + · ────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a[''] && a[b] < 1) {} + · ────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a[''] && a[b()] < 1) {} + · ────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a[``] && a[b()] < 1) {} + · ────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a[b()] && a[b()] < 1) {} + · ─────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= a.null && a[/(?0)/] <= 1) {} + · ─────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (3 == a) {} + · ────── + ╰──── + help: Expected literal to be on the right side of ==. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ foo(3 === a); + · ─────── + ╰──── + help: Expected literal to be on the right side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ foo(a === 3); + · ─────── + ╰──── + help: Expected literal to be on the left side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ foo(a === `3`); + · ───────── + ╰──── + help: Expected literal to be on the left side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 <= x && x < 1) {} + · ────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:14] + 1 │ if ( /* a */ 0 /* b */ < /* c */ foo /* d */ ) {} + · ─────────────────────── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:14] + 1 │ if ( /* a */ foo /* b */ > /* c */ 0 /* d */ ) {} + · ─────────────────────── + ╰──── + help: Expected literal to be on the left side of >. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (foo()===1) {} + · ───────── + ╰──── + help: Expected literal to be on the left side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (foo() === 1) {} + · ─────────────── + ╰──── + help: Expected literal to be on the left side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:8] + 1 │ while (0 === (a)); + · ───────── + ╰──── + help: Expected literal to be on the right side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:8] + 1 │ while (0 === (a = b)); + · ───────────── + ╰──── + help: Expected literal to be on the right side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:8] + 1 │ while ((a) === 0); + · ───────── + ╰──── + help: Expected literal to be on the left side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:8] + 1 │ while ((a = b) === 0); + · ───────────── + ╰──── + help: Expected literal to be on the left side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (((((((((((foo)))))))))) === ((((((5))))))); + · ───────────────────────────────────────── + ╰──── + help: Expected literal to be on the left side of ===. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:24] + 1 │ function *foo() { yield(1) < a } + · ─────── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:24] + 1 │ function *foo() { yield((1)) < a } + · ───────── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:25] + 1 │ function *foo() { yield 1 < a } + · ───── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:28] + 1 │ function *foo() { yield/**/1 < a } + · ───── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:24] + 1 │ function *foo() { yield(1) < ++a } + · ───────── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:24] + 1 │ function *foo() { yield(1) < (a) } + · ───────── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:3] + 1 │ x=1 < a + · ───── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:24] + 1 │ function *foo() { yield++a < 1 } + · ─────── + ╰──── + help: Expected literal to be on the left side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:24] + 1 │ function *foo() { yield(a) < 1 } + · ─────── + ╰──── + help: Expected literal to be on the left side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:25] + 1 │ function *foo() { yield a < 1 } + · ───── + ╰──── + help: Expected literal to be on the left side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:28] + 1 │ function *foo() { yield/**/a < 1 } + · ───── + ╰──── + help: Expected literal to be on the left side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:24] + 1 │ function *foo() { yield++a < (1) } + · ───────── + ╰──── + help: Expected literal to be on the left side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:3] + 1 │ x=a < 1 + · ───── + ╰──── + help: Expected literal to be on the left side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:1] + 1 │ 0 < f()in obj + · ─────── + ╰──── + help: Expected literal to be on the right side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:1] + 1 │ 1 > x++instanceof foo + · ─────── + ╰──── + help: Expected literal to be on the right side of >. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:1] + 1 │ x < ('foo')in bar + · ─────────── + ╰──── + help: Expected literal to be on the left side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:1] + 1 │ false <= ((x))in foo + · ────────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:1] + 1 │ x >= (1)instanceof foo + · ──────── + ╰──── + help: Expected literal to be on the left side of >=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:1] + 1 │ false <= ((x)) in foo + · ────────────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:1] + 1 │ x >= 1 instanceof foo + · ────── + ╰──── + help: Expected literal to be on the left side of >=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:1] + 1 │ x >= 1/**/instanceof foo + · ────── + ╰──── + help: Expected literal to be on the left side of >=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:2] + 1 │ (x >= 1)instanceof foo + · ────── + ╰──── + help: Expected literal to be on the left side of >=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:1] + 1 │ (x) >= (1)instanceof foo + · ────────── + ╰──── + help: Expected literal to be on the left side of >=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:1] + 1 │ 1 > x===foo + · ───── + ╰──── + help: Expected literal to be on the right side of >. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:1] + 1 │ 1 > x + · ───── + ╰──── + help: Expected literal to be on the right side of >. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:22] + 1 │ if (`green` < x.y && x.y < `blue`) {} + · ──────────── + ╰──── + help: Expected literal to be on the left side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:16] + 1 │ if('a' <= x && x < 'b') {} + · ─────── + ╰──── + help: Expected literal to be on the left side of <. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if ('b' <= x && x < 'a') {} + · ──────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:4] + 1 │ if('a' <= x && x < 1) {} + · ──────── + ╰──── + help: Expected literal to be on the right side of <=. + + ⚠ eslint(yoda): Require or disallow "Yoda" conditions + ╭─[yoda.tsx:1:5] + 1 │ if (0 < a && b < max) {} + · ───── + ╰──── + help: Expected literal to be on the right side of <. diff --git a/crates/oxc_linter/src/snapshots/oxc_const_comparisons.snap b/crates/oxc_linter/src/snapshots/oxc_const_comparisons.snap index 0a2c19fcd1fc9c..ff1b076dbed445 100644 --- a/crates/oxc_linter/src/snapshots/oxc_const_comparisons.snap +++ b/crates/oxc_linter/src/snapshots/oxc_const_comparisons.snap @@ -1,6 +1,5 @@ --- source: crates/oxc_linter/src/tester.rs -snapshot_kind: text --- ⚠ oxc(const-comparisons): Unexpected constant comparison ╭─[const_comparisons.tsx:1:1] @@ -235,3 +234,67 @@ snapshot_kind: text · ╰── This will always evaluate to true. ╰──── help: if `status_code < 500` evaluates to true, `status_code <= 500` will always evaluate to true as well + + ⚠ oxc(const-comparisons): This comparison will always evaluate to false + ╭─[const_comparisons.tsx:1:1] + 1 │ a < a + · ───── + ╰──── + help: Because `a` will never be less then itself + + ⚠ oxc(const-comparisons): This comparison will always evaluate to true + ╭─[const_comparisons.tsx:1:1] + 1 │ a <= a + · ────── + ╰──── + help: Because `a` will always be equal to itself + + ⚠ oxc(const-comparisons): This comparison will always evaluate to false + ╭─[const_comparisons.tsx:1:1] + 1 │ a > a + · ───── + ╰──── + help: Because `a` will never be greater than itself + + ⚠ oxc(const-comparisons): This comparison will always evaluate to true + ╭─[const_comparisons.tsx:1:1] + 1 │ a >= a + · ────── + ╰──── + help: Because `a` will always be equal to itself + + ⚠ oxc(const-comparisons): Both sides of the logical operator are the same + ╭─[const_comparisons.tsx:1:1] + 1 │ a == b && a == b + · ───┬── ───┬── + · │ ╰── This expression will always evaluate to true + · ╰── If this expression evaluates to true + ╰──── + help: This logical expression will always evaluate to the same value as the expression itself. + + ⚠ oxc(const-comparisons): Both sides of the logical operator are the same + ╭─[const_comparisons.tsx:1:1] + 1 │ a == b || a == b + · ───┬── ───┬── + · │ ╰── This expression will always evaluate to true + · ╰── If this expression evaluates to true + ╰──── + help: This logical expression will always evaluate to the same value as the expression itself. + + ⚠ oxc(const-comparisons): Both sides of the logical operator are the same + ╭─[const_comparisons.tsx:1:1] + 1 │ !foo && !foo + · ──┬─ ──┬─ + · │ ╰── This expression will always evaluate to true + · ╰── If this expression evaluates to true + ╰──── + help: This logical expression will always evaluate to the same value as the expression itself. + + ⚠ oxc(const-comparisons): Both sides of the logical operator are the same + ╭─[const_comparisons.tsx:1:1] + 1 │ !foo || !foo + · ──┬─ ──┬─ + · │ ╰── This expression will always evaluate to true + · ╰── If this expression evaluates to true + ╰──── + help: This logical expression will always evaluate to the same value as the expression itself. diff --git a/crates/oxc_linter/src/snapshots/react_exhaustive_deps.snap b/crates/oxc_linter/src/snapshots/react_exhaustive_deps.snap index 6be9889e18f36f..45552be6d85df9 100644 --- a/crates/oxc_linter/src/snapshots/react_exhaustive_deps.snap +++ b/crates/oxc_linter/src/snapshots/react_exhaustive_deps.snap @@ -1,8 +1,7 @@ --- source: crates/oxc_linter/src/tester.rs -snapshot_kind: text --- - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useCallback has a missing dependency: 'props.foo.toString' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useCallback has a missing dependency: 'props.foo' ╭─[exhaustive_deps.tsx:4:14] 3 │ console.log(props.foo?.toString()); 4 │ }, []); @@ -29,7 +28,7 @@ snapshot_kind: text ╰──── help: Either include it or remove the dependency array. - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useCallback has a missing dependency: 'props.foo.bar.toString' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useCallback has a missing dependency: 'props.foo.bar' ╭─[exhaustive_deps.tsx:4:14] 3 │ console.log(props.foo?.bar.toString()); 4 │ }, []); @@ -192,37 +191,37 @@ snapshot_kind: text help: Either include it or remove the dependency array. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:7:15] + ╭─[exhaustive_deps.tsx:7:14] 6 │ console.log(local2); 7 │ }, [local1]); - · ────── + · ──────── 8 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useMemo has unnecessary dependency: local2 - ╭─[exhaustive_deps.tsx:6:23] + ╭─[exhaustive_deps.tsx:6:14] 5 │ console.log(local1); 6 │ }, [local1, local2]); - · ────── + · ──────────────── 7 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:6:15] + ╭─[exhaustive_deps.tsx:6:14] 5 │ console.log(local1); 6 │ }, [local1, local2]); - · ────── + · ──────────────── 7 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:6:23] + ╭─[exhaustive_deps.tsx:6:14] 5 │ console.log(local1); 6 │ }, [local1, local2]); - · ────── + · ──────────────── 7 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. @@ -264,10 +263,10 @@ snapshot_kind: text help: Remove the literal from the array. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:6:15] + ╭─[exhaustive_deps.tsx:6:14] 5 │ console.log(local); 6 │ }, [local, local]); - · ───── + · ────────────── 7 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. @@ -282,24 +281,24 @@ snapshot_kind: text help: Consider removing it from the dependency array. Outer scope values like aren't valid dependencies because mutating them doesn't re-render the component. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: window - ╭─[exhaustive_deps.tsx:2:34] + ╭─[exhaustive_deps.tsx:2:33] 1 │ function MyComponent() { 2 │ useCallback(() => {}, [window]); - · ────── + · ──────── 3 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: local - ╭─[exhaustive_deps.tsx:3:34] + ╭─[exhaustive_deps.tsx:3:33] 2 │ let local = props.foo; 3 │ useCallback(() => {}, [local]); - · ───── + · ─────── 4 │ } ╰──── help: Either include it or remove the dependency array. - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a missing dependency: 'history.listen' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a missing dependency: 'history' ╭─[exhaustive_deps.tsx:4:14] 3 │ return history.listen(); 4 │ }, []); @@ -308,7 +307,7 @@ snapshot_kind: text ╰──── help: Either include it or remove the dependency array. - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a missing dependency: 'history.foo.bar' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has missing dependencies: 'history.foo', and 'history.foo.bar' ╭─[exhaustive_deps.tsx:7:14] 6 │ ]; 7 │ }, []); @@ -516,10 +515,10 @@ snapshot_kind: text help: Extract the expression to a separate variable so it can be statically checked. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: props.foo - ╭─[exhaustive_deps.tsx:6:22] + ╭─[exhaustive_deps.tsx:6:14] 5 │ console.log(props.bar); 6 │ }, [props, props.foo]); - · ───────── + · ────────────────── 7 │ } ╰──── help: Either include it or remove the dependency array. @@ -552,37 +551,37 @@ snapshot_kind: text help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: local.id - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(local); 5 │ }, [local.id]); - · ──────── + · ────────── 6 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: local - ╭─[exhaustive_deps.tsx:5:25] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(local); 5 │ }, [local.id, local]); - · ───── + · ───────────────── 6 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: local.id - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(local); 5 │ }, [local.id, local]); - · ──────── + · ───────────────── 6 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useCallback has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:25] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(local); 5 │ }, [local.id, local]); - · ───── + · ───────────────── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. @@ -606,19 +605,19 @@ snapshot_kind: text help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: props.foo.bar.baz - ╭─[exhaustive_deps.tsx:6:26] + ╭─[exhaustive_deps.tsx:6:14] 5 │ console.log(color); 6 │ }, [props.foo, props.foo.bar.baz]); - · ───────────────── + · ────────────────────────────── 7 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: props.foo - ╭─[exhaustive_deps.tsx:4:34] + ╭─[exhaustive_deps.tsx:4:14] 3 │ console.log(props.foo.bar.baz); 4 │ }, [props.foo.bar.baz, props.foo]); - · ───────── + · ────────────────────────────── 5 │ } ╰──── help: Either include it or remove the dependency array. @@ -642,10 +641,10 @@ snapshot_kind: text help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: props.foo.bar.baz - ╭─[exhaustive_deps.tsx:4:15] + ╭─[exhaustive_deps.tsx:4:14] 3 │ console.log(props.foo.bar); 4 │ }, [props.foo.bar.baz]); - · ───────────────── + · ─────────────────── 5 │ } ╰──── help: Either include it or remove the dependency array. @@ -660,10 +659,10 @@ snapshot_kind: text help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: props.foo.bar.baz - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(props.hello); 5 │ }, [props.foo.bar.baz]); - · ───────────────── + · ─────────────────── 6 │ } ╰──── help: Either include it or remove the dependency array. @@ -678,10 +677,10 @@ snapshot_kind: text help: Remove the literal from the array. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(local); 5 │ }, [local, local]); - · ───── + · ────────────── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. @@ -694,19 +693,19 @@ snapshot_kind: text ╰──── ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: local1 - ╭─[exhaustive_deps.tsx:3:34] + ╭─[exhaustive_deps.tsx:3:33] 2 │ const local1 = {}; 3 │ useCallback(() => {}, [local1]); - · ────── + · ──────── 4 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useCallback has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:34] + ╭─[exhaustive_deps.tsx:3:33] 2 │ const local1 = {}; 3 │ useCallback(() => {}, [local1]); - · ────── + · ──────── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. @@ -1009,10 +1008,10 @@ snapshot_kind: text help: Consider removing it from the dependency array. Outer scope values like aren't valid dependencies because mutating them doesn't re-render the component. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: activeTab - ╭─[exhaustive_deps.tsx:7:43] + ╭─[exhaustive_deps.tsx:7:14] 6 │ ref2.current.scrollTop = initY; 7 │ }, [ref1.current, ref2.current, activeTab, initY]); - · ───────── + · ────────────────────────────────────────────── 8 │ } ╰──── help: Either include it or remove the dependency array. @@ -1035,7 +1034,7 @@ snapshot_kind: text ╰──── help: Either include it or remove the dependency array. - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a missing dependency: 'props.onChange' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has missing dependencies: 'props.onChange', and 'props' ╭─[exhaustive_deps.tsx:6:14] 5 │ } 6 │ }, []); @@ -1044,7 +1043,7 @@ snapshot_kind: text ╰──── help: Either include it or remove the dependency array. - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a missing dependency: 'props.onChange' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has missing dependencies: 'props.onChange', and 'props' ╭─[exhaustive_deps.tsx:6:14] 5 │ } 6 │ }, []); @@ -1053,7 +1052,7 @@ snapshot_kind: text ╰──── help: Either include it or remove the dependency array. - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has missing dependencies: 'props.onPause', and 'props.onPlay' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a missing dependency: 'props' ╭─[exhaustive_deps.tsx:9:14] 8 │ } 9 │ }, []); @@ -1062,7 +1061,7 @@ snapshot_kind: text ╰──── help: Either include it or remove the dependency array. - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a missing dependency: 'props.foo.onChange' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has missing dependencies: 'props.foo.onChange', and 'props.foo' ╭─[exhaustive_deps.tsx:6:14] 5 │ } 6 │ }, []); @@ -1071,7 +1070,7 @@ snapshot_kind: text ╰──── help: Either include it or remove the dependency array. - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has missing dependencies: 'props.onChange', and 'props.foo.onChange' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has missing dependencies: 'props.foo', 'props', and 'props.foo.onChange' ╭─[exhaustive_deps.tsx:7:14] 6 │ } 7 │ }, []); @@ -1080,7 +1079,16 @@ snapshot_kind: text ╰──── help: Either include it or remove the dependency array. - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has missing dependencies: 'skillsCount', 'props.toggleEditMode', and 'props.isEditMode' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a missing dependency: 'props' + ╭─[exhaustive_deps.tsx:7:14] + 6 │ } + 7 │ }, [skillsCount, props.isEditMode, props.toggleEditMode]); + · ───────────────────────────────────────────────────── + 8 │ } + ╰──── + help: Either include it or remove the dependency array. + + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has missing dependencies: 'skillsCount', 'props.isEditMode', and 'props' ╭─[exhaustive_deps.tsx:7:14] 6 │ } 7 │ }, []); @@ -1089,7 +1097,7 @@ snapshot_kind: text ╰──── help: Either include it or remove the dependency array. - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has missing dependencies: 'props.onChange', and 'props' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a missing dependency: 'props' ╭─[exhaustive_deps.tsx:5:14] 4 │ props.onChange(); 5 │ }, []); @@ -1098,7 +1106,7 @@ snapshot_kind: text ╰──── help: Either include it or remove the dependency array. - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has missing dependencies: 'props.onChange', and 'props' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a missing dependency: 'props' ╭─[exhaustive_deps.tsx:5:14] 4 │ externalCall(props); 5 │ }, []); @@ -1243,10 +1251,10 @@ snapshot_kind: text help: Consider removing it from the dependency array. Outer scope values like aren't valid dependencies because mutating them doesn't re-render the component. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:10:60] + ╭─[exhaustive_deps.tsx:10:16] 9 │ console.log(MutableStore.hello.world, props.foo, x, y, z, global.stuff); 10 │ }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]); - · ─ + · ──────────────────────────────────────────────────────────── 11 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. @@ -1279,10 +1287,10 @@ snapshot_kind: text help: Consider removing it from the dependency array. Outer scope values like aren't valid dependencies because mutating them doesn't re-render the component. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:10:60] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]); - · ─ + · ──────────────────────────────────────────────────────────── 11 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. @@ -1315,64 +1323,64 @@ snapshot_kind: text help: Consider removing it from the dependency array. Outer scope values like aren't valid dependencies because mutating them doesn't re-render the component. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: props.foo - ╭─[exhaustive_deps.tsx:10:43] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]); - · ───────── + · ──────────────────────────────────────────────────────────── 11 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: y - ╭─[exhaustive_deps.tsx:10:57] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]); - · ─ + · ──────────────────────────────────────────────────────────── 11 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: z - ╭─[exhaustive_deps.tsx:10:60] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]); - · ─ + · ──────────────────────────────────────────────────────────── 11 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: MutableStore.hello.world - ╭─[exhaustive_deps.tsx:10:17] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]); - · ──────────────────────── + · ──────────────────────────────────────────────────────────── 11 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: global.stuff - ╭─[exhaustive_deps.tsx:10:63] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]); - · ──────────── + · ──────────────────────────────────────────────────────────── 11 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: x - ╭─[exhaustive_deps.tsx:10:54] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]); - · ─ + · ──────────────────────────────────────────────────────────── 11 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useCallback has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:10:60] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]); - · ─ + · ──────────────────────────────────────────────────────────── 11 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. @@ -1405,64 +1413,64 @@ snapshot_kind: text help: Consider removing it from the dependency array. Outer scope values like aren't valid dependencies because mutating them doesn't re-render the component. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: props.foo - ╭─[exhaustive_deps.tsx:10:45] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore?.hello?.world, props.foo, x, y, z, global?.stuff]); - · ───────── + · ─────────────────────────────────────────────────────────────── 11 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: y - ╭─[exhaustive_deps.tsx:10:59] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore?.hello?.world, props.foo, x, y, z, global?.stuff]); - · ─ + · ─────────────────────────────────────────────────────────────── 11 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: z - ╭─[exhaustive_deps.tsx:10:62] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore?.hello?.world, props.foo, x, y, z, global?.stuff]); - · ─ + · ─────────────────────────────────────────────────────────────── 11 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: MutableStore.hello.world - ╭─[exhaustive_deps.tsx:10:17] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore?.hello?.world, props.foo, x, y, z, global?.stuff]); - · ────────────────────────── + · ─────────────────────────────────────────────────────────────── 11 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: global.stuff - ╭─[exhaustive_deps.tsx:10:65] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore?.hello?.world, props.foo, x, y, z, global?.stuff]); - · ───────────── + · ─────────────────────────────────────────────────────────────── 11 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react(exhaustive-deps): React Hook useCallback has unnecessary dependency: x - ╭─[exhaustive_deps.tsx:10:56] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore?.hello?.world, props.foo, x, y, z, global?.stuff]); - · ─ + · ─────────────────────────────────────────────────────────────── 11 │ } ╰──── help: Either include it or remove the dependency array. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useCallback has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:10:62] + ╭─[exhaustive_deps.tsx:10:16] 9 │ // nothing 10 │ }, [MutableStore?.hello?.world, props.foo, x, y, z, global?.stuff]); - · ─ + · ─────────────────────────────────────────────────────────────── 11 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. @@ -1549,163 +1557,163 @@ snapshot_kind: text help: Either include it or remove the dependency array. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:10:15] + ╭─[exhaustive_deps.tsx:10:14] 9 │ return Store.subscribe(handleNext); 10 │ }, [handleNext]); - · ────────── + · ──────────── 11 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:10:15] + ╭─[exhaustive_deps.tsx:10:14] 9 │ return Store.subscribe(handleNext); 10 │ }, [handleNext]); - · ────────── + · ──────────── 11 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:10:15] + ╭─[exhaustive_deps.tsx:10:14] 9 │ return Store.subscribe(handleNext); 10 │ }, [handleNext]); - · ────────── + · ──────────── 11 │ ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:13:15] + ╭─[exhaustive_deps.tsx:13:14] 12 │ return Store.subscribe(handleNext1); 13 │ }, [handleNext1]); - · ─────────── + · ───────────── 14 │ useLayoutEffect(() => { ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useLayoutEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:16:15] + ╭─[exhaustive_deps.tsx:16:14] 15 │ return Store.subscribe(handleNext2); 16 │ }, [handleNext2]); - · ─────────── + · ───────────── 17 │ useMemo(() => { ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:19:15] + ╭─[exhaustive_deps.tsx:19:14] 18 │ return Store.subscribe(handleNext3); 19 │ }, [handleNext3]); - · ─────────── + · ───────────── 20 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:14:15] + ╭─[exhaustive_deps.tsx:14:14] 13 │ return Store.subscribe(() => handleNext1()); 14 │ }, [handleNext1]); - · ─────────── + · ───────────── 15 │ useLayoutEffect(() => { ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useLayoutEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:18:15] + ╭─[exhaustive_deps.tsx:18:14] 17 │ return Store.subscribe(() => handleNext2()); 18 │ }, [handleNext2]); - · ─────────── + · ───────────── 19 │ useMemo(() => { ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:22:15] + ╭─[exhaustive_deps.tsx:22:14] 21 │ return Store.subscribe(() => handleNext3()); 22 │ }, [handleNext3]); - · ─────────── + · ───────────── 23 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:14:15] + ╭─[exhaustive_deps.tsx:14:14] 13 │ return Store.subscribe(() => handleNext1()); 14 │ }, [handleNext1]); - · ─────────── + · ───────────── 15 │ useLayoutEffect(() => { ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useLayoutEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:18:15] + ╭─[exhaustive_deps.tsx:18:14] 17 │ return Store.subscribe(() => handleNext2()); 18 │ }, [handleNext2]); - · ─────────── + · ───────────── 19 │ useMemo(() => { ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:22:15] + ╭─[exhaustive_deps.tsx:22:14] 21 │ return Store.subscribe(() => handleNext3()); 22 │ }, [handleNext3]); - · ─────────── + · ───────────── 23 │ return ( ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:11:15] + ╭─[exhaustive_deps.tsx:11:14] 10 │ return Store.subscribe(handleNext2); 11 │ }, [handleNext1, handleNext2]); - · ─────────── + · ────────────────────────── 12 │ useEffect(() => { ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:11:28] + ╭─[exhaustive_deps.tsx:11:14] 10 │ return Store.subscribe(handleNext2); 11 │ }, [handleNext1, handleNext2]); - · ─────────── + · ────────────────────────── 12 │ useEffect(() => { ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:15:15] + ╭─[exhaustive_deps.tsx:15:14] 14 │ return Store.subscribe(handleNext2); 15 │ }, [handleNext1, handleNext2]); - · ─────────── + · ────────────────────────── 16 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:15:28] + ╭─[exhaustive_deps.tsx:15:14] 14 │ return Store.subscribe(handleNext2); 15 │ }, [handleNext1, handleNext2]); - · ─────────── + · ────────────────────────── 16 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:12:15] + ╭─[exhaustive_deps.tsx:12:14] 11 │ return Store.subscribe(handleNext); 12 │ }, [handleNext]); - · ────────── + · ──────────── 13 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:13:15] + ╭─[exhaustive_deps.tsx:13:14] 12 │ return Store.subscribe(handleNext); 13 │ }, [handleNext]); - · ────────── + · ──────────── 14 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. @@ -1756,10 +1764,10 @@ snapshot_kind: text help: Either include it or remove the dependency array. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:13:15] + ╭─[exhaustive_deps.tsx:13:14] 12 │ return () => clearInterval(id); 13 │ }, [increment]); - · ───────── + · ─────────── 14 │ ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. @@ -1954,12 +1962,11 @@ snapshot_kind: text help: Either include it or remove the dependency array. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect received a function whose dependencies are unknown. - ╭─[exhaustive_deps.tsx:3:21] - 2 │ const local = {}; - 3 │ ╭─▶ useEffect(debounce(() => { - 4 │ │ console.log(local); - 5 │ ╰─▶ }, delay), []); - 6 │ } + ╭─[exhaustive_deps.tsx:3:11] + 2 │ const local = {}; + 3 │ useEffect(debounce(() => { + · ───────── + 4 │ console.log(local); ╰──── help: Pass an inline function instead. @@ -1982,244 +1989,244 @@ snapshot_kind: text help: Either include it or remove the dependency array. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:31] + ╭─[exhaustive_deps.tsx:3:30] 2 │ const foo = {}; 3 │ useMemo(() => foo, [foo]); - · ─── + · ───── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:31] + ╭─[exhaustive_deps.tsx:3:30] 2 │ const foo = []; 3 │ useMemo(() => foo, [foo]); - · ─── + · ───── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:31] + ╭─[exhaustive_deps.tsx:3:30] 2 │ const foo = () => {}; 3 │ useMemo(() => foo, [foo]); - · ─── + · ───── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:31] + ╭─[exhaustive_deps.tsx:3:30] 2 │ const foo = function bar(){}; 3 │ useMemo(() => foo, [foo]); - · ─── + · ───── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:31] + ╭─[exhaustive_deps.tsx:3:30] 2 │ const foo = class {}; 3 │ useMemo(() => foo, [foo]); - · ─── + · ───── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:31] + ╭─[exhaustive_deps.tsx:3:30] 2 │ const foo = true ? {} : 'fine'; 3 │ useMemo(() => foo, [foo]); - · ─── + · ───── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:31] + ╭─[exhaustive_deps.tsx:3:30] 2 │ const foo = bar || {}; 3 │ useMemo(() => foo, [foo]); - · ─── + · ───── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:31] + ╭─[exhaustive_deps.tsx:3:30] 2 │ const foo = bar ?? {}; 3 │ useMemo(() => foo, [foo]); - · ─── + · ───── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:31] + ╭─[exhaustive_deps.tsx:3:30] 2 │ const foo = bar && {}; 3 │ useMemo(() => foo, [foo]); - · ─── + · ───── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:31] + ╭─[exhaustive_deps.tsx:3:30] 2 │ const foo = bar ? baz ? {} : null : null; 3 │ useMemo(() => foo, [foo]); - · ─── + · ───── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:31] + ╭─[exhaustive_deps.tsx:3:30] 2 │ let foo = {}; 3 │ useMemo(() => foo, [foo]); - · ─── + · ───── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:3:31] + ╭─[exhaustive_deps.tsx:3:30] 2 │ var foo = {}; 3 │ useMemo(() => foo, [foo]); - · ─── + · ───── 4 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useCallback has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(foo); 5 │ }, [foo]); - · ─── + · ───── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(foo); 5 │ }, [foo]); - · ─── + · ───── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useLayoutEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(foo); 5 │ }, [foo]); - · ─── + · ───── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useImperativeHandle has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:8:14] + ╭─[exhaustive_deps.tsx:8:13] 7 │ }, 8 │ [foo] - · ─── + · ───── 9 │ ); ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(foo); 5 │ }, [foo]); - · ─── + · ───── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:6:15] + ╭─[exhaustive_deps.tsx:6:14] 5 │ console.log(foo); 6 │ }, [foo]); - · ─── + · ───── 7 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(foo); 5 │ }, [foo]); - · ─── + · ───── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(foo); 5 │ }, [foo]); - · ─── + · ───── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(foo); 5 │ }, [foo]); - · ─── + · ───── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(foo); 5 │ }, [foo]); - · ─── + · ───── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(foo); 5 │ }, [foo]); - · ─── + · ───── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(foo); 5 │ }, [foo]); - · ─── + · ───── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useMemo has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(new Bar()); 5 │ }, [Bar]); - · ─── + · ───── 6 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useLayoutEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:5:15] + ╭─[exhaustive_deps.tsx:5:14] 4 │ console.log(foo); 5 │ }, [foo]); - · ─── + · ───── 6 │ useEffect(() => { ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a dependency array that changes every render. - ╭─[exhaustive_deps.tsx:8:15] + ╭─[exhaustive_deps.tsx:8:14] 7 │ console.log(foo); 8 │ }, [foo]); - · ─── + · ───── 9 │ } ╰──── help: Try memoizing this variable with `useRef` or `useCallback`. diff --git a/crates/oxc_linter/src/utils/mod.rs b/crates/oxc_linter/src/utils/mod.rs index 62fc5d8538578b..c3627fb7d6cd81 100644 --- a/crates/oxc_linter/src/utils/mod.rs +++ b/crates/oxc_linter/src/utils/mod.rs @@ -34,6 +34,49 @@ const VITEST_COMPATIBLE_JEST_RULES: phf::Set<&'static str> = phf::phf_set! { "valid-expect", }; +// List of Eslint rules that have Typescript equivalents. +const TYPESCRIPT_COMPATIBLE_ESLINT_RULES: phf::Set<&'static str> = phf::phf_set! { + "class-methods-use-this", + "default-param-last", + "init-declarations", + "max-params", + "no-array-constructor", + "no-dupe-class-members", + "no-empty-function", + "no-invalid-this", + "no-loop-func", + "no-loss-of-precision", + "no-magic-numbers", + "no-redeclare", + "no-restricted-imports", + "no-shadow", + "no-unused-expressions", + "no-unused-vars", + "no-use-before-define", + "no-useless-constructor", + + // these rules are equivalents, but not supported + // "block-spacing", + // "brace-style", + // "comma-dangle", + // "comma-spacing", + // "func-call-spacing", + // "indent", + // "key-spacing", + // "keyword-spacing", + // "lines-around-comment", + // "lines-between-class-members", + // "no-extra-parens", + // "no-extra-semi", + // "object-curly-spacing", + // "padding-line-between-statements", + // "quotes", + // "semi", + // "space-before-blocks", + // "space-before-function-paren", + // "space-infix-ops", +}; + /// Check if the Jest rule is adapted to Vitest. /// Many Vitest rule are essentially ports of Jest plugin rules with minor modifications. /// For these rules, we use the corresponding jest rules with some adjustments for compatibility. @@ -41,6 +84,13 @@ pub fn is_jest_rule_adapted_to_vitest(rule_name: &str) -> bool { VITEST_COMPATIBLE_JEST_RULES.contains(rule_name) } +/// Check if the Eslint rule is adapted to Typescript. +/// Many Typescript rule are essentially ports of Eslint plugin rules with minor modifications. +/// For these rules, we use the corresponding eslint rules with some adjustments for compatibility. +pub fn is_eslint_rule_adapted_to_typescript(rule_name: &str) -> bool { + TYPESCRIPT_COMPATIBLE_ESLINT_RULES.contains(rule_name) +} + pub fn read_to_string(path: &Path) -> io::Result { // `simdutf8` is faster than `std::str::from_utf8` which `fs::read_to_string` uses internally let bytes = std::fs::read(path)?; diff --git a/crates/oxc_linter/src/utils/unicorn.rs b/crates/oxc_linter/src/utils/unicorn.rs index 7b697e67fa2553..b2b770379b5d32 100644 --- a/crates/oxc_linter/src/utils/unicorn.rs +++ b/crates/oxc_linter/src/utils/unicorn.rs @@ -7,6 +7,7 @@ use oxc_ast::{ AstKind, }; use oxc_semantic::AstNode; +use oxc_span::cmp::ContentEq; use oxc_syntax::operator::LogicalOperator; pub use self::boolean::*; @@ -177,6 +178,20 @@ pub fn is_same_reference(left: &Expression, right: &Expression, ctx: &LintContex (Expression::StringLiteral(left_str), Expression::StringLiteral(right_str)) => { return left_str.value == right_str.value; } + (Expression::StringLiteral(string_lit), Expression::TemplateLiteral(template_lit)) + | (Expression::TemplateLiteral(template_lit), Expression::StringLiteral(string_lit)) => { + return template_lit.is_no_substitution_template() + && string_lit.value == template_lit.quasi().unwrap(); + } + (Expression::TemplateLiteral(left_str), Expression::TemplateLiteral(right_str)) => { + return left_str.quasis.content_eq(&right_str.quasis) + && left_str.expressions.len() == right_str.expressions.len() + && left_str + .expressions + .iter() + .zip(right_str.expressions.iter()) + .all(|(left, right)| is_same_reference(left, right, ctx)); + } (Expression::NumericLiteral(left_num), Expression::NumericLiteral(right_num)) => { return left_num.raw == right_num.raw; } @@ -189,6 +204,35 @@ pub fn is_same_reference(left: &Expression, right: &Expression, ctx: &LintContex return left_bool.value == right_bool.value; } + ( + Expression::BinaryExpression(left_bin_expr), + Expression::BinaryExpression(right_bin_expr), + ) => { + return left_bin_expr.operator == right_bin_expr.operator + && is_same_reference( + left_bin_expr.left.get_inner_expression(), + right_bin_expr.left.get_inner_expression(), + ctx, + ) + && is_same_reference( + left_bin_expr.right.get_inner_expression(), + right_bin_expr.right.get_inner_expression(), + ctx, + ); + } + + ( + Expression::UnaryExpression(left_unary_expr), + Expression::UnaryExpression(right_unary_expr), + ) => { + return left_unary_expr.operator == right_unary_expr.operator + && is_same_reference( + left_unary_expr.argument.get_inner_expression(), + right_unary_expr.argument.get_inner_expression(), + ctx, + ); + } + ( Expression::ChainExpression(left_chain_expr), Expression::ChainExpression(right_chain_expr), @@ -237,12 +281,35 @@ pub fn is_same_member_expression( MemberExpression::ComputedMemberExpression(right), ) = (left, right) { - if !is_same_reference( - left.expression.get_inner_expression(), - right.expression.get_inner_expression(), - ctx, - ) { - return false; + // TODO(camc314): refactor this to go through `is_same_reference` and introduce some sort of `context` to indicate how the two values should be compared. + match (&left.expression, &right.expression) { + // x['/regex/'] === x[/regex/] + // x[/regex/] === x['/regex/'] + (Expression::StringLiteral(string_lit), Expression::RegExpLiteral(regex_lit)) + | (Expression::RegExpLiteral(regex_lit), Expression::StringLiteral(string_lit)) => { + if string_lit.value != regex_lit.raw { + return false; + } + } + // ex) x[`/regex/`] === x[/regex/] + // ex) x[/regex/] === x[`/regex/`] + (Expression::TemplateLiteral(template_lit), Expression::RegExpLiteral(regex_lit)) + | (Expression::RegExpLiteral(regex_lit), Expression::TemplateLiteral(template_lit)) => { + if !(template_lit.is_no_substitution_template() + && template_lit.quasi().unwrap() == regex_lit.raw) + { + return false; + } + } + _ => { + if !is_same_reference( + left.expression.get_inner_expression(), + right.expression.get_inner_expression(), + ctx, + ) { + return false; + } + } } } diff --git a/crates/oxc_mangler/Cargo.toml b/crates/oxc_mangler/Cargo.toml index c801cc40618f4b..019a1416d608ec 100644 --- a/crates/oxc_mangler/Cargo.toml +++ b/crates/oxc_mangler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_mangler" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_minifier/CHANGELOG.md b/crates/oxc_minifier/CHANGELOG.md index f4c3e342644a83..2cd488e25ff7c4 100644 --- a/crates/oxc_minifier/CHANGELOG.md +++ b/crates/oxc_minifier/CHANGELOG.md @@ -4,6 +4,34 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +- f2f31a8 traverse: [**BREAKING**] Remove unsound APIs (#7514) (overlookmotel) + +- b0e1c03 ast: [**BREAKING**] Add `StringLiteral::raw` field (#7393) (Boshen) + +### Features + +- 97af341 minifier: Minify alternated one child if block (#7231) (7086cmd) +- ac0d25c minifier: Minify one child if statement expression (#7230) (Ethan Goh) + +### Bug Fixes + +- 896ff86 minifier: Do not fold if statement block with lexical declaration (#7519) (Boshen) + +### Performance + +- c133693 minifier: Fuse ast passes (#7493) (Boshen) + +### Refactor + +- 63a66cf minifier: Remove unused ast pass from DCE (#7540) (Boshen) +- 625a5ba minifier: Improve ast passes (#7518) (Boshen) + +### Testing + +- 9d6e14b ecmascript: Move tests to `oxc_minifier` due to cyclic dependency with `oxc_parser` (#7542) (Boshen) + ## [0.37.0] - 2024-11-21 ### Features diff --git a/crates/oxc_minifier/Cargo.toml b/crates/oxc_minifier/Cargo.toml index 352847c62ca4e6..e0dd97fe8ac190 100644 --- a/crates/oxc_minifier/Cargo.toml +++ b/crates/oxc_minifier/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_minifier" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_napi/Cargo.toml b/crates/oxc_napi/Cargo.toml deleted file mode 100644 index 43ed04239cc044..00000000000000 --- a/crates/oxc_napi/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "oxc_napi" -version = "0.38.0" -authors.workspace = true -categories.workspace = true -edition.workspace = true -homepage.workspace = true -include = ["/src"] -keywords.workspace = true -license.workspace = true -publish = false -repository.workspace = true -rust-version.workspace = true -description.workspace = true - -[lints] -workspace = true - -[lib] -test = false -doctest = false - -[dependencies] -oxc_isolated_declarations = { workspace = true } -oxc_sourcemap = { workspace = true, features = ["napi"] } -oxc_span = { workspace = true } -oxc_syntax = { workspace = true } -oxc_transformer = { workspace = true } - -napi = { workspace = true } -napi-derive = { workspace = true } - -rustc-hash = { workspace = true } diff --git a/crates/oxc_napi/src/isolated_declarations.rs b/crates/oxc_napi/src/isolated_declarations.rs deleted file mode 100644 index 11a4a0dbb0e060..00000000000000 --- a/crates/oxc_napi/src/isolated_declarations.rs +++ /dev/null @@ -1,30 +0,0 @@ -use napi_derive::napi; - -use oxc_sourcemap::napi::SourceMap; - -#[napi(object)] -pub struct IsolatedDeclarationsResult { - pub code: String, - pub map: Option, - pub errors: Vec, -} - -#[napi(object)] -#[derive(Debug, Default, Clone, Copy)] -pub struct IsolatedDeclarationsOptions { - /// Do not emit declarations for code that has an @internal annotation in its JSDoc comment. - /// This is an internal compiler option; use at your own risk, because the compiler does not check that the result is valid. - /// - /// Default: `false` - /// - /// See - pub strip_internal: Option, - - pub sourcemap: Option, -} - -impl From for oxc_isolated_declarations::IsolatedDeclarationsOptions { - fn from(options: IsolatedDeclarationsOptions) -> Self { - Self { strip_internal: options.strip_internal.unwrap_or_default() } - } -} diff --git a/crates/oxc_napi/src/lib.rs b/crates/oxc_napi/src/lib.rs deleted file mode 100644 index c341153196592b..00000000000000 --- a/crates/oxc_napi/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod parse; - -pub mod isolated_declarations; - -pub mod transform; diff --git a/crates/oxc_napi/src/transform.rs b/crates/oxc_napi/src/transform.rs deleted file mode 100644 index 6586a6cd51f6e5..00000000000000 --- a/crates/oxc_napi/src/transform.rs +++ /dev/null @@ -1,385 +0,0 @@ -// NOTE: Types must be aligned with [@types/babel__core](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b5dc32740d9b45d11cff9b025896dd333c795b39/types/babel__core/index.d.ts). - -#![allow(rustdoc::bare_urls)] - -use std::path::PathBuf; - -use napi::Either; -use napi_derive::napi; -use rustc_hash::FxHashMap; - -use oxc_sourcemap::napi::SourceMap; -use oxc_transformer::{EnvOptions, JsxRuntime, RewriteExtensionsMode}; - -use super::isolated_declarations::IsolatedDeclarationsOptions; - -#[derive(Default)] -#[napi(object)] -pub struct TransformResult { - /// The transformed code. - /// - /// If parsing failed, this will be an empty string. - pub code: String, - - /// The source map for the transformed code. - /// - /// This will be set if {@link TransformOptions#sourcemap} is `true`. - pub map: Option, - - /// The `.d.ts` declaration file for the transformed code. Declarations are - /// only generated if `declaration` is set to `true` and a TypeScript file - /// is provided. - /// - /// If parsing failed and `declaration` is set, this will be an empty string. - /// - /// @see {@link TypeScriptOptions#declaration} - /// @see [declaration tsconfig option](https://www.typescriptlang.org/tsconfig/#declaration) - pub declaration: Option, - - /// Declaration source map. Only generated if both - /// {@link TypeScriptOptions#declaration declaration} and - /// {@link TransformOptions#sourcemap sourcemap} are set to `true`. - pub declaration_map: Option, - - /// Parse and transformation errors. - /// - /// Oxc's parser recovers from common syntax errors, meaning that - /// transformed code may still be available even if there are errors in this - /// list. - pub errors: Vec, -} - -/// Options for transforming a JavaScript or TypeScript file. -/// -/// @see {@link transform} -#[napi(object)] -#[derive(Default)] -pub struct TransformOptions { - #[napi(ts_type = "'script' | 'module' | 'unambiguous' | undefined")] - pub source_type: Option, - - /// Treat the source text as `js`, `jsx`, `ts`, or `tsx`. - #[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")] - pub lang: Option, - - /// The current working directory. Used to resolve relative paths in other - /// options. - pub cwd: Option, - - /// Enable source map generation. - /// - /// When `true`, the `sourceMap` field of transform result objects will be populated. - /// - /// @default false - /// - /// @see {@link SourceMap} - pub sourcemap: Option, - - /// Set assumptions in order to produce smaller output. - pub assumptions: Option, - - /// Configure how TypeScript is transformed. - pub typescript: Option, - - /// Configure how TSX and JSX are transformed. - pub jsx: Option, - - /// Sets the target environment for the generated JavaScript. - /// - /// The lowest target is `es2015`. - /// - /// Example: - /// - /// * 'es2015' - /// * ['es2020', 'chrome58', 'edge16', 'firefox57', 'node12', 'safari11'] - /// - /// @default `esnext` (No transformation) - /// - /// @see [esbuild#target](https://esbuild.github.io/api/#target) - pub target: Option>>, - - /// Define Plugin - #[napi(ts_type = "Record")] - pub define: Option>, - - /// Inject Plugin - #[napi(ts_type = "Record")] - pub inject: Option>>>, -} - -impl TryFrom for oxc_transformer::TransformOptions { - type Error = String; - - fn try_from(options: TransformOptions) -> Result { - let env = match options.target { - Some(Either::A(s)) => EnvOptions::from_target(&s)?, - Some(Either::B(list)) => EnvOptions::from_target_list(&list)?, - _ => EnvOptions::default(), - }; - Ok(Self { - cwd: options.cwd.map(PathBuf::from).unwrap_or_default(), - assumptions: options.assumptions.map(Into::into).unwrap_or_default(), - typescript: options - .typescript - .map(oxc_transformer::TypeScriptOptions::from) - .unwrap_or_default(), - jsx: options.jsx.map(Into::into).unwrap_or_default(), - env, - ..Self::default() - }) - } -} - -#[napi(object)] -#[derive(Default, Debug)] -pub struct CompilerAssumptions { - pub ignore_function_length: Option, - pub no_document_all: Option, - pub object_rest_no_symbols: Option, - pub pure_getters: Option, - pub set_public_class_fields: Option, -} - -impl From for oxc_transformer::CompilerAssumptions { - fn from(value: CompilerAssumptions) -> Self { - let ops = oxc_transformer::CompilerAssumptions::default(); - Self { - ignore_function_length: value - .ignore_function_length - .unwrap_or(ops.ignore_function_length), - no_document_all: value.no_document_all.unwrap_or(ops.no_document_all), - object_rest_no_symbols: value - .object_rest_no_symbols - .unwrap_or(ops.object_rest_no_symbols), - pure_getters: value.pure_getters.unwrap_or(ops.pure_getters), - set_public_class_fields: value - .set_public_class_fields - .unwrap_or(ops.set_public_class_fields), - ..ops - } - } -} - -#[napi(object)] -#[derive(Default)] -pub struct TypeScriptOptions { - pub jsx_pragma: Option, - pub jsx_pragma_frag: Option, - pub only_remove_type_imports: Option, - pub allow_namespaces: Option, - pub allow_declare_fields: Option, - /// Also generate a `.d.ts` declaration file for TypeScript files. - /// - /// The source file must be compliant with all - /// [`isolatedDeclarations`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html#isolated-declarations) - /// requirements. - /// - /// @default false - pub declaration: Option, - /// Rewrite or remove TypeScript import/export declaration extensions. - /// - /// - When set to `rewrite`, it will change `.ts`, `.mts`, `.cts` extensions to `.js`, `.mjs`, `.cjs` respectively. - /// - When set to `remove`, it will remove `.ts`/`.mts`/`.cts`/`.tsx` extension entirely. - /// - When set to `true`, it's equivalent to `rewrite`. - /// - When set to `false` or omitted, no changes will be made to the extensions. - /// - /// @default false - #[napi(ts_type = "'rewrite' | 'remove' | boolean")] - pub rewrite_import_extensions: Option>, -} - -impl From for oxc_transformer::TypeScriptOptions { - fn from(options: TypeScriptOptions) -> Self { - let ops = oxc_transformer::TypeScriptOptions::default(); - oxc_transformer::TypeScriptOptions { - jsx_pragma: options.jsx_pragma.map(Into::into).unwrap_or(ops.jsx_pragma), - jsx_pragma_frag: options.jsx_pragma_frag.map(Into::into).unwrap_or(ops.jsx_pragma_frag), - only_remove_type_imports: options - .only_remove_type_imports - .unwrap_or(ops.only_remove_type_imports), - allow_namespaces: options.allow_namespaces.unwrap_or(ops.allow_namespaces), - allow_declare_fields: options.allow_declare_fields.unwrap_or(ops.allow_declare_fields), - optimize_const_enums: false, - rewrite_import_extensions: options.rewrite_import_extensions.and_then(|value| { - match value { - Either::A(v) => { - if v { - Some(RewriteExtensionsMode::Rewrite) - } else { - None - } - } - Either::B(v) => match v.as_str() { - "rewrite" => Some(RewriteExtensionsMode::Rewrite), - "remove" => Some(RewriteExtensionsMode::Remove), - _ => None, - }, - } - }), - } - } -} - -/// Configure how TSX and JSX are transformed. -/// -/// @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx#options} -#[napi(object)] -pub struct JsxOptions { - /// Decides which runtime to use. - /// - /// - 'automatic' - auto-import the correct JSX factories - /// - 'classic' - no auto-import - /// - /// @default 'automatic' - #[napi(ts_type = "'classic' | 'automatic'")] - pub runtime: Option, - - /// Emit development-specific information, such as `__source` and `__self`. - /// - /// @default false - /// - /// @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx-development} - pub development: Option, - - /// Toggles whether or not to throw an error if an XML namespaced tag name - /// is used. - /// - /// Though the JSX spec allows this, it is disabled by default since React's - /// JSX does not currently have support for it. - /// - /// @default true - pub throw_if_namespace: Option, - - /// Enables `@babel/plugin-transform-react-pure-annotations`. - /// - /// It will mark top-level React method calls as pure for tree shaking. - /// - /// @see {@link https://babeljs.io/docs/en/babel-plugin-transform-react-pure-annotations} - /// - /// @default true - pub pure: Option, - - /// Replaces the import source when importing functions. - /// - /// @default 'react' - pub import_source: Option, - - /// Replace the function used when compiling JSX expressions. It should be a - /// qualified name (e.g. `React.createElement`) or an identifier (e.g. - /// `createElement`). - /// - /// Only used for `classic` {@link runtime}. - /// - /// @default 'React.createElement' - pub pragma: Option, - - /// Replace the component used when compiling JSX fragments. It should be a - /// valid JSX tag name. - /// - /// Only used for `classic` {@link runtime}. - /// - /// @default 'React.Fragment' - pub pragma_frag: Option, - - /// When spreading props, use `Object.assign` directly instead of an extend helper. - /// - /// Only used for `classic` {@link runtime}. - /// - /// @default false - pub use_built_ins: Option, - - /// When spreading props, use inline object with spread elements directly - /// instead of an extend helper or Object.assign. - /// - /// Only used for `classic` {@link runtime}. - /// - /// @default false - pub use_spread: Option, - - /// Enable React Fast Refresh . - /// - /// Conforms to the implementation in {@link https://github.com/facebook/react/tree/v18.3.1/packages/react-refresh} - /// - /// @default false - pub refresh: Option>, -} - -impl From for oxc_transformer::JsxOptions { - fn from(options: JsxOptions) -> Self { - let ops = oxc_transformer::JsxOptions::default(); - oxc_transformer::JsxOptions { - runtime: match options.runtime.as_deref() { - Some("classic") => JsxRuntime::Classic, - /* "automatic" */ _ => JsxRuntime::Automatic, - }, - development: options.development.unwrap_or(ops.development), - throw_if_namespace: options.throw_if_namespace.unwrap_or(ops.throw_if_namespace), - pure: options.pure.unwrap_or(ops.pure), - import_source: options.import_source, - pragma: options.pragma, - pragma_frag: options.pragma_frag, - use_built_ins: options.use_built_ins, - use_spread: options.use_spread, - refresh: options.refresh.and_then(|value| match value { - Either::A(b) => b.then(oxc_transformer::ReactRefreshOptions::default), - Either::B(options) => Some(oxc_transformer::ReactRefreshOptions::from(options)), - }), - ..Default::default() - } - } -} - -#[napi(object)] -pub struct ReactRefreshOptions { - /// Specify the identifier of the refresh registration variable. - /// - /// @default `$RefreshReg$`. - pub refresh_reg: Option, - - /// Specify the identifier of the refresh signature variable. - /// - /// @default `$RefreshSig$`. - pub refresh_sig: Option, - - pub emit_full_signatures: Option, -} - -impl From for oxc_transformer::ReactRefreshOptions { - fn from(options: ReactRefreshOptions) -> Self { - let ops = oxc_transformer::ReactRefreshOptions::default(); - oxc_transformer::ReactRefreshOptions { - refresh_reg: options.refresh_reg.unwrap_or(ops.refresh_reg), - refresh_sig: options.refresh_sig.unwrap_or(ops.refresh_sig), - emit_full_signatures: options.emit_full_signatures.unwrap_or(ops.emit_full_signatures), - } - } -} - -#[napi(object)] -pub struct ArrowFunctionsOptions { - /// This option enables the following: - /// * Wrap the generated function in .bind(this) and keeps uses of this inside the function as-is, instead of using a renamed this. - /// * Add a runtime check to ensure the functions are not instantiated. - /// * Add names to arrow functions. - /// - /// @default false - pub spec: Option, -} - -impl From for oxc_transformer::ArrowFunctionsOptions { - fn from(options: ArrowFunctionsOptions) -> Self { - oxc_transformer::ArrowFunctionsOptions { spec: options.spec.unwrap_or_default() } - } -} - -#[napi(object)] -pub struct Es2015Options { - /// Transform arrow functions into function expressions. - pub arrow_function: Option, -} - -impl From for oxc_transformer::ES2015Options { - fn from(options: Es2015Options) -> Self { - oxc_transformer::ES2015Options { arrow_function: options.arrow_function.map(Into::into) } - } -} diff --git a/crates/oxc_parser/CHANGELOG.md b/crates/oxc_parser/CHANGELOG.md index 1e5db53aff885e..7f287cf33947e9 100644 --- a/crates/oxc_parser/CHANGELOG.md +++ b/crates/oxc_parser/CHANGELOG.md @@ -4,6 +4,26 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +- c2ced15 parser,linter: [**BREAKING**] Use a different `ModuleRecord` for linter (#7554) (Boshen) + +- 8a788b8 parser: [**BREAKING**] Build `ModuleRecord` directly in parser (#7546) (Boshen) + +- b0e1c03 ast: [**BREAKING**] Add `StringLiteral::raw` field (#7393) (Boshen) + +### Features + +- 33e5a49 syntax: Add statement span to `ImportEntry` and `ExportEntry` (#7583) (Boshen) + +### Refactor + +- b24beeb parser: Use `PropName` trait from `oxc_ecmascript` (#7543) (Boshen) +- f0e7acc syntax: Change `ModuleRecord::not_esm` to `has_module_syntax` (#7579) (Boshen) +- 18519de syntax: Remove `ModuleRecord::export_default` (#7578) (Boshen) +- d476660 syntax: Remove `ModuleRecord::exported_bindings_duplicated` because it is a syntax error (#7577) (Boshen) +- 17663f5 syntax: Remove `ModuleRecord::export_default_duplicated` because it is a syntax error (#7576) (Boshen) + ## [0.37.0] - 2024-11-21 - f059b0e ast: [**BREAKING**] Add missing `ChainExpression` from `TSNonNullExpression` (#7377) (Boshen) diff --git a/crates/oxc_parser/Cargo.toml b/crates/oxc_parser/Cargo.toml index 55cc1ce6f8c1bb..848274eeec5c9b 100644 --- a/crates/oxc_parser/Cargo.toml +++ b/crates/oxc_parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_parser" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index 713ceb1d9192c8..88101e35b9de62 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -170,8 +170,6 @@ impl<'a> ParserImpl<'a> { match self.cur_kind() { Kind::Ident => self.parse_identifier_expression(), // fast path, keywords are checked at the end - // Literal, RegularExpressionLiteral - kind if kind.is_literal() => self.parse_literal_expression(), // ArrayLiteral Kind::LBrack => self.parse_array_expression(), // ObjectLiteral @@ -199,6 +197,8 @@ impl<'a> ParserImpl<'a> { Kind::Slash | Kind::SlashEq => self .parse_literal_regexp() .map(|literal| Expression::RegExpLiteral(self.alloc(literal))), + // Literal, RegularExpressionLiteral + kind if kind.is_literal() => self.parse_literal_expression(), // JSXElement, JSXFragment Kind::LAngle if self.source_type.is_jsx() => self.parse_jsx_expression(), _ => self.parse_identifier_expression(), @@ -566,7 +566,7 @@ impl<'a> ParserImpl<'a> { self.bump_any(); // bump `.` let property = match self.cur_kind() { Kind::Meta => { - self.set_source_type_to_module_if_unambiguous(); + self.module_record_builder.visit_import_meta(); self.parse_keyword_identifier(Kind::Meta) } Kind::Target => self.parse_keyword_identifier(Kind::Target), @@ -1136,11 +1136,10 @@ impl<'a> ParserImpl<'a> { /// await `UnaryExpression`[?Yield, +Await] fn parse_await_expression(&mut self, lhs_span: Span) -> Result> { let span = self.start_span(); - self.bump_any(); - let has_await = self.ctx.has_await(); - if !has_await { - self.error(diagnostics::await_expression(Span::new(span.start, span.start + 5))); + if !self.ctx.has_await() { + self.error(diagnostics::await_expression(self.cur_token().span())); } + self.bump_any(); let argument = self.context(Context::Await, Context::empty(), |p| { p.parse_simple_unary_expression(lhs_span) })?; diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index 82ada7b37b441f..5e584677432b99 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -187,7 +187,6 @@ impl<'a> ParserImpl<'a> { self.expect(Kind::Eq)?; let expression = self.parse_assignment_expression_or_higher()?; self.asi()?; - self.set_source_type_to_module_if_unambiguous(); Ok(self.ast.alloc_ts_export_assignment(self.end_span(start_span), expression)) } diff --git a/crates/oxc_parser/src/js/statement.rs b/crates/oxc_parser/src/js/statement.rs index 0d933bf2ece765..4add19cd8203c5 100644 --- a/crates/oxc_parser/src/js/statement.rs +++ b/crates/oxc_parser/src/js/statement.rs @@ -44,7 +44,6 @@ impl<'a> ParserImpl<'a> { if is_top_level { if let Some(module_decl) = stmt.as_module_declaration() { - self.set_source_type_to_module_if_unambiguous(); self.module_record_builder.visit_module_declaration(module_decl); } } @@ -87,6 +86,10 @@ impl<'a> ParserImpl<'a> { self.eat_decorators()?; } + // For performance reasons, match orders are: + // 1. plain if check + // 2. check current token + // 3. peek token match self.cur_kind() { Kind::LCurly => self.parse_block_statement(), Kind::Semicolon => Ok(self.parse_empty_statement()), @@ -101,24 +104,28 @@ impl<'a> ParserImpl<'a> { Kind::Try => self.parse_try_statement(), Kind::Debugger => self.parse_debugger_statement(), Kind::Class => self.parse_class_statement(stmt_ctx, start_span), - Kind::Import if !matches!(self.peek_kind(), Kind::Dot | Kind::LParen) => { - self.parse_import_declaration() - } Kind::Export => self.parse_export_declaration(), // [+Return] ReturnStatement[?Yield, ?Await] Kind::Return => self.parse_return_statement(), Kind::Var => self.parse_variable_statement(stmt_ctx), + // Fast path + Kind::Function => self.parse_function_declaration(stmt_ctx), + Kind::Let if !self.cur_token().escaped() => self.parse_let(stmt_ctx), + Kind::Import if !matches!(self.peek_kind(), Kind::Dot | Kind::LParen) => { + self.parse_import_declaration() + } Kind::Const if !(self.is_ts && self.is_at_enum_declaration()) => { self.parse_variable_statement(stmt_ctx) } - Kind::Let if !self.cur_token().escaped() => self.parse_let(stmt_ctx), Kind::Await if self.peek_kind() == Kind::Using && self.nth_kind(2).is_binding_identifier() => { self.parse_using() } Kind::Using if self.peek_kind().is_binding_identifier() => self.parse_using(), - _ if self.at_function_with_async() => self.parse_function_declaration(stmt_ctx), + Kind::Async if self.peek_at(Kind::Function) && !self.peek_token().is_on_new_line => { + self.parse_function_declaration(stmt_ctx) + } _ if self.is_ts && self.at_start_of_ts_declaration() => { self.parse_ts_declaration_statement(start_span) } @@ -234,7 +241,15 @@ impl<'a> ParserImpl<'a> { self.bump_any(); // bump `for` // [+Await] - let r#await = self.ctx.has_await() && self.eat(Kind::Await); + let r#await = if self.at(Kind::Await) { + if !self.ctx.has_await() { + self.error(diagnostics::await_expression(self.cur_token().span())); + } + self.bump_any(); + true + } else { + false + }; self.expect(Kind::LParen)?; diff --git a/crates/oxc_parser/src/lexer/kind.rs b/crates/oxc_parser/src/lexer/kind.rs index c80a487113a6f1..3b6eac6f8e1246 100644 --- a/crates/oxc_parser/src/lexer/kind.rs +++ b/crates/oxc_parser/src/lexer/kind.rs @@ -198,10 +198,12 @@ pub enum Kind { use self::Kind::*; impl Kind { + #[inline] pub fn is_eof(self) -> bool { matches!(self, Eof) } + #[inline] pub fn is_number(self) -> bool { matches!( self, @@ -222,27 +224,32 @@ impl Kind { /// [Identifiers](https://tc39.es/ecma262/#sec-identifiers) /// `IdentifierReference` + #[inline] pub fn is_identifier_reference(self, r#yield: bool, r#await: bool) -> bool { self.is_identifier() || (!r#yield && self == Yield) || (!r#await && self == Await) } /// `BindingIdentifier` + #[inline] pub fn is_binding_identifier(self) -> bool { self.is_identifier() || matches!(self, Yield | Await) } /// `LabelIdentifier` + #[inline] pub fn is_label_identifier(self, r#yield: bool, r#await: bool) -> bool { self.is_identifier() || (!r#yield && self == Yield) || (!r#await && self == Await) } /// Identifier /// `IdentifierName` but not `ReservedWord` + #[inline] pub fn is_identifier(self) -> bool { self.is_identifier_name() && !self.is_reserved_keyword() } /// `IdentifierName` + #[inline] pub fn is_identifier_name(self) -> bool { matches!(self, Ident) || self.is_all_keyword() } @@ -252,6 +259,7 @@ impl Kind { /// ```javascript /// let { a, b } = c, let [a, b] = c, let ident /// ``` + #[inline] pub fn is_after_let(self) -> bool { self != Self::In && (matches!(self, LCurly | LBrack | Ident) || self.is_all_keyword()) } @@ -262,10 +270,12 @@ impl Kind { /// `BooleanLiteral` /// `NumericLiteral` /// `StringLiteral` + #[inline] pub fn is_literal(self) -> bool { matches!(self, Null | True | False | Str | RegExp) || self.is_number() } + #[inline] pub fn is_after_await_or_yield(self) -> bool { !self.is_binary_operator() && (self.is_literal() || self.is_identifier_name()) } @@ -275,16 +285,19 @@ impl Kind { /// `IdentifierName` /// `StringLiteral` /// `NumericLiteral` + #[inline] pub fn is_literal_property_name(self) -> bool { self.is_identifier_name() || self == Str || self.is_number() } + #[inline] pub fn is_identifier_or_keyword(self) -> bool { self.is_literal_property_name() || matches!(self, Self::PrivateIdentifier) || self.is_all_keyword() } + #[inline] pub fn is_variable_declaration(self) -> bool { matches!(self, Var | Let | Const) } @@ -296,11 +309,13 @@ impl Kind { /// `PropertyName`[Yield, Await] : /// `LiteralPropertyName` /// `ComputedPropertyName`[?Yield, ?Await] + #[inline] pub fn is_class_element_name_start(self) -> bool { self.is_literal_property_name() || matches!(self, LBrack | PrivateIdentifier) } #[rustfmt::skip] + #[inline] pub fn is_assignment_operator(self) -> bool { matches!(self, Eq | PlusEq | MinusEq | StarEq | SlashEq | PercentEq | ShiftLeftEq | ShiftRightEq | ShiftRight3Eq | Pipe2Eq | Amp2Eq | PipeEq | CaretEq | AmpEq | Question2Eq @@ -308,25 +323,30 @@ impl Kind { } #[rustfmt::skip] + #[inline] pub fn is_binary_operator(self) -> bool { matches!(self, Eq2 | Neq | Eq3 | Neq2 | LAngle | LtEq | RAngle | GtEq | ShiftLeft | ShiftRight | ShiftRight3 | Plus | Minus | Star | Slash | Percent | Pipe | Caret | Amp | In | Instanceof | Star2) } + #[inline] pub fn is_logical_operator(self) -> bool { matches!(self, Pipe2 | Amp2 | Question2) } + #[inline] pub fn is_unary_operator(self) -> bool { matches!(self, Minus | Plus | Bang | Tilde | Typeof | Void | Delete) } + #[inline] pub fn is_update_operator(self) -> bool { matches!(self, Plus2 | Minus2) } /// [Keywords and Reserved Words](https://tc39.es/ecma262/#sec-keywords-and-reserved-words) + #[inline] pub fn is_all_keyword(self) -> bool { self.is_reserved_keyword() || self.is_contextual_keyword() @@ -335,6 +355,7 @@ impl Kind { } #[rustfmt::skip] + #[inline] pub fn is_reserved_keyword(self) -> bool { matches!(self, Await | Break | Case | Catch | Class | Const | Continue | Debugger | Default | Delete | Do | Else | Enum | Export | Extends | False | Finally | For | Function | If @@ -343,11 +364,13 @@ impl Kind { } #[rustfmt::skip] + #[inline] pub fn is_strict_mode_contextual_keyword(self) -> bool { matches!(self, Let | Static | Implements | Interface | Package | Private | Protected | Public) } #[rustfmt::skip] + #[inline] pub fn is_contextual_keyword(self) -> bool { matches!(self, Async | From | Get | Meta | Of | Set | Target | Accessor | Abstract | As | Asserts | Assert | Any | Boolean | Constructor | Declare | Infer | Intrinsic | Is | KeyOf | Module @@ -356,20 +379,24 @@ impl Kind { } #[rustfmt::skip] + #[inline] pub fn is_future_reserved_keyword(self) -> bool { matches!(self, Implements | Interface | Package | Private | Protected | Public | Static) } + #[inline] pub fn is_template_start_of_tagged_template(self) -> bool { matches!(self, NoSubstitutionTemplate | TemplateHead) } #[rustfmt::skip] + #[inline] pub fn is_modifier_kind(self) -> bool { matches!(self, Abstract | Accessor | Async | Const | Declare | Default | Export | In | Out | Public | Private | Protected | Readonly | Static | Override) } + #[inline] pub fn is_binding_identifier_or_private_identifier_or_pattern(self) -> bool { matches!(self, LCurly | LBrack | PrivateIdentifier) || self.is_binding_identifier() } diff --git a/crates/oxc_parser/src/lib.rs b/crates/oxc_parser/src/lib.rs index b3658dc723dde3..e5349ac6bbc8f5 100644 --- a/crates/oxc_parser/src/lib.rs +++ b/crates/oxc_parser/src/lib.rs @@ -411,7 +411,7 @@ impl<'a> ParserImpl<'a> { /// Recoverable errors are stored inside `errors`. #[inline] pub fn parse(mut self) -> ParserReturn<'a> { - let (program, panicked) = match self.parse_program() { + let (mut program, panicked) = match self.parse_program() { Ok(program) => (program, false), Err(error) => { self.error(self.overlong_error().unwrap_or(error)); @@ -450,6 +450,16 @@ impl<'a> ParserImpl<'a> { } let irregular_whitespaces = self.lexer.trivia_builder.irregular_whitespaces.into_boxed_slice(); + + let source_type = program.source_type; + if source_type.is_unambiguous() { + program.source_type = if module_record.has_module_syntax { + source_type.with_module(true) + } else { + source_type.with_script(true) + }; + } + ParserReturn { program, module_record, @@ -481,8 +491,6 @@ impl<'a> ParserImpl<'a> { let (directives, statements) = self.parse_directives_and_statements(/* is_top_level */ true)?; - self.set_source_type_to_script_if_unambiguous(); - let span = Span::new(0, self.source_text.len() as u32); let comments = self.ast.vec_from_iter(self.lexer.trivia_builder.comments.iter().copied()); Ok(self.ast.program( @@ -564,18 +572,6 @@ impl<'a> ParserImpl<'a> { self.errors.len() + self.lexer.errors.len() } - fn set_source_type_to_module_if_unambiguous(&mut self) { - if self.source_type.is_unambiguous() { - self.source_type = self.source_type.with_module(true); - } - } - - fn set_source_type_to_script_if_unambiguous(&mut self) { - if self.source_type.is_unambiguous() { - self.source_type = self.source_type.with_script(true); - } - } - #[inline] fn alloc(&self, value: T) -> ArenaBox<'a, T> { self.ast.alloc(value) diff --git a/crates/oxc_parser/src/module_record.rs b/crates/oxc_parser/src/module_record.rs index bfb0eb4dafb59d..5b295244a321a6 100644 --- a/crates/oxc_parser/src/module_record.rs +++ b/crates/oxc_parser/src/module_record.rs @@ -173,6 +173,10 @@ impl<'a> ModuleRecordBuilder<'a> { } } + pub fn visit_import_meta(&mut self) { + self.module_record.has_module_syntax = true; + } + pub fn visit_module_declaration(&mut self, module_decl: &ModuleDeclaration<'a>) { self.module_record.has_module_syntax = true; match module_decl { diff --git a/crates/oxc_prettier/src/comments/print.rs b/crates/oxc_prettier/src/comments/print.rs index a8f5cb5025b2d1..317f70c7d1c116 100644 --- a/crates/oxc_prettier/src/comments/print.rs +++ b/crates/oxc_prettier/src/comments/print.rs @@ -1,10 +1,7 @@ use oxc_allocator::Vec; use oxc_span::Span; -use crate::{ - ir::{Doc, DocBuilder}, - Prettier, -}; +use crate::{ir::Doc, Prettier}; use super::{CommentFlags, DanglingCommentsPrintOptions}; @@ -17,7 +14,7 @@ impl<'a> Prettier<'a> { after: Option>, ) -> Doc<'a> { if before.is_some() || after.is_some() { - let mut parts = self.vec(); + let mut parts = Vec::new_in(self.allocator); if let Some(doc) = before { parts.push(doc); } @@ -46,7 +43,7 @@ impl<'a> Prettier<'a> { #[must_use] pub(crate) fn print_inner_comment(&mut self, _span: Span) -> Vec<'a, Doc<'a>> { - self.vec() + Vec::new_in(self.allocator) } #[must_use] diff --git a/crates/oxc_prettier/src/format/array.rs b/crates/oxc_prettier/src/format/array.rs index 77fb6b2bcfe827..e56a5a6c2f94c4 100644 --- a/crates/oxc_prettier/src/format/array.rs +++ b/crates/oxc_prettier/src/format/array.rs @@ -1,11 +1,14 @@ +use oxc_allocator::Vec; use oxc_ast::ast::*; use oxc_span::{GetSpan, Span}; use oxc_syntax::operator::UnaryOperator; use crate::{ + array, comments::{CommentFlags, DanglingCommentsPrintOptions}, - ir::{Doc, DocBuilder}, - p_vec, Format, Prettier, + fill, group, hardline, if_break, indent, + ir::Doc, + line, softline, text, Format, Prettier, }; #[allow(clippy::enum_variant_names)] @@ -80,32 +83,32 @@ pub fn print_array<'a>(p: &mut Prettier<'a>, arr: &Array<'a, '_>) -> Doc<'a> { let trailing_comma_fn = |p: &Prettier<'a>| { if !can_have_trailing_comma { - p.text("") + text!("") } else if needs_forced_trailing_comma { - p.text(",") + text!(",") } else if should_use_concise_formatting { - p.if_break(p.text(","), p.text(""), Some(id)) + if_break!(p, text!(","), text!(""), Some(id)) } else { - p.if_break(p.text(","), p.text(""), None) + if_break!(p, text!(",")) } }; - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let group = { - let mut group = p.vec(); - group.push(p.text("[")); + let mut group = Vec::new_in(p.allocator); + group.push(text!("[")); let indent_parts = { - let mut indent_parts = p.vec(); - indent_parts.push(p.softline()); + let mut indent_parts = Vec::new_in(p.allocator); + indent_parts.push(softline!()); indent_parts.push(if should_use_concise_formatting { print_array_elements_concisely(p, arr, trailing_comma_fn) } else { let trailing_comma = trailing_comma_fn(p); let elements = print_array_elements(p, arr); - p.array(p_vec!(p, elements, trailing_comma)) + array!(p, [elements, trailing_comma]) }); if let Some(dangling_comments) = p.print_dangling_comments(arr.span(), None) { indent_parts.push(dangling_comments); @@ -113,39 +116,37 @@ pub fn print_array<'a>(p: &mut Prettier<'a>, arr: &Array<'a, '_>) -> Doc<'a> { indent_parts }; - group.push(p.indent(indent_parts)); - group.push(p.softline()); - group.push(p.text("]")); + group.push(indent!(p, indent_parts)); + group.push(softline!()); + group.push(text!("]")); - p.array(group) + group }; - parts.push(p.group_with_opts(group, should_break(arr), Some(id))); + parts.push(group!(p, group, should_break(arr), Some(id))); - p.array(parts) + array!(p, parts) } fn print_empty_array_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { let dangling_options = DanglingCommentsPrintOptions::default().with_ident(true); p.print_dangling_comments(array.span(), Some(&dangling_options)).map_or_else( - || p.text("[]"), - |dangling_comments| { - p.group(p.array(p_vec!(p, p.text("["), dangling_comments, p.softline(), p.text("]")))) - }, + || text!("[]"), + |dangling_comments| group!(p, [text!("["), dangling_comments, softline!(), text!("]")]), ) } fn print_array_elements<'a>(p: &mut Prettier<'a>, arr: &Array<'a, '_>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); match arr { Array::ArrayExpression(array) => { for (i, element) in array.elements.iter().enumerate() { parts.push(element.format(p)); let is_last = i == array.elements.len() - 1; if !is_last { - parts.push(p.text(",")); - parts.push(p.line()); + parts.push(text!(",")); + parts.push(line!()); if !element.is_elision() && is_line_after_element_empty(p, element.span().end) { - parts.push(p.softline()); + parts.push(softline!()); } } } @@ -153,8 +154,8 @@ fn print_array_elements<'a>(p: &mut Prettier<'a>, arr: &Array<'a, '_>) -> Doc<'a Array::TSTupleType(tuple) => { for (i, element) in tuple.element_types.iter().enumerate() { if i > 0 && i < tuple.element_types.len() { - parts.push(p.text(",")); - parts.push(p.line()); + parts.push(text!(",")); + parts.push(line!()); } parts.push(element.format(p)); @@ -166,24 +167,24 @@ fn print_array_elements<'a>(p: &mut Prettier<'a>, arr: &Array<'a, '_>) -> Doc<'a for (i, element) in array_pat.elements.iter().enumerate() { if let Some(binding_pat) = element { let binding_pat_doc = binding_pat.format(p); - parts.push(p.group(binding_pat_doc)); + parts.push(group!(p, [binding_pat_doc])); } if i == len - 1 && !has_rest { break; } - parts.push(p.text(",")); - parts.push(p.line()); + parts.push(text!(",")); + parts.push(line!()); } if let Some(rest) = &array_pat.rest { let rest_doc = rest.format(p); - parts.push(p.group(rest_doc)); + parts.push(group!(p, [rest_doc])); } } Array::ArrayAssignmentTarget(array_pat) => { for (i, element) in array_pat.elements.iter().enumerate() { if i > 0 && i < array_pat.elements.len() { - parts.push(p.text(",")); - parts.push(p.line()); + parts.push(text!(",")); + parts.push(line!()); } if let Some(binding_pat) = element { @@ -192,14 +193,14 @@ fn print_array_elements<'a>(p: &mut Prettier<'a>, arr: &Array<'a, '_>) -> Doc<'a } if let Some(rest) = &array_pat.rest { - parts.push(p.text(",")); - parts.push(p.line()); + parts.push(text!(",")); + parts.push(line!()); parts.push(rest.format(p)); } } } - p.array(parts) + array!(p, parts) } fn print_array_elements_concisely<'a, F>( @@ -210,42 +211,42 @@ fn print_array_elements_concisely<'a, F>( where F: Fn(&Prettier<'a>) -> Doc<'a>, { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if let Array::ArrayExpression(arr) = arr { for (i, element) in arr.elements.iter().enumerate() { let is_last = i == arr.elements.len() - 1; let element_doc = element.format(p); let part = if is_last { - p.array(p_vec!(p, element_doc, trailing_comma_fn(p))) + array!(p, [element_doc, trailing_comma_fn(p)]) } else { - p.array(p_vec!(p, element_doc, p.text(","))) + array!(p, [element_doc, text!(",")]) }; parts.push(part); if !is_last { if is_line_after_element_empty(p, element.span().end) { - let mut space_parts = p.vec(); - space_parts.extend(p.hardline()); - space_parts.extend(p.hardline()); - parts.push(p.array(space_parts)); + let mut space_parts = Vec::new_in(p.allocator); + space_parts.extend(hardline!()); + space_parts.extend(hardline!()); + parts.push(array!(p, space_parts)); } else if arr.elements.get(i + 1).is_some_and(|next| { p.has_comment(next.span(), CommentFlags::Leading | CommentFlags::Line) }) { - let mut space_parts = p.vec(); - space_parts.extend(p.hardline()); - parts.push(p.array(space_parts)); + let mut space_parts = Vec::new_in(p.allocator); + space_parts.extend(hardline!()); + parts.push(array!(p, space_parts)); } else { - parts.push(p.line()); + parts.push(line!()); } } } } else { // TODO: implement let elements = print_array_elements(p, arr); - p.array(p_vec!(p, elements, trailing_comma_fn(p))); + array!(p, [elements, trailing_comma_fn(p)]); } - p.fill(parts) + fill!(p, parts) } fn should_break(array: &Array) -> bool { diff --git a/crates/oxc_prettier/src/format/arrow_function.rs b/crates/oxc_prettier/src/format/arrow_function.rs index a2672875b584cd..62bc0e66c4d921 100644 --- a/crates/oxc_prettier/src/format/arrow_function.rs +++ b/crates/oxc_prettier/src/format/arrow_function.rs @@ -1,22 +1,20 @@ +use oxc_allocator::Vec; use oxc_ast::ast::*; -use crate::{ - ir::{Doc, DocBuilder}, - Format, Prettier, -}; +use crate::{array, group, ir::Doc, text, Format, Prettier}; pub(super) fn print_arrow_function<'a>( p: &mut Prettier<'a>, expr: &ArrowFunctionExpression<'a>, ) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if !p.options.semi && p.options.arrow_parens.is_always() { - parts.push(p.text(";")); + parts.push(text!(";")); } if expr.r#async { - parts.push(p.text("async ")); + parts.push(text!("async ")); } if let Some(type_params) = &expr.type_parameters { @@ -24,14 +22,14 @@ pub(super) fn print_arrow_function<'a>( } let params_doc = expr.params.format(p); - parts.push(p.group(params_doc)); + parts.push(group!(p, [params_doc])); if let Some(return_type) = &expr.return_type { - parts.push(p.text(": ")); + parts.push(text!(": ")); parts.push(return_type.type_annotation.format(p)); } - parts.push(p.text(" => ")); + parts.push(text!(" => ")); if expr.expression { let stmt = &expr.body.statements[0]; @@ -45,5 +43,5 @@ pub(super) fn print_arrow_function<'a>( parts.push(expr.body.format(p)); } - p.array(parts) + array!(p, parts) } diff --git a/crates/oxc_prettier/src/format/assignment.rs b/crates/oxc_prettier/src/format/assignment.rs index 29d494d58e699c..bac328f243996e 100644 --- a/crates/oxc_prettier/src/format/assignment.rs +++ b/crates/oxc_prettier/src/format/assignment.rs @@ -1,3 +1,4 @@ +use oxc_allocator::Vec; use oxc_ast::{ ast::{ match_member_expression, AccessorProperty, Argument, AssignmentExpression, @@ -9,9 +10,11 @@ use oxc_ast::{ }; use crate::{ + array, format::{binaryish::should_inline_logical_expression, class::ClassMemberish}, - ir::{Doc, DocBuilder}, - p_vec, Format, Prettier, + group, indent, indent_if_break, + ir::Doc, + line, text, Format, Prettier, }; pub(super) fn print_assignment_expression<'a>( @@ -23,7 +26,7 @@ pub(super) fn print_assignment_expression<'a>( p, AssignmentLikeNode::AssignmentExpression(assignment_expr), left_doc, - p.array(p_vec!(p, p.space(), p.text(assignment_expr.operator.as_str()))), + array!(p, [text!(" "), text!(assignment_expr.operator.as_str())]), Some(&assignment_expr.right), ) } @@ -37,7 +40,7 @@ pub(super) fn print_variable_declarator<'a>( p, AssignmentLikeNode::VariableDeclarator(variable_declarator), left_doc, - p.text(" ="), + text!(" ="), variable_declarator.init.as_ref(), ) } @@ -74,42 +77,37 @@ pub(super) fn print_assignment<'a>( let layout = choose_layout(p, &node, &left_doc, right_expr); // TODO: set the layout in options so that when we print the right-hand side, we can refer to it. - let right_doc = if let Some(expr) = right_expr { expr.format(p) } else { p.array(p.vec()) }; + let right_doc = if let Some(expr) = right_expr { expr.format(p) } else { array!(p, []) }; match layout { - Layout::BreakAfterOperator => p.group(p.array(p_vec!( - p, - p.group(left_doc), - op, - p.group(p.indent(p_vec!(p, p.line(), right_doc))) - ))), + Layout::BreakAfterOperator => { + group!(p, [group!(p, [left_doc]), op, group!(p, [indent!(p, [line!(), right_doc])])]) + } Layout::NeverBreakAfterOperator => { - p.group(p.array(p_vec!(p, p.group(left_doc), op, p.space(), p.group(right_doc)))) + group!(p, [group!(p, [left_doc]), op, text!(" "), group!(p, [right_doc])]) } // First break right-hand side, then after operator Layout::Fluid => { let group_id = p.next_id(); let after_op = { - let mut parts = p.vec(); - parts.push(p.indent(p_vec!(p, p.line()))); - p.group_with_opts(p.array(parts), false, Some(group_id)) + let mut parts = Vec::new_in(p.allocator); + parts.push(indent!(p, [line!()])); + group!(p, parts, false, Some(group_id)) }; - let right_doc = { p.indent_if_break(p.group(right_doc), group_id) }; + let right_doc = { indent_if_break!(p, group!(p, [right_doc]), group_id) }; - p.group(p.array(p_vec!(p, p.group(left_doc), op, after_op, right_doc))) - } - Layout::BreakLhs => { - p.group(p.array(p_vec!(p, left_doc, op, p.space(), p.group(right_doc)))) + group!(p, [group!(p, [left_doc]), op, after_op, right_doc]) } + Layout::BreakLhs => group!(p, [left_doc, op, text!(" "), group!(p, [right_doc])]), // Parts of assignment chains aren't wrapped in groups. // Once one of them breaks, the chain breaks too. - Layout::Chain => p.array(p_vec!(p, p.group(left_doc), op, p.line(), right_doc)), + Layout::Chain => array!(p, [group!(p, [left_doc]), op, line!(), right_doc]), Layout::ChainTail => { - p.array(p_vec!(p, p.group(left_doc), op, p.indent(p_vec!(p, p.line(), right_doc)))) + array!(p, [group!(p, [left_doc]), op, indent!(p, [line!(), right_doc])]) } - Layout::ChainTailArrowChain => p.array(p_vec!(p, p.group(left_doc), op, right_doc)), + Layout::ChainTailArrowChain => array!(p, [group!(p, [left_doc]), op, right_doc]), Layout::OnlyLeft => left_doc, } } diff --git a/crates/oxc_prettier/src/format/binaryish.rs b/crates/oxc_prettier/src/format/binaryish.rs index d4d7bcd9c65582..63ba25f6cef6e0 100644 --- a/crates/oxc_prettier/src/format/binaryish.rs +++ b/crates/oxc_prettier/src/format/binaryish.rs @@ -3,10 +3,8 @@ use oxc_ast::{ast::*, AstKind}; use oxc_span::GetSpan; use crate::{ - binaryish::BinaryishOperator, - comments::CommentFlags, - ir::{Doc, DocBuilder}, - Format, Prettier, + array, binaryish::BinaryishOperator, comments::CommentFlags, group, indent, ir::Doc, line, + text, Format, Prettier, }; pub(super) fn print_binaryish_expression<'a>( @@ -27,14 +25,14 @@ pub(super) fn print_binaryish_expression<'a>( let parts = print_binaryish_expressions(p, left, operator, right); if is_inside_parenthesis { - return p.array(parts); + return array!(p, parts); } // Avoid indenting sub-expressions in some cases where the first sub-expression is already // indented accordingly. We should indent sub-expressions where the first case isn't indented. let should_not_indent = matches!(parent_kind, AstKind::ReturnStatement(_)); if should_not_indent { - return p.group(p.array(parts)); + return group!(p, parts); } let first_group_index = parts.iter().position(|part| { @@ -46,8 +44,8 @@ pub(super) fn print_binaryish_expression<'a>( // Separate the leftmost expression, possibly with its leading comments. let first_group_index = first_group_index.map_or(1, |index| index + 1); - let mut group = p.vec(); - let mut rest = p.vec(); + let mut group = Vec::new_in(p.allocator); + let mut rest = Vec::new_in(p.allocator); for (i, part) in parts.into_iter().enumerate() { if i < first_group_index { group.push(part); @@ -55,8 +53,8 @@ pub(super) fn print_binaryish_expression<'a>( rest.push(part); } } - group.push(p.indent(rest)); - p.group(p.array(group)) + group.push(indent!(p, rest)); + group!(p, group) } fn print_binaryish_expressions<'a>( @@ -65,7 +63,7 @@ fn print_binaryish_expressions<'a>( operator: BinaryishOperator, right: &Expression<'a>, ) -> Vec<'a, Doc<'a>> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let left_operator = match left { Expression::LogicalExpression(e) => Some(BinaryishOperator::LogicalOperator(e.operator)), @@ -77,35 +75,35 @@ fn print_binaryish_expressions<'a>( parts.push(match left { Expression::BinaryExpression(e) => { let expr_doc = print_binaryish_expressions(p, &e.left, e.operator.into(), &e.right); - p.array(expr_doc) + array!(p, expr_doc) } Expression::LogicalExpression(e) => { let expr_doc = print_binaryish_expressions(p, &e.left, e.operator.into(), &e.right); - p.array(expr_doc) + array!(p, expr_doc) } _ => unreachable!(), }); } else { let left_doc = left.format(p); - parts.push(p.group(left_doc)); + parts.push(group!(p, [left_doc])); } let should_inline = should_inline_logical_expression(right); let line_before_operator = false; let right = if should_inline { - let mut parts = p.vec(); - parts.push(p.text(operator.as_str())); - parts.push(p.space()); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!(operator.as_str())); + parts.push(text!(" ")); parts.push(right.format(p)); parts } else { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if line_before_operator { - parts.push(p.line()); + parts.push(line!()); } - parts.push(p.text(operator.as_str())); - parts.push(if line_before_operator { p.space() } else { p.line() }); + parts.push(text!(operator.as_str())); + parts.push(if line_before_operator { text!(" ") } else { line!() }); parts.push(right.format(p)); parts }; @@ -114,14 +112,10 @@ fn print_binaryish_expressions<'a>( let should_group = should_break; if !line_before_operator { - parts.push(p.space()); + parts.push(text!(" ")); } - parts.push(if should_group { - p.group_with_opts(p.array(right), should_break, None) - } else { - p.array(right) - }); + parts.push(if should_group { group!(p, right, should_break, None) } else { array!(p, right) }); parts } diff --git a/crates/oxc_prettier/src/format/block.rs b/crates/oxc_prettier/src/format/block.rs index aa25511507ca7e..3db38f9b5f4116 100644 --- a/crates/oxc_prettier/src/format/block.rs +++ b/crates/oxc_prettier/src/format/block.rs @@ -1,26 +1,24 @@ +use oxc_allocator::Vec; use oxc_ast::{ast::*, AstKind}; -use crate::{ - format::statement, - ir::{Doc, DocBuilder}, - Format, Prettier, -}; +use crate::{array, format::statement, hardline, indent, ir::Doc, text, Format, Prettier}; pub(super) fn print_block<'a>( p: &mut Prettier<'a>, stmts: &[Statement<'a>], directives: Option<&[Directive<'a>]>, ) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.text("{")); + let mut parts = Vec::new_in(p.allocator); + + parts.push(text!("{")); if let Some(doc) = print_block_body(p, stmts, directives, true, false) { parts.push({ - let mut parts = p.vec(); - parts.extend(p.hardline()); + let mut parts = Vec::new_in(p.allocator); + parts.extend(hardline!()); parts.push(doc); - p.indent(parts) + indent!(p, parts) }); - parts.extend(p.hardline()); + parts.extend(hardline!()); } else { let parent = p.parent_kind(); let parent_parent = p.parent_parent_kind(); @@ -42,11 +40,12 @@ pub(super) fn print_block<'a>( && !matches!(p.parent_parent_kind(), Some(AstKind::TryStatement(stmt)) if stmt.finalizer.is_some())) || matches!(p.current_kind(), AstKind::StaticBlock(_))) { - parts.extend(p.hardline()); + parts.extend(hardline!()); } } - parts.push(p.text("}")); - p.array(parts) + parts.push(text!("}")); + + array!(p, parts) } pub(super) fn print_block_body<'a>( @@ -63,7 +62,7 @@ pub(super) fn print_block_body<'a>( return None; } - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if has_directives { if let Some(directives) = directives { @@ -80,5 +79,5 @@ pub(super) fn print_block_body<'a>( )); } - Some(p.array(parts)) + Some(array!(p, parts)) } diff --git a/crates/oxc_prettier/src/format/call_arguments.rs b/crates/oxc_prettier/src/format/call_arguments.rs index 689a05dd0b50a2..498e044abc9605 100644 --- a/crates/oxc_prettier/src/format/call_arguments.rs +++ b/crates/oxc_prettier/src/format/call_arguments.rs @@ -4,12 +4,14 @@ use oxc_span::{GetSpan, Span}; use oxc_syntax::operator::UnaryOperator; use crate::{ + array, break_parent, conditional_group, format::{ call_expression::{is_commons_js_or_amd_call, CallExpressionLike}, misc, }, - ir::{Doc, DocBuilder}, - p_vec, + group, hardline, if_break, indent, + ir::Doc, + line, softline, text, utils::will_break, Format, Prettier, }; @@ -18,8 +20,8 @@ pub fn print_call_arguments<'a>( p: &mut Prettier<'a>, expression: &CallExpressionLike<'a, '_>, ) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.text("(")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("(")); let callee = expression.callee(); let arguments = expression.arguments(); @@ -31,13 +33,14 @@ pub fn print_call_arguments<'a>( if arguments.is_empty() { parts.extend(p.print_inner_comment(Span::new(callee.span().end, expression.span().end))); - parts.push(p.text(")")); - return p.array(parts); + parts.push(text!(")")); + + return array!(p, parts); } #[allow(clippy::cast_sign_loss)] let get_printed_arguments = |p: &mut Prettier<'a>, skip_index: isize| { - let mut printed_arguments = p.vec(); + let mut printed_arguments = Vec::new_in(p.allocator); let mut len = arguments.len(); let arguments: Box> = match skip_index { _ if skip_index > 0 => { @@ -55,36 +58,38 @@ pub fn print_call_arguments<'a>( for (i, element) in arguments { let doc = element.format(p); - let mut arg = p.vec(); + let mut arg = Vec::new_in(p.allocator); arg.push(doc); if i < len - 1 { - arg.push(p.text(",")); + arg.push(text!(",")); if p.is_next_line_empty(element.span()) { - arg.extend(p.hardline()); - arg.extend(p.hardline()); + arg.extend(hardline!()); + arg.extend(hardline!()); } else { - arg.push(p.line()); + arg.push(line!()); } } - printed_arguments.push(p.array(arg)); + printed_arguments.push(array!(p, arg)); } printed_arguments }; let all_args_broken_out = |p: &mut Prettier<'a>| { - let mut parts = p.vec(); - parts.push(p.text("(")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("(")); let arguments_doc = get_printed_arguments(p, 0); - parts.push(p.indent(p_vec!( + parts.push(indent!( p, - p.line(), - p.array(arguments_doc), - if p.should_print_all_comma() { p.text(",") } else { p.text("") } - ))); - parts.push(p.line()); - parts.push(p.text(")")); - p.group_with_opts(p.array(parts), true, None) + [ + line!(), + array!(p, arguments_doc), + if p.should_print_all_comma() { text!(",") } else { text!("") } + ] + )); + parts.push(line!()); + parts.push(text!(")")); + group!(p, parts, true, None) }; if should_expand_first_arg(arguments) { @@ -96,22 +101,28 @@ pub fn print_call_arguments<'a>( let last_doc = get_printed_arguments(p, 1).pop().unwrap(); let all_args_broken_out_doc = all_args_broken_out(p); - return p.array(p_vec!( + return array!( p, - p.break_parent(), - p.conditional_group( - p.array(p_vec!( + [ + break_parent!(), + conditional_group!( p, - p.text("("), - p.group_with_opts(first_doc, true, None), - p.text(", "), - last_doc, - p.text(")"), - )), - vec![all_args_broken_out_doc], - None - ) - )); + [ + array!( + p, + [ + text!("("), + group!(p, [first_doc], true, None), + text!(", "), + last_doc, + text!(")"), + ] + ), + all_args_broken_out_doc + ] + ) + ] + ); } } @@ -122,8 +133,8 @@ pub fn print_call_arguments<'a>( } if !printed_arguments.is_empty() { - printed_arguments.push(p.text(",")); - printed_arguments.push(p.line()); + printed_arguments.push(text!(",")); + printed_arguments.push(line!()); } let get_last_doc = |p: &mut Prettier<'a>| { @@ -137,60 +148,68 @@ pub fn print_call_arguments<'a>( if will_break(&mut last_doc) { let all_args_broken_out_doc = all_args_broken_out(p); - return p.array(p_vec!( + return array!( p, - p.break_parent(), - p.conditional_group( - p.array(p_vec!( + [ + break_parent!(), + conditional_group!( p, - p.text("("), - p.array(printed_arguments), - p.group_with_opts(last_doc, true, None), - p.text(")") - )), - vec![all_args_broken_out_doc], - None - ), - )); + [ + array!( + p, + [ + text!("("), + array!(p, printed_arguments), + group!(p, [last_doc], true, None), + text!(")") + ] + ), + all_args_broken_out_doc + ] + ), + ] + ); } let printed_arguments2 = get_printed_arguments(p, -1); let last_doc2 = get_last_doc(p); let all_args_broken_out_doc = all_args_broken_out(p); - return p.conditional_group( - p.array(p_vec!(p, p.text("("), p.array(printed_arguments), last_doc, p.text(")"))), - vec![ - p.array(p_vec!( + return conditional_group!( + p, + [ + array!(p, [text!("("), array!(p, printed_arguments), last_doc, text!(")")]), + array!( p, - p.text("("), - p.array(printed_arguments2), - p.group_with_opts(last_doc2, true, None), - p.text(")") - )), + [ + text!("("), + array!(p, printed_arguments2), + group!(p, [last_doc2], true, None), + text!(")") + ] + ), all_args_broken_out_doc, - ], - None, + ] ); } let mut printed_arguments = get_printed_arguments(p, 0); if should_break { - printed_arguments.insert(0, p.softline()); - parts.push(p.indent(printed_arguments)); - parts.push(p.if_break(p.text(","), p.text(""), None)); - parts.push(p.softline()); + printed_arguments.insert(0, softline!()); + parts.push(indent!(p, printed_arguments)); + parts.push(if_break!(p, text!(","))); + parts.push(softline!()); } else { parts.extend(printed_arguments); } - parts.push(p.text(")")); + parts.push(text!(")")); let should_break = should_break && arguments.iter().any(|arg| { misc::has_new_line_in_range(p.source_text, arg.span().start, arg.span().end) }); - p.group_with_opts(p.array(parts), should_break, None) + group!(p, parts, should_break, None) } /// * Reference diff --git a/crates/oxc_prettier/src/format/call_expression.rs b/crates/oxc_prettier/src/format/call_expression.rs index 17cfb46c12163e..0b53d0d2afc612 100644 --- a/crates/oxc_prettier/src/format/call_expression.rs +++ b/crates/oxc_prettier/src/format/call_expression.rs @@ -2,11 +2,7 @@ use oxc_allocator::Vec; use oxc_ast::ast::*; use oxc_span::{GetSpan, Span}; -use crate::{ - format::call_arguments::print_call_arguments, - ir::{Doc, DocBuilder}, - Format, Prettier, -}; +use crate::{format::call_arguments::print_call_arguments, group, ir::Doc, text, Format, Prettier}; pub(super) enum CallExpressionLike<'a, 'b> { CallExpression(&'b CallExpression<'a>), @@ -62,10 +58,10 @@ pub(super) fn print_call_expression<'a>( p: &mut Prettier<'a>, expression: &CallExpressionLike<'a, '_>, ) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if expression.is_new() { - parts.push(p.text("new ")); + parts.push(text!("new ")); }; parts.push(expression.callee().format(p)); @@ -75,12 +71,12 @@ pub(super) fn print_call_expression<'a>( } if expression.optional() { - parts.push(p.text("?.")); + parts.push(text!("?.")); } parts.push(print_call_arguments(p, expression)); - p.group(p.array(parts)) + group!(p, parts) } /// diff --git a/crates/oxc_prettier/src/format/class.rs b/crates/oxc_prettier/src/format/class.rs index 26a73219dc056e..1182b5e0cf8b8f 100644 --- a/crates/oxc_prettier/src/format/class.rs +++ b/crates/oxc_prettier/src/format/class.rs @@ -1,18 +1,21 @@ use std::ops::Add; +use oxc_allocator::Vec; use oxc_ast::ast::*; use oxc_span::GetSpan; use crate::{ - format::{assignment, assignment::AssignmentLikeNode, Separator}, - ir::{Doc, DocBuilder}, - p_vec, Format, Prettier, + array, + format::{assignment, assignment::AssignmentLikeNode, JoinSeparator}, + group, hardline, if_break, indent, + ir::Doc, + join, line, softline, text, Format, Prettier, }; pub(super) fn print_class<'a>(p: &mut Prettier<'a>, class: &Class<'a>) -> Doc<'a> { - let mut parts = p.vec(); - let mut heritage_clauses_parts = p.vec(); - let mut group_parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); + let mut heritage_clauses_parts = Vec::new_in(p.allocator); + let mut group_parts = Vec::new_in(p.allocator); // Keep old behaviour of extends in same line // If there is only on extends and there are not comments @@ -21,41 +24,41 @@ pub(super) fn print_class<'a>(p: &mut Prettier<'a>, class: &Class<'a>) -> Doc<'a let group_mode = class.implements.as_ref().is_some_and(|v| !v.is_empty()); if let Some(super_class) = &class.super_class { - let mut extend_parts = p.vec(); + let mut extend_parts = Vec::new_in(p.allocator); - extend_parts.push(p.text("extends ")); + extend_parts.push(text!("extends ")); extend_parts.push(super_class.format(p)); if let Some(super_type_parameters) = &class.super_type_parameters { extend_parts.push(super_type_parameters.format(p)); } - extend_parts.push(p.space()); + extend_parts.push(text!(" ")); if group_mode { - heritage_clauses_parts.push(p.softline()); + heritage_clauses_parts.push(softline!()); } - heritage_clauses_parts.push(p.array(extend_parts)); + heritage_clauses_parts.push(array!(p, extend_parts)); } heritage_clauses_parts.push(print_heritage_clauses_implements(p, class)); for decorator in &class.decorators { - parts.push(p.text("@")); + parts.push(text!("@")); parts.push(decorator.expression.format(p)); - parts.extend(p.hardline()); + parts.extend(hardline!()); } if class.declare { - parts.push(p.text("declare ")); + parts.push(text!("declare ")); } if class.r#abstract { - parts.push(p.text("abstract ")); + parts.push(text!("abstract ")); } - parts.push(p.text("class ")); + parts.push(text!("class ")); if let Some(id) = &class.id { group_parts.push(id.format(p)); @@ -66,35 +69,31 @@ pub(super) fn print_class<'a>(p: &mut Prettier<'a>, class: &Class<'a>) -> Doc<'a } if class.id.is_some() || class.type_parameters.is_some() { - group_parts.push(p.space()); + group_parts.push(text!(" ")); } if group_mode { let printend_parts_group = if should_indent_only_heritage_clauses(class) { - p.array(p_vec!( - p, - p.array(group_parts), - p.indent(p_vec!(p, p.array(heritage_clauses_parts))) - )) + array!(p, [array!(p, group_parts), indent!(p, heritage_clauses_parts)]) } else { - p.indent(p_vec!(p, p.array(group_parts), p.group(p.array(heritage_clauses_parts)))) + indent!(p, [array!(p, group_parts), group!(p, heritage_clauses_parts)]) }; parts.push(printend_parts_group); if !class.body.body.is_empty() && has_multiple_heritage(class) { - parts.extend(p.hardline()); + parts.extend(hardline!()); } } else { - parts.push(p.array(p_vec!(p, p.array(group_parts), p.array(heritage_clauses_parts)))); + parts.push(array!(p, [array!(p, group_parts), array!(p, heritage_clauses_parts)])); } parts.push(class.body.format(p)); - p.array(parts) + array!(p, parts) } pub(super) fn print_class_body<'a>(p: &mut Prettier<'a>, class_body: &ClassBody<'a>) -> Doc<'a> { - let mut parts_inner = p.vec(); + let mut parts_inner = Vec::new_in(p.allocator); for (i, node) in class_body.body.iter().enumerate() { parts_inner.push(node.format(p)); @@ -103,36 +102,36 @@ pub(super) fn print_class_body<'a>(p: &mut Prettier<'a>, class_body: &ClassBody< && node.is_property() && should_print_semicolon_after_class_property(node, class_body.body.get(i + 1)) { - parts_inner.push(p.text(";")); + parts_inner.push(text!(";")); } if i < class_body.body.len() - 1 { - parts_inner.extend(p.hardline()); + parts_inner.extend(hardline!()); if p.is_next_line_empty(node.span()) { - parts_inner.extend(p.hardline()); + parts_inner.extend(hardline!()); } } } // TODO: if there are any dangling comments, print them - let mut parts = p.vec(); - parts.push(p.text("{")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("{")); if !parts_inner.is_empty() { let indent = { - let mut parts = p.vec(); - parts.extend(p.hardline()); - parts.push(p.array(parts_inner)); - p.indent(parts) + let mut parts = Vec::new_in(p.allocator); + parts.extend(hardline!()); + parts.push(array!(p, parts_inner)); + indent!(p, parts) }; - parts.push(p.array(p_vec!(p, indent))); - parts.extend(p.hardline()); + parts.push(array!(p, [indent])); + parts.extend(hardline!()); } - parts.push(p.text("}")); + parts.push(text!("}")); - p.array(parts) + array!(p, parts) } #[derive(Debug)] @@ -227,10 +226,8 @@ impl<'a> ClassMemberish<'a, '_> { fn format_accessibility(&self, p: &mut Prettier<'a>) -> Option> { match self { - ClassMemberish::AccessorProperty(def) => def.accessibility.map(|v| p.text(v.as_str())), - ClassMemberish::PropertyDefinition(def) => { - def.accessibility.map(|v| p.text(v.as_str())) - } + ClassMemberish::AccessorProperty(def) => def.accessibility.map(|v| text!(v.as_str())), + ClassMemberish::PropertyDefinition(def) => def.accessibility.map(|v| text!(v.as_str())), } } @@ -250,51 +247,51 @@ pub(super) fn print_class_property<'a>( p: &mut Prettier<'a>, node: &ClassMemberish<'a, '_>, ) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if let Some(decarators) = node.decorators() { for decorator in decarators { - parts.push(p.text("@")); + parts.push(text!("@")); parts.push(decorator.expression.format(p)); - parts.extend(p.hardline()); + parts.extend(hardline!()); } } if let Some(accessibility) = node.format_accessibility(p) { parts.push(accessibility); - parts.push(p.space()); + parts.push(text!(" ")); } if node.is_declare() { - parts.push(p.text("declare ")); + parts.push(text!("declare ")); } if node.is_static() { - parts.push(p.text("static ")); + parts.push(text!("static ")); } if node.is_abstract() { - parts.push(p.text("abstract ")); + parts.push(text!("abstract ")); } if node.is_override() { - parts.push(p.text("override ")); + parts.push(text!("override ")); } if node.is_readonly() { - parts.push(p.text("readonly ")); + parts.push(text!("readonly ")); } parts.push(node.format_key(p)); if node.is_optional() { - parts.push(p.text("?")); + parts.push(text!("?")); } else if node.is_definite() { - parts.push(p.text("!")); + parts.push(text!("!")); } if let Some(type_annotation) = node.format_type_annotation(p) { - parts.push(p.text(": ")); + parts.push(text!(": ")); parts.push(type_annotation); } @@ -304,13 +301,13 @@ pub(super) fn print_class_property<'a>( ClassMemberish::AccessorProperty(v) => AssignmentLikeNode::AccessorProperty(v), }; let mut result = - assignment::print_assignment(p, node, p.array(parts), p.text(" ="), right_expr); + assignment::print_assignment(p, node, array!(p, parts), text!(" ="), right_expr); if p.options.semi { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(result); - parts.push(p.text(";")); - result = p.array(parts); + parts.push(text!(";")); + result = array!(p, parts); } result } @@ -379,45 +376,37 @@ fn should_print_semicolon_after_class_property<'a>( * @link */ fn print_heritage_clauses_implements<'a>(p: &mut Prettier<'a>, class: &Class<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if class.implements.is_none() { - return p.array(parts); + return array!(p, parts); } let implements = class.implements.as_ref().unwrap(); if implements.len() == 0 { - return p.array(parts); + return array!(p, parts); } if should_indent_only_heritage_clauses(class) { - parts.push(p.if_break( - p.line(), - p.text(""), - None, // ToDo - how to attach group id - )); + parts.push(if_break!(p, line!())); } else if class.super_class.is_some() { - parts.extend(p.hardline()); + parts.extend(hardline!()); } else { - parts.push(p.softline()); + parts.push(softline!()); } - parts.push(p.text("implements ")); + parts.push(text!("implements ")); - let implements_docs = implements.iter().map(|v| v.format(p)).collect(); + let implements_docs = implements.iter().map(|v| v.format(p)).collect::>(); - parts.push(p.indent(p_vec!( + parts.push(indent!( p, - p.group(p.array(p_vec!( - p, - p.softline(), - p.array(p.join(Separator::CommaLine, implements_docs)), - ))) - ))); - parts.push(p.space()); - - p.group(p.array(parts)) + [group!(p, [softline!(), join!(p, JoinSeparator::CommaLine, implements_docs),])] + )); + parts.push(text!(" ")); + + group!(p, parts) } fn should_indent_only_heritage_clauses(class: &Class) -> bool { diff --git a/crates/oxc_prettier/src/format/function.rs b/crates/oxc_prettier/src/format/function.rs index e572384172df60..6e25884a6de9bb 100644 --- a/crates/oxc_prettier/src/format/function.rs +++ b/crates/oxc_prettier/src/format/function.rs @@ -1,9 +1,9 @@ +use oxc_allocator::Vec; use oxc_ast::ast::*; use crate::{ - format::function_parameters::should_group_function_parameters, - ir::{Doc, DocBuilder}, - p_vec, Format, Prettier, + array, dynamic_text, format::function_parameters::should_group_function_parameters, group, + if_break, indent, ir::Doc, softline, text, Format, Prettier, }; pub(super) fn print_function<'a>( @@ -11,29 +11,29 @@ pub(super) fn print_function<'a>( func: &Function<'a>, property_name: Option<&'a str>, ) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if func.declare { - parts.push(p.text("declare ")); + parts.push(text!("declare ")); } if func.r#async { - parts.push(p.text("async ")); + parts.push(text!("async ")); } if let Some(name) = property_name { - parts.push(p.dynamic_text(name)); + parts.push(dynamic_text!(p, name)); } else { - parts.push(p.text("function")); + parts.push(text!("function")); if func.generator { - parts.push(p.text("*")); + parts.push(text!("*")); } - parts.push(p.text(" ")); + parts.push(text!(" ")); } if let Some(id) = &func.id { - parts.push(p.dynamic_text(id.name.as_str())); + parts.push(dynamic_text!(p, id.name.as_str())); } if let Some(type_params) = &func.type_parameters { @@ -41,21 +41,24 @@ pub(super) fn print_function<'a>( } // Prettier has `returnTypeDoc` to group together, write this for keep same with prettier. let params_doc = func.params.format(p); - parts.push(p.group({ - if should_group_function_parameters(func) { - p.group(params_doc) - } else { - params_doc - } - })); + parts.push(group!( + p, + [{ + if should_group_function_parameters(func) { + group!(p, [params_doc]) + } else { + params_doc + } + }] + )); if let Some(return_type) = &func.return_type { - parts.push(p.text(": ")); + parts.push(text!(": ")); parts.push(return_type.type_annotation.format(p)); } if let Some(body) = &func.body { - parts.push(p.space()); + parts.push(text!(" ")); parts.push(body.format(p)); } if func.is_ts_declare_function() || func.body.is_none() { @@ -64,84 +67,84 @@ pub(super) fn print_function<'a>( } } - p.array(parts) + array!(p, parts) } pub(super) fn print_method<'a>(p: &mut Prettier<'a>, method: &MethodDefinition<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if let Some(accessibility) = &method.accessibility { - parts.push(p.text(accessibility.as_str())); - parts.push(p.space()); + parts.push(text!(accessibility.as_str())); + parts.push(text!(" ")); } if method.r#static { - parts.push(p.text("static ")); + parts.push(text!("static ")); } if matches!(method.r#type, MethodDefinitionType::TSAbstractMethodDefinition) { - parts.push(p.text("abstract ")); + parts.push(text!("abstract ")); } if method.r#override { - parts.push(p.text("override ")); + parts.push(text!("override ")); } match method.kind { MethodDefinitionKind::Constructor | MethodDefinitionKind::Method => {} MethodDefinitionKind::Get => { - parts.push(p.text("get ")); + parts.push(text!("get ")); } MethodDefinitionKind::Set => { - parts.push(p.text("set ")); + parts.push(text!("set ")); } } if method.value.r#async { - parts.push(p.text("async ")); + parts.push(text!("async ")); } if method.value.generator { - parts.push(p.text("*")); + parts.push(text!("*")); } parts.push(method.key.format(p)); if method.optional { - parts.push(p.text("?")); + parts.push(text!("?")); } parts.push(print_method_value(p, &method.value)); - p.array(parts) + array!(p, parts) } fn print_method_value<'a>(p: &mut Prettier<'a>, function: &Function<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let parameters_doc = function.params.format(p); let should_group_parameters = should_group_function_parameters(function); let parameters_doc = - if should_group_parameters { p.group(parameters_doc) } else { parameters_doc }; + if should_group_parameters { group!(p, [parameters_doc]) } else { parameters_doc }; if let Some(type_parameters) = &function.type_parameters { parts.push(type_parameters.format(p)); } - parts.push(p.group(parameters_doc)); + parts.push(group!(p, [parameters_doc])); if let Some(ret_typ) = &function.return_type { - parts.push(p.text(": ")); + parts.push(text!(": ")); parts.push(ret_typ.type_annotation.format(p)); } if let Some(body) = &function.body { - parts.push(p.space()); + parts.push(text!(" ")); parts.push(body.format(p)); } else if p.options.semi { - parts.push(p.text(";")); + parts.push(text!(";")); } - p.array(parts) + array!(p, parts) } pub(super) fn print_return_or_throw_argument<'a>( @@ -149,22 +152,24 @@ pub(super) fn print_return_or_throw_argument<'a>( argument: Option<&Expression<'a>>, is_return: bool, ) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); - parts.push(p.text(if is_return { "return" } else { "throw" })); + parts.push(text!(if is_return { "return" } else { "throw" })); if let Some(argument) = argument { - parts.push(p.space()); + parts.push(text!(" ")); parts.push( if argument.is_binaryish() || matches!(argument, Expression::SequenceExpression(_)) { let argument_doc = argument.format(p); - p.group(p.array(p_vec!( + group!( p, - p.if_break(p.text("("), p.text(""), None), - p.indent(p_vec!(p, p.softline(), argument_doc)), - p.softline(), - p.if_break(p.text(")"), p.text(""), None), - ))) + [ + if_break!(p, text!("(")), + indent!(p, [softline!(), argument_doc]), + softline!(), + if_break!(p, text!(")")), + ] + ) } else { argument.format(p) }, @@ -174,5 +179,6 @@ pub(super) fn print_return_or_throw_argument<'a>( if let Some(semi) = p.semi() { parts.push(semi); } - p.array(parts) + + array!(p, parts) } diff --git a/crates/oxc_prettier/src/format/function_parameters.rs b/crates/oxc_prettier/src/format/function_parameters.rs index a82805a78739f8..5b969dab6acd1b 100644 --- a/crates/oxc_prettier/src/format/function_parameters.rs +++ b/crates/oxc_prettier/src/format/function_parameters.rs @@ -1,9 +1,9 @@ +use oxc_allocator::Vec; use oxc_ast::{ast::*, AstKind}; use crate::{ - comments::CommentFlags, - ir::{Doc, DocBuilder}, - p_vec, Format, Prettier, + array, comments::CommentFlags, group, hardline, if_break, indent, ir::Doc, line, softline, + text, Format, Prettier, }; pub(super) fn should_hug_the_only_function_parameter( @@ -61,17 +61,17 @@ pub(super) fn print_function_parameters<'a>( p: &mut Prettier<'a>, params: &FormalParameters<'a>, ) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let is_arrow_function = matches!(p.parent_kind(), AstKind::ArrowFunctionExpression(_)); let need_parens = !is_arrow_function || p.options.arrow_parens.is_always() || params.items.len() != 1; if need_parens { - parts.push(p.text("(")); + parts.push(text!("(")); } let should_hug_the_only_function_parameter = should_hug_the_only_function_parameter(p, params); - let mut printed = p.vec(); + let mut printed = Vec::new_in(p.allocator); let len = params.items.len(); let has_rest = params.rest.is_some(); @@ -80,15 +80,15 @@ pub(super) fn print_function_parameters<'a>( parts.push(this_param.format(p)); if params.items.len() > 0 { - printed.push(p.text(",")); + printed.push(text!(",")); if should_hug_the_only_function_parameter { - printed.push(p.space()); + printed.push(text!(" ")); } else if p.is_next_line_empty(this_param.span) { - printed.extend(p.hardline()); - printed.extend(p.hardline()); + printed.extend(hardline!()); + printed.extend(hardline!()); } else { - printed.push(p.line()); + printed.push(line!()); } } } @@ -96,30 +96,30 @@ pub(super) fn print_function_parameters<'a>( for (i, param) in params.items.iter().enumerate() { if let Some(accessibility) = ¶m.accessibility { - printed.push(p.text(accessibility.as_str())); - printed.push(p.space()); + printed.push(text!(accessibility.as_str())); + printed.push(text!(" ")); } if param.r#override { - printed.push(p.text("override ")); + printed.push(text!("override ")); } if param.readonly { - printed.push(p.text("readonly ")); + printed.push(text!("readonly ")); } printed.push(param.format(p)); if i == len - 1 && !has_rest { break; } - printed.push(p.text(",")); + printed.push(text!(",")); if should_hug_the_only_function_parameter { - printed.push(p.space()); + printed.push(text!(" ")); } else if p.is_next_line_empty(param.span) { - printed.extend(p.hardline()); - printed.extend(p.hardline()); + printed.extend(hardline!()); + printed.extend(hardline!()); } else { - printed.push(p.line()); + printed.push(line!()); } } if let Some(rest) = ¶ms.rest { @@ -127,30 +127,30 @@ pub(super) fn print_function_parameters<'a>( } if should_hug_the_only_function_parameter { - let mut parts = p.vec(); - parts.push(p.text("(")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("(")); parts.extend(printed); - parts.push(p.text(")")); - return p.array(parts); + parts.push(text!(")")); + return array!(p, parts); } - let mut indented = p.vec(); - indented.push(p.softline()); + let mut indented = Vec::new_in(p.allocator); + indented.push(softline!()); indented.extend(printed); - let indented = p.indent(p_vec!(p, p.array(indented))); + let indented = indent!(p, indented); parts.push(indented); let skip_dangling_comma = params.rest.is_some() || matches!(p.parent_kind(), AstKind::Function(func) if func.this_param.is_some()); - parts.push(p.if_break(p.text(if skip_dangling_comma { "" } else { "," }), p.text(""), None)); - parts.push(p.softline()); + parts.push(if_break!(p, text!(if skip_dangling_comma { "" } else { "," }))); + parts.push(softline!()); if need_parens { - parts.push(p.text(")")); + parts.push(text!(")")); } if p.args.expand_first_arg { - p.array(parts) + array!(p, parts) } else { - p.group(p.array(parts)) + group!(p, parts) } } diff --git a/crates/oxc_prettier/src/format/misc.rs b/crates/oxc_prettier/src/format/misc.rs index e65155392608ed..37e47552c09381 100644 --- a/crates/oxc_prettier/src/format/misc.rs +++ b/crates/oxc_prettier/src/format/misc.rs @@ -1,7 +1,7 @@ use oxc_ast::{ast::*, AstKind}; use oxc_span::Span; -use crate::{ir::Doc, p_vec, DocBuilder, Prettier}; +use crate::{array, indent, ir::Doc, line, text, Prettier}; pub(super) fn adjust_clause<'a>( p: &Prettier<'a>, @@ -10,14 +10,14 @@ pub(super) fn adjust_clause<'a>( force_space: bool, ) -> Doc<'a> { if matches!(node, Statement::EmptyStatement(_)) { - return p.text(";"); + return text!(";"); } if matches!(node, Statement::BlockStatement(_)) || force_space { - return p.array(p_vec!(p, p.space(), clause)); + return array!(p, [text!(" "), clause]); } - p.indent(p_vec!(p, p.line(), clause)) + indent!(p, [line!(), clause]) } pub(super) fn has_new_line_in_range(text: &str, start: u32, end: u32) -> bool { diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index e135d7f8f3dfa4..efe70237c10320 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -31,9 +31,11 @@ use oxc_span::GetSpan; use oxc_syntax::identifier::{is_identifier_name, is_line_terminator}; use crate::{ + array, dynamic_text, format::{array::Array, object::ObjectLike, template_literal::TemplateLiteralPrinter}, - ir::{Doc, DocBuilder, Separator}, - p_vec, wrap, Prettier, + group, hardline, indent, + ir::{Doc, JoinSeparator}, + join, line, softline, text, wrap, Prettier, }; pub trait Format<'a> { @@ -53,7 +55,7 @@ where impl<'a> Format<'a> for Program<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, Program, { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if let Some(hashbang) = &self.hashbang { parts.push(hashbang.format(p)); @@ -69,39 +71,38 @@ impl<'a> Format<'a> for Program<'a> { parts.push(doc); } - p.array(parts) + array!(p, parts) }) } } impl<'a> Format<'a> for Hashbang<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.dynamic_text(self.span.source_text(p.source_text))); - parts.extend(p.hardline()); + let mut parts = Vec::new_in(p.allocator); + parts.push(dynamic_text!(p, self.span.source_text(p.source_text))); + parts.extend(hardline!()); // Preserve original newline if let Some(c) = p.source_text[self.span.end as usize..].chars().nth(1) { if is_line_terminator(c) { - parts.extend(p.hardline()); + parts.extend(hardline!()); } } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for Directive<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.dynamic_text(string::print_string( + let mut parts = Vec::new_in(p.allocator); + parts.push(dynamic_text!( p, - self.directive.as_str(), - p.options.single_quote, - ))); + string::print_string(p, self.directive.as_str(), p.options.single_quote,) + )); if let Some(semi) = p.semi() { parts.push(semi); } - parts.extend(p.hardline()); - p.array(parts) + parts.extend(hardline!()); + array!(p, parts) } } @@ -135,62 +136,63 @@ impl<'a> Format<'a> for Statement<'a> { impl<'a> Format<'a> for ExpressionStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, ExpressionStatement, { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.expression.format(p)); if let Some(semi) = p.semi() { parts.push(semi); } - p.array(parts) + array!(p, parts) }) } } impl<'a> Format<'a> for EmptyStatement { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("") + text!("") } } impl<'a> Format<'a> for IfStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, IfStatement, { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let test_doc = self.test.format(p); let consequent = self.consequent.format(p); let consequent = misc::adjust_clause(p, &self.consequent, consequent, false); - let opening = p.group(p.array(p_vec!( + let opening = group!( p, - p.text("if ("), - p.group(p.array(p_vec!( - p, - p.indent(p_vec!(p, p.softline(), test_doc)), - p.softline(), - ))), - p.text(")"), - consequent - ))); + [ + text!("if ("), + group!(p, [indent!(p, [softline!(), test_doc]), softline!(),]), + text!(")"), + consequent + ] + ); parts.push(opening); if let Some(alternate) = &self.alternate { let else_on_same_line = matches!(alternate, Statement::BlockStatement(_)); if else_on_same_line { - parts.push(p.space()); + parts.push(text!(" ")); } else { - parts.extend(p.hardline()); + parts.extend(hardline!()); } - parts.push(p.text("else")); + parts.push(text!("else")); let alternate_doc = alternate.format(p); - parts.push(p.group(misc::adjust_clause( + parts.push(group!( p, - alternate, - alternate_doc, - matches!(alternate, Statement::IfStatement(_)), - ))); + [misc::adjust_clause( + p, + alternate, + alternate_doc, + matches!(alternate, Statement::IfStatement(_)), + )] + )); } - p.array(parts) + array!(p, parts) }) } } @@ -208,35 +210,29 @@ impl<'a> Format<'a> for ForStatement<'a> { let body = misc::adjust_clause(p, &self.body, body, false); if self.init.is_none() && self.test.is_none() && self.update.is_none() { - return p.group(p.array(p_vec!(p, p.text("for (;;)"), body))); + return group!(p, [text!("for (;;)"), body]); } let parts_head = { - let mut parts_head = p.vec(); - parts_head.push(p.softline()); + let mut parts_head = Vec::new_in(p.allocator); + parts_head.push(softline!()); if let Some(init) = &self.init { parts_head.push(init.format(p)); } - parts_head.push(p.text(";")); - parts_head.push(p.line()); + parts_head.push(text!(";")); + parts_head.push(line!()); if let Some(init) = &self.test { parts_head.push(init.format(p)); } - parts_head.push(p.text(";")); - parts_head.push(p.line()); + parts_head.push(text!(";")); + parts_head.push(line!()); if let Some(init) = &self.update { parts_head.push(init.format(p)); } - p.indent(parts_head) + indent!(p, parts_head) }; - p.group(p.array(p_vec!( - p, - p.text("for ("), - p.group(p.array(p_vec!(p, parts_head, p.softline()))), - p.text(")"), - body, - ))) + group!(p, [text!("for ("), group!(p, [parts_head, softline!()]), text!(")"), body,]) }) } } @@ -253,15 +249,15 @@ impl<'a> Format<'a> for ForStatementInit<'a> { impl<'a> Format<'a> for ForInStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, ForInStatement, { - let mut parts = p.vec(); - parts.push(p.text("for (")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("for (")); parts.push(self.left.format(p)); - parts.push(p.text(" in ")); + parts.push(text!(" in ")); parts.push(self.right.format(p)); - parts.push(p.text(")")); + parts.push(text!(")")); let body = self.body.format(p); parts.push(misc::adjust_clause(p, &self.body, body, false)); - p.group(p.array(parts)) + group!(p, parts) }) } } @@ -269,19 +265,19 @@ impl<'a> Format<'a> for ForInStatement<'a> { impl<'a> Format<'a> for ForOfStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, ForOfStatement, { - let mut parts = p.vec(); - parts.push(p.text("for")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("for")); if self.r#await { - parts.push(p.text(" await")); + parts.push(text!(" await")); } - parts.push(p.text(" (")); + parts.push(text!(" (")); parts.push(self.left.format(p)); - parts.push(p.text(" of ")); + parts.push(text!(" of ")); parts.push(self.right.format(p)); - parts.push(p.text(")")); + parts.push(text!(")")); let body = self.body.format(p); parts.push(misc::adjust_clause(p, &self.body, body, false)); - p.group(p.array(parts)) + group!(p, parts) }) } } @@ -298,21 +294,17 @@ impl<'a> Format<'a> for ForStatementLeft<'a> { impl<'a> Format<'a> for WhileStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, WhileStatement, { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); - parts.push(p.text("while (")); + parts.push(text!("while (")); let test_doc = self.test.format(p); - parts.push(p.group(p.array(p_vec!( - p, - p.indent(p_vec!(p, p.softline(), test_doc)), - p.softline(), - )))); - parts.push(p.text(")")); + parts.push(group!(p, [indent!(p, [softline!(), test_doc]), softline!(),])); + parts.push(text!(")")); let body = self.body.format(p); parts.push(misc::adjust_clause(p, &self.body, body, false)); - p.group(p.array(parts)) + group!(p, parts) }) } } @@ -320,121 +312,117 @@ impl<'a> Format<'a> for WhileStatement<'a> { impl<'a> Format<'a> for DoWhileStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, DoWhileStatement, { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let clause = self.body.format(p); let clause = misc::adjust_clause(p, &self.body, clause, false); - let do_body = p.group(p.array(p_vec!(p, p.text("do"), clause))); + let do_body = group!(p, [text!("do"), clause]); parts.push(do_body); if matches!(self.body, Statement::BlockStatement(_)) { - parts.push(p.space()); + parts.push(text!(" ")); } else { - parts.extend(p.hardline()); + parts.extend(hardline!()); } - parts.push(p.text("while (")); + parts.push(text!("while (")); let test_doc = self.test.format(p); - parts.push(p.group(p.array(p_vec!( - p, - p.indent(p_vec!(p, p.softline(), test_doc)), - p.softline() - )))); - parts.push(p.text(")")); + parts.push(group!(p, [indent!(p, [softline!(), test_doc]), softline!()])); + parts.push(text!(")")); if let Some(semi) = p.semi() { parts.push(semi); } - p.array(parts) + array!(p, parts) }) } } impl<'a> Format<'a> for ContinueStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.text("continue")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("continue")); if let Some(label) = &self.label { - parts.push(p.space()); + parts.push(text!(" ")); parts.push(label.format(p)); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for BreakStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.text("break")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("break")); if let Some(label) = &self.label { - parts.push(p.space()); + parts.push(text!(" ")); parts.push(label.format(p)); } if p.options.semi { - parts.push(p.text(";")); + parts.push(text!(";")); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for SwitchStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, SwitchStatement, { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); - let mut header_parts = p.vec(); + let mut header_parts = Vec::new_in(p.allocator); - header_parts.push(p.text("switch (")); + header_parts.push(text!("switch (")); let discriminant_doc = self.discriminant.format(p); - header_parts.push(p.indent(p_vec!(p, p.softline(), discriminant_doc))); + header_parts.push(indent!(p, [softline!(), discriminant_doc])); - header_parts.push(p.softline()); - header_parts.push(p.text(")")); + header_parts.push(softline!()); + header_parts.push(text!(")")); - parts.push(p.group(p.array(header_parts))); + parts.push(group!(p, header_parts)); - parts.push(p.text(" {")); + parts.push(text!(" {")); - let mut cases_parts = p.vec(); + let mut cases_parts = Vec::new_in(p.allocator); let len = self.cases.len(); for (i, case) in self.cases.iter().enumerate() { cases_parts.push({ - let mut parts = p.vec(); - parts.extend(p.hardline()); + let mut parts = Vec::new_in(p.allocator); + parts.extend(hardline!()); parts.push(case.format(p)); - p.indent(parts) + indent!(p, parts) }); if i != len - 1 && p.is_next_line_empty(case.span) { - cases_parts.extend(p.hardline()); + cases_parts.extend(hardline!()); } } parts.extend(cases_parts); - parts.extend(p.hardline()); - parts.push(p.text("}")); + parts.extend(hardline!()); + parts.push(text!("}")); - p.array(parts) + array!(p, parts) }) } } impl<'a> Format<'a> for SwitchCase<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if let Some(test) = &self.test { - parts.push(p.text("case ")); + parts.push(text!("case ")); parts.push(test.format(p)); - parts.push(p.text(":")); + parts.push(text!(":")); } else { - parts.push(p.text("default:")); + parts.push(text!("default:")); } let consequent: Vec<_> = Vec::from_iter_in( @@ -445,21 +433,21 @@ impl<'a> Format<'a> for SwitchCase<'a> { let is_only_one_block_statement = len == 1 && matches!(self.consequent[0], Statement::BlockStatement(_)); - let mut consequent_parts = p.vec(); + let mut consequent_parts = Vec::new_in(p.allocator); for i in 0..len { let stmt = &consequent[i]; if i != 0 && matches!(stmt, Statement::BreakStatement(_)) { let last_stmt = &consequent[i - 1]; if p.is_next_line_empty(last_stmt.span()) { - consequent_parts.extend(p.hardline()); + consequent_parts.extend(hardline!()); } } if is_only_one_block_statement { - consequent_parts.push(p.space()); + consequent_parts.push(text!(" ")); } else { - consequent_parts.extend(p.hardline()); + consequent_parts.extend(hardline!()); } consequent_parts.push(stmt.format(p)); } @@ -468,11 +456,11 @@ impl<'a> Format<'a> for SwitchCase<'a> { if is_only_one_block_statement { parts.extend(consequent_parts); } else { - parts.push(p.indent(p_vec!(p, p.group(p.array(consequent_parts))))); + parts.push(indent!(p, [group!(p, consequent_parts)])); } } - p.array(parts) + array!(p, parts) } } @@ -488,32 +476,32 @@ impl<'a> Format<'a> for LabeledStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { if matches!(self.body, Statement::EmptyStatement(_)) { let label_doc = self.label.format(p); - return p.array(p_vec!(p, label_doc, p.text(":;"))); + return array!(p, [label_doc, text!(":;")]); } let label_doc = self.label.format(p); let body_doc = self.body.format(p); - p.array(p_vec!(p, label_doc, p.text(": "), body_doc)) + array!(p, [label_doc, text!(": "), body_doc]) } } impl<'a> Format<'a> for TryStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, TryStatement, { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); - parts.push(p.text("try ")); + parts.push(text!("try ")); parts.push(self.block.format(p)); if let Some(handler) = &self.handler { - parts.push(p.space()); + parts.push(text!(" ")); parts.push(handler.format(p)); } if let Some(finalizer) = &self.finalizer { - parts.push(p.text(" finally ")); + parts.push(text!(" finally ")); parts.push(finalizer.format(p)); } - p.array(parts) + array!(p, parts) }) } } @@ -521,17 +509,17 @@ impl<'a> Format<'a> for TryStatement<'a> { impl<'a> Format<'a> for CatchClause<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, CatchClause, { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); - parts.push(p.text("catch ")); + parts.push(text!("catch ")); if let Some(param) = &self.param { - parts.push(p.text("(")); + parts.push(text!("(")); parts.push(param.pattern.format(p)); - parts.push(p.text(") ")); + parts.push(text!(") ")); } parts.push(self.body.format(p)); - p.array(parts) + array!(p, parts) }) } } @@ -546,26 +534,28 @@ impl<'a> Format<'a> for WithStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let object_doc = self.object.format(p); let body_doc = self.body.format(p); - p.group(p.array(p_vec!( + group!( p, - p.text("with ("), - object_doc, - p.text(")"), - misc::adjust_clause(p, &self.body, body_doc, false) - ))) + [ + text!("with ("), + object_doc, + text!(")"), + misc::adjust_clause(p, &self.body, body_doc, false) + ] + ) } } impl<'a> Format<'a> for DebuggerStatement { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.text("debugger")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("debugger")); if p.options.semi { - parts.push(p.text(";")); + parts.push(text!(";")); } - p.array(parts) + array!(p, parts) } } @@ -610,31 +600,31 @@ impl<'a> Format<'a> for VariableDeclaration<'a> { let kind = self.kind.as_str(); - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if self.declare { - parts.push(p.text("declare ")); + parts.push(text!("declare ")); } - parts.push(p.text(kind)); - parts.push(p.space()); + parts.push(text!(kind)); + parts.push(text!(" ")); let is_hardline = !p.parent_kind().is_iteration_statement() && self.declarations.iter().all(|decl| decl.init.is_some()); let decls_len = self.declarations.len(); parts.extend(self.declarations.iter().enumerate().map(|(i, decl)| { if decls_len > 1 { - let mut d_parts = p.vec(); + let mut d_parts = Vec::new_in(p.allocator); if i != 0 { - d_parts.push(p.text(",")); + d_parts.push(text!(",")); if is_hardline { - d_parts.extend(p.hardline()); + d_parts.extend(hardline!()); } else { - d_parts.push(p.line()); + d_parts.push(line!()); } } d_parts.push(decl.format(p)); - p.indent(d_parts) + indent!(p, d_parts) } else { decl.format(p) } @@ -646,7 +636,7 @@ impl<'a> Format<'a> for VariableDeclaration<'a> { } } - p.group(p.array(parts)) + group!(p, parts) }) } } @@ -659,27 +649,27 @@ impl<'a> Format<'a> for VariableDeclarator<'a> { impl<'a> Format<'a> for TSTypeAliasDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if self.declare { - parts.push(p.text("declare ")); + parts.push(text!("declare ")); } - parts.push(p.text("type ")); + parts.push(text!("type ")); parts.push(self.id.format(p)); if let Some(params) = &self.type_parameters { parts.push(params.format(p)); } - parts.push(p.text(" = ")); + parts.push(text!(" = ")); parts.push(self.type_annotation.format(p)); if let Some(semi) = p.semi() { parts.push(semi); } - p.array(parts) + array!(p, parts) } } @@ -730,128 +720,128 @@ impl<'a> Format<'a> for TSType<'a> { impl<'a> Format<'a> for TSAnyKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("any") + text!("any") } } impl<'a> Format<'a> for TSBigIntKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("bigint") + text!("bigint") } } impl<'a> Format<'a> for TSBooleanKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("boolean") + text!("boolean") } } impl<'a> Format<'a> for TSIntrinsicKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("intrinsic") + text!("intrinsic") } } impl<'a> Format<'a> for TSNeverKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("never") + text!("never") } } impl<'a> Format<'a> for TSNullKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("null") + text!("null") } } impl<'a> Format<'a> for TSNumberKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("number") + text!("number") } } impl<'a> Format<'a> for TSObjectKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("object") + text!("object") } } impl<'a> Format<'a> for TSStringKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("string") + text!("string") } } impl<'a> Format<'a> for TSSymbolKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("symbol") + text!("symbol") } } impl<'a> Format<'a> for TSThisType { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("this") + text!("this") } } impl<'a> Format<'a> for TSUndefinedKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("undefined") + text!("undefined") } } impl<'a> Format<'a> for TSUnknownKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("unknown") + text!("unknown") } } impl<'a> Format<'a> for TSVoidKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("void") + text!("void") } } impl<'a> Format<'a> for TSArrayType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let element_type_doc = self.element_type.format(p); - p.array(p_vec!(p, element_type_doc, p.text("[]"))) + array!(p, [element_type_doc, text!("[]")]) } } impl<'a> Format<'a> for TSConditionalType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.check_type.format(p)); - parts.push(p.text(" extends ")); + parts.push(text!(" extends ")); parts.push(self.extends_type.format(p)); - parts.push(p.text(" ? ")); + parts.push(text!(" ? ")); parts.push(self.true_type.format(p)); - parts.push(p.text(" : ")); + parts.push(text!(" : ")); parts.push(self.false_type.format(p)); - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSConstructorType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if self.r#abstract { - parts.push(p.text("abstract ")); + parts.push(text!("abstract ")); } - parts.push(p.text("new ")); + parts.push(text!("new ")); parts.push(self.params.format(p)); let type_annotation_doc = self.return_type.type_annotation.format(p); - parts.push(p.array(p_vec!(p, p.text(" => "), type_annotation_doc))); - p.array(parts) + parts.push(array!(p, [text!(" => "), type_annotation_doc])); + array!(p, parts) } } impl<'a> Format<'a> for TSFunctionType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if let Some(type_parameters) = &self.type_parameters { parts.push(type_parameters.format(p)); @@ -859,42 +849,42 @@ impl<'a> Format<'a> for TSFunctionType<'a> { parts.push(self.params.format(p)); - parts.push(p.text(" => ")); + parts.push(text!(" => ")); parts.push(self.return_type.type_annotation.format(p)); - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSThisParameter<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.text("this")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("this")); if let Some(type_annotation) = &self.type_annotation { - parts.push(p.text(": ")); + parts.push(text!(": ")); parts.push(type_annotation.type_annotation.format(p)); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSImportType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if self.is_type_of { - parts.push(p.text("typeof ")); + parts.push(text!("typeof ")); } - parts.push(p.text("import(")); + parts.push(text!("import(")); parts.push(self.parameter.format(p)); // ToDo: attributes - parts.push(p.text(")")); + parts.push(text!(")")); if let Some(qualifier) = &self.qualifier { - parts.push(p.text(".")); + parts.push(text!(".")); parts.push(qualifier.format(p)); } @@ -902,36 +892,36 @@ impl<'a> Format<'a> for TSImportType<'a> { parts.push(type_parameters.format(p)); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSIndexedAccessType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.object_type.format(p)); - parts.push(p.text("[")); + parts.push(text!("[")); parts.push(self.index_type.format(p)); - parts.push(p.text("]")); - p.array(parts) + parts.push(text!("]")); + array!(p, parts) } } impl<'a> Format<'a> for TSInferType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let type_parameter_doc = self.type_parameter.format(p); - p.array(p_vec!(p, p.text("infer "), type_parameter_doc)) + array!(p, [text!("infer "), type_parameter_doc]) } } impl<'a> Format<'a> for TSIntersectionType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let mut add_symbol = false; for ts_type in &self.types { if add_symbol { - parts.push(p.text(" & ")); + parts.push(text!(" & ")); } else { add_symbol = true; } @@ -939,7 +929,7 @@ impl<'a> Format<'a> for TSIntersectionType<'a> { parts.push(ts_type.format(p)); } - p.array(parts) + array!(p, parts) } } @@ -960,76 +950,77 @@ impl<'a> Format<'a> for TSLiteralType<'a> { impl<'a> Format<'a> for TSMappedType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts: Vec<'_, Doc<'_>> = p.vec(); + let mut parts: Vec<'_, Doc<'_>> = Vec::new_in(p.allocator); match self.readonly { - TSMappedTypeModifierOperator::Plus => parts.push(p.text("+readonly ")), - TSMappedTypeModifierOperator::Minus => parts.push(p.text("-readonly ")), - TSMappedTypeModifierOperator::True => parts.push(p.text("readonly ")), + TSMappedTypeModifierOperator::Plus => parts.push(text!("+readonly ")), + TSMappedTypeModifierOperator::Minus => parts.push(text!("-readonly ")), + TSMappedTypeModifierOperator::True => parts.push(text!("readonly ")), TSMappedTypeModifierOperator::None => (), } - parts.push(p.text("[")); + parts.push(text!("[")); parts.push(self.type_parameter.format(p)); if let Some(name_type) = &self.name_type { - parts.push(p.text(" as ")); + parts.push(text!(" as ")); parts.push(name_type.format(p)); } - parts.push(p.text("]")); + parts.push(text!("]")); match self.optional { - TSMappedTypeModifierOperator::Plus => parts.push(p.text("+?")), - TSMappedTypeModifierOperator::Minus => parts.push(p.text("-?")), - TSMappedTypeModifierOperator::True => parts.push(p.text("?")), + TSMappedTypeModifierOperator::Plus => parts.push(text!("+?")), + TSMappedTypeModifierOperator::Minus => parts.push(text!("-?")), + TSMappedTypeModifierOperator::True => parts.push(text!("?")), TSMappedTypeModifierOperator::None => (), } if let Some(type_annotation) = &self.type_annotation { - parts.push(p.text(": ")); + parts.push(text!(": ")); parts.push(type_annotation.format(p)); } - let mut result = p.vec(); - result.push(p.text("{ ")); - - // TODO: check ident/grouping in method/method-signature.ts - result.push(p.group(p.array(parts))); - result.push(p.text(" }")); - - p.array(result) + array!( + p, + [ + text!("{ "), + // TODO: check ident/grouping in method/method-signature.ts + group!(p, parts), + text!(" }") + ] + ) } } impl<'a> Format<'a> for TSNamedTupleMember<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.label.format(p)); if self.optional { - parts.push(p.text("?")); + parts.push(text!("?")); } - parts.push(p.text(": ")); + parts.push(text!(": ")); parts.push(self.element_type.format(p)); - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSRestType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let type_annotation_doc = self.type_annotation.format(p); - p.array(p_vec!(p, p.text("..."), type_annotation_doc)) + array!(p, [text!("..."), type_annotation_doc]) } } impl<'a> Format<'a> for TSOptionalType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let type_annotation_doc = self.type_annotation.format(p); - p.array(p_vec!(p, type_annotation_doc, p.text("?"))) + array!(p, [type_annotation_doc, text!("?")]) } } @@ -1037,7 +1028,7 @@ impl<'a> Format<'a> for TSQualifiedName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let left_doc = self.left.format(p); let right_doc = self.right.format(p); - p.array(p_vec!(p, left_doc, p.text("."), right_doc)) + array!(p, [left_doc, text!("."), right_doc]) } } @@ -1064,31 +1055,31 @@ impl<'a> Format<'a> for TSTypeLiteral<'a> { impl<'a> Format<'a> for TSTypeOperator<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); - parts.push(p.text(self.operator.to_str())); - parts.push(p.space()); + parts.push(text!(self.operator.to_str())); + parts.push(text!(" ")); parts.push(self.type_annotation.format(p)); - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSTypePredicate<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if self.asserts { - parts.push(p.text("asserts ")); + parts.push(text!("asserts ")); } parts.push(self.parameter_name.format(p)); if let Some(type_annotation) = &self.type_annotation { - parts.push(p.text(" is ")); + parts.push(text!(" is ")); parts.push(type_annotation.type_annotation.format(p)); } - p.array(parts) + array!(p, parts) } } @@ -1103,9 +1094,9 @@ impl<'a> Format<'a> for TSTypePredicateName<'a> { impl<'a> Format<'a> for TSTypeQuery<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); - parts.push(p.text("typeof ")); + parts.push(text!("typeof ")); match &self.expr_name { TSTypeQueryExprName::TSImportType(import_type) => parts.push(import_type.format(p)), @@ -1121,18 +1112,18 @@ impl<'a> Format<'a> for TSTypeQuery<'a> { parts.push(type_parameters.format(p)); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSTypeReference<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.type_name.format(p)); if let Some(params) = &self.type_parameters { parts.push(params.format(p)); } - p.array(parts) + array!(p, parts) } } @@ -1144,12 +1135,12 @@ impl<'a> Format<'a> for TSParenthesizedType<'a> { impl<'a> Format<'a> for TSUnionType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let mut add_symbol = false; for ts_type in &self.types { if add_symbol { - parts.push(p.text(" | ")); + parts.push(text!(" | ")); } else { add_symbol = true; } @@ -1157,55 +1148,55 @@ impl<'a> Format<'a> for TSUnionType<'a> { parts.push(ts_type.format(p)); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for JSDocNullableType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.line() + line!() } } impl<'a> Format<'a> for JSDocNonNullableType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.line() + line!() } } impl<'a> Format<'a> for JSDocUnknownType { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.line() + line!() } } impl<'a> Format<'a> for TSInterfaceDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if self.declare { - parts.push(p.text("declare ")); + parts.push(text!("declare ")); } - parts.push(p.text("interface ")); + parts.push(text!("interface ")); parts.push(self.id.format(p)); if let Some(type_parameters) = &self.type_parameters { parts.push(type_parameters.format(p)); } - parts.push(p.space()); + parts.push(text!(" ")); if let Some(extends) = &self.extends { if extends.len() > 0 { - let mut extends_parts = p.vec(); + let mut extends_parts = Vec::new_in(p.allocator); let mut display_comma = false; - extends_parts.push(p.text("extends ")); + extends_parts.push(text!("extends ")); for extend in extends { if display_comma { - extends_parts.push(p.text(", ")); + extends_parts.push(text!(", ")); } else { display_comma = true; } @@ -1217,69 +1208,69 @@ impl<'a> Format<'a> for TSInterfaceDeclaration<'a> { } parts.extend(extends_parts); - parts.push(p.space()); + parts.push(text!(" ")); } } - parts.push(p.text("{")); + parts.push(text!("{")); if self.body.body.len() > 0 { - let mut indent_parts = p.vec(); + let mut indent_parts = Vec::new_in(p.allocator); for sig in &self.body.body { - indent_parts.extend(p.hardline()); + indent_parts.extend(hardline!()); indent_parts.push(sig.format(p)); if let Some(semi) = p.semi() { indent_parts.push(semi); } } - parts.push(p.indent(indent_parts)); - parts.extend(p.hardline()); + parts.push(indent!(p, indent_parts)); + parts.extend(hardline!()); } - parts.push(p.text("}")); - p.array(parts) + parts.push(text!("}")); + array!(p, parts) } } impl<'a> Format<'a> for TSEnumDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if self.declare { - parts.push(p.text("declare ")); + parts.push(text!("declare ")); } if self.r#const { - parts.push(p.text("const ")); + parts.push(text!("const ")); } - parts.push(p.text("enum ")); + parts.push(text!("enum ")); parts.push(self.id.format(p)); - parts.push(p.text(" {")); + parts.push(text!(" {")); if self.members.len() > 0 { - let mut indent_parts = p.vec(); + let mut indent_parts = Vec::new_in(p.allocator); for member in &self.members { - indent_parts.extend(p.hardline()); + indent_parts.extend(hardline!()); indent_parts.push(member.format(p)); } - parts.push(p.indent(indent_parts)); - parts.extend(p.hardline()); + parts.push(indent!(p, indent_parts)); + parts.extend(hardline!()); } - parts.push(p.text("}")); + parts.push(text!("}")); - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSEnumMember<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.id.format(p)); if let Some(initializer) = &self.initializer { - parts.push(p.text(" = ")); + parts.push(text!(" = ")); parts.push(initializer.format(p)); } - parts.push(p.text(",")); + parts.push(text!(",")); - p.array(parts) + array!(p, parts) } } @@ -1294,32 +1285,32 @@ impl<'a> Format<'a> for TSEnumMemberName<'a> { impl<'a> Format<'a> for TSModuleDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if self.declare { - parts.push(p.text("declare ")); + parts.push(text!("declare ")); } - parts.push(p.text(self.kind.as_str())); - parts.push(p.space()); + parts.push(text!(self.kind.as_str())); + parts.push(text!(" ")); parts.push(self.id.format(p)); - parts.push(p.text(" {")); + parts.push(text!(" {")); if let Some(body) = &self.body { if !body.is_empty() { - let mut indent_parts = p.vec(); + let mut indent_parts = Vec::new_in(p.allocator); - indent_parts.extend(p.hardline()); + indent_parts.extend(hardline!()); indent_parts.push(body.format(p)); - parts.push(p.indent(indent_parts)); - parts.extend(p.hardline()); + parts.push(indent!(p, indent_parts)); + parts.extend(hardline!()); } } - parts.push(p.text("}")); + parts.push(text!("}")); - p.array(parts) + array!(p, parts) } } @@ -1345,12 +1336,12 @@ impl<'a> Format<'a> for TSModuleDeclarationBody<'a> { impl<'a> Format<'a> for TSModuleBlock<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let mut add_line = false; for body_part in &self.body { if add_line { - parts.push(p.line()); + parts.push(line!()); } else { add_line = true; } @@ -1358,28 +1349,28 @@ impl<'a> Format<'a> for TSModuleBlock<'a> { parts.push(body_part.format(p)); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSImportEqualsDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); - parts.push(p.text("import ")); + parts.push(text!("import ")); if self.import_kind == ImportOrExportKind::Type { - parts.push(p.text("type ")); + parts.push(text!("type ")); } parts.push(self.id.format(p)); - parts.push(p.text(" = ")); + parts.push(text!(" = ")); parts.push(self.module_reference.format(p)); if let Some(semi) = p.semi() { parts.push(semi); } - p.array(parts) + array!(p, parts) } } @@ -1405,48 +1396,48 @@ impl<'a> Format<'a> for TSTypeName<'a> { impl<'a> Format<'a> for TSExternalModuleReference<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let expression_doc = self.expression.format(p); - p.array(p_vec!(p, p.text("require("), expression_doc, p.text(")"))) + array!(p, [text!("require("), expression_doc, text!(")")]) } } impl<'a> Format<'a> for TSTypeParameter<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if self.r#in { - parts.push(p.text("in ")); + parts.push(text!("in ")); } if self.out { - parts.push(p.text("out ")); + parts.push(text!("out ")); } parts.push(self.name.format(p)); if let Some(constraint) = &self.constraint { - parts.push(p.text(" extends ")); + parts.push(text!(" extends ")); parts.push(constraint.format(p)); } if let Some(default) = &self.default { - parts.push(p.text(" = ")); + parts.push(text!(" = ")); parts.push(default.format(p)); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSTypeParameterDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let mut print_comma = false; - parts.push(p.text("<")); + parts.push(text!("<")); for param in &self.params { if print_comma { - parts.push(p.text(", ")); + parts.push(text!(", ")); } else { print_comma = true; } @@ -1454,22 +1445,22 @@ impl<'a> Format<'a> for TSTypeParameterDeclaration<'a> { parts.push(param.format(p)); } - parts.push(p.text(">")); + parts.push(text!(">")); - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSTypeParameterInstantiation<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let mut print_comma = false; - parts.push(p.text("<")); + parts.push(text!("<")); for param in &self.params { if print_comma { - parts.push(p.text(", ")); + parts.push(text!(", ")); } else { print_comma = true; } @@ -1477,9 +1468,9 @@ impl<'a> Format<'a> for TSTypeParameterInstantiation<'a> { parts.push(param.format(p)); } - parts.push(p.text(">")); + parts.push(text!(">")); - p.array(parts) + array!(p, parts) } } @@ -1560,10 +1551,10 @@ impl<'a> Format<'a> for FormalParameter<'a> { impl<'a> Format<'a> for ImportDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.text("import")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("import")); if self.import_kind.is_type() { - parts.push(p.text(" type")); + parts.push(text!(" type")); } if let Some(specifiers) = &self.specifiers { @@ -1579,20 +1570,20 @@ impl<'a> Format<'a> for ImportDeclaration<'a> { || specifiers.get(1).is_some_and(validate_namespace); parts.push(module::print_module_specifiers(p, specifiers, is_default, is_namespace)); - parts.push(p.text(" from")); + parts.push(text!(" from")); } - parts.push(p.space()); + parts.push(text!(" ")); parts.push(self.source.format(p)); if let Some(with_clause) = &self.with_clause { - parts.push(p.space()); + parts.push(text!(" ")); parts.push(with_clause.format(p)); } if let Some(semi) = p.semi() { parts.push(semi); } - p.array(parts) + array!(p, parts) } } @@ -1608,15 +1599,15 @@ impl<'a> Format<'a> for ImportDeclarationSpecifier<'a> { impl<'a> Format<'a> for ImportSpecifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let typed = if self.import_kind.is_type() { p.text("type ") } else { p.text("") }; + let typed = if self.import_kind.is_type() { text!("type ") } else { text!("") }; if self.imported.span() == self.local.span { let local_doc = self.local.format(p); - p.array(p_vec!(p, typed, local_doc)) + array!(p, [typed, local_doc]) } else { let imported_doc = self.imported.format(p); let local_doc = self.local.format(p); - p.array(p_vec!(p, typed, imported_doc, p.text(" as "), local_doc)) + array!(p, [typed, imported_doc, text!(" as "), local_doc]) } } } @@ -1630,7 +1621,7 @@ impl<'a> Format<'a> for ImportDefaultSpecifier<'a> { impl<'a> Format<'a> for ImportNamespaceSpecifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let local_doc = self.local.format(p); - p.array(p_vec!(p, p.text("* as "), local_doc)) + array!(p, [text!("* as "), local_doc]) } } @@ -1638,7 +1629,7 @@ impl<'a> Format<'a> for WithClause<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let attribute_keyword_doc = self.attributes_keyword.format(p); let with_clause_doc = object::print_object_properties(p, ObjectLike::WithClause(self)); - p.array(p_vec!(p, attribute_keyword_doc, p.space(), with_clause_doc)) + array!(p, [attribute_keyword_doc, text!(" "), with_clause_doc]) } } @@ -1646,7 +1637,7 @@ impl<'a> Format<'a> for ImportAttribute<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let key_doc = self.key.format(p); let value_doc = self.value.format(p); - p.array(p_vec!(p, key_doc, p.text(": "), value_doc)) + array!(p, [key_doc, text!(": "), value_doc]) } } @@ -1661,9 +1652,9 @@ impl<'a> Format<'a> for ImportAttributeKey<'a> { impl<'a> Format<'a> for ExportNamedDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if let Some(decl) = &self.declaration { - parts.push(p.space()); + parts.push(text!(" ")); parts.push(decl.format(p)); } else { parts.push(module::print_module_specifiers( @@ -1673,21 +1664,21 @@ impl<'a> Format<'a> for ExportNamedDeclaration<'a> { /* include_namespace */ false, )); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSExportAssignment<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let expression_doc = self.expression.format(p); - p.array(p_vec!(p, p.text(" = "), expression_doc)) + array!(p, [text!(" = "), expression_doc]) } } impl<'a> Format<'a> for TSNamespaceExportDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let id_doc = self.id.format(p); - p.array(p_vec!(p, p.text(" as namespace "), id_doc, p.text(";"))) + array!(p, [text!(" as namespace "), id_doc, text!(";")]) } } @@ -1698,7 +1689,7 @@ impl<'a> Format<'a> for ExportSpecifier<'a> { } else { let local_doc = self.local.format(p); let exported_doc = self.exported.format(p); - p.array(p_vec!(p, local_doc, p.text(" as "), exported_doc)) + array!(p, [local_doc, text!(" as "), exported_doc]) } } } @@ -1715,13 +1706,13 @@ impl<'a> Format<'a> for ModuleExportName<'a> { impl<'a> Format<'a> for ExportAllDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.text(" *")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!(" *")); if let Some(exported) = &self.exported { - parts.push(p.text(" as ")); + parts.push(text!(" as ")); parts.push(exported.format(p)); } - p.array(parts) + array!(p, parts) } } @@ -1790,37 +1781,37 @@ impl<'a> Format<'a> for Expression<'a> { impl<'a> Format<'a> for IdentifierReference<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - wrap!(p, self, IdentifierReference, { p.dynamic_text(self.name.as_str()) }) + wrap!(p, self, IdentifierReference, { dynamic_text!(p, self.name.as_str()) }) } } impl<'a> Format<'a> for IdentifierName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.dynamic_text(self.name.as_str()) + dynamic_text!(p, self.name.as_str()) } } impl<'a> Format<'a> for BindingIdentifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - wrap!(p, self, BindingIdentifier, { p.dynamic_text(self.name.as_str()) }) + wrap!(p, self, BindingIdentifier, { dynamic_text!(p, self.name.as_str()) }) } } impl<'a> Format<'a> for LabelIdentifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.dynamic_text(self.name.as_str()) + dynamic_text!(p, self.name.as_str()) } } impl<'a> Format<'a> for BooleanLiteral { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text(if self.value { "true" } else { "false" }) + text!(if self.value { "true" } else { "false" }) } } impl<'a> Format<'a> for NullLiteral { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("null") + text!("null") } } @@ -1882,7 +1873,7 @@ impl<'a> Format<'a> for NumericLiteral<'a> { } } - p.dynamic_text(&string) + dynamic_text!(p, &string) }) } } @@ -1890,20 +1881,20 @@ impl<'a> Format<'a> for NumericLiteral<'a> { impl<'a> Format<'a> for BigIntLiteral<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self.span.source_text(p.source_text).cow_to_ascii_lowercase() { - Cow::Borrowed(s) => p.dynamic_text(s), - Cow::Owned(s) => p.dynamic_text(&s), + Cow::Borrowed(s) => dynamic_text!(p, s), + Cow::Owned(s) => dynamic_text!(p, &s), } } } impl<'a> Format<'a> for RegExpLiteral<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.text("/")); - parts.push(p.dynamic_text(self.regex.pattern.source_text(p.source_text).as_ref())); - parts.push(p.text("/")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("/")); + parts.push(dynamic_text!(p, self.regex.pattern.source_text(p.source_text).as_ref())); + parts.push(text!("/")); parts.push(self.regex.flags.format(p)); - p.array(parts) + array!(p, parts) } } @@ -1912,14 +1903,14 @@ impl<'a> Format<'a> for StringLiteral<'a> { wrap!(p, self, StringLiteral, { let raw = &p.source_text[(self.span.start + 1) as usize..(self.span.end - 1) as usize]; // TODO: implement `makeString` from prettier/src/utils/print-string.js - p.dynamic_text(string::print_string(p, raw, p.options.single_quote)) + dynamic_text!(p, string::print_string(p, raw, p.options.single_quote)) }) } } impl<'a> Format<'a> for ThisExpression { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("this") + text!("this") } } @@ -1937,38 +1928,38 @@ impl<'a> Format<'a> for MemberExpression<'a> { impl<'a> Format<'a> for ComputedMemberExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.object.format(p)); if self.optional { - parts.push(p.text("?.")); + parts.push(text!("?.")); } - parts.push(p.text("[")); + parts.push(text!("[")); parts.push(self.expression.format(p)); - parts.push(p.text("]")); - p.array(parts) + parts.push(text!("]")); + array!(p, parts) } } impl<'a> Format<'a> for StaticMemberExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.object.format(p)); if self.optional { - parts.push(p.text("?")); + parts.push(text!("?")); } - parts.push(p.text(".")); + parts.push(text!(".")); parts.push(self.property.format(p)); - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for PrivateFieldExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.object.format(p)); - parts.push(if self.optional { p.text("?.") } else { p.text(".") }); + parts.push(if self.optional { text!("?.") } else { text!(".") }); parts.push(self.field.format(p)); - p.array(parts) + array!(p, parts) } } @@ -1997,7 +1988,7 @@ impl<'a> Format<'a> for ArrayExpressionElement<'a> { match self { Self::SpreadElement(expr) => expr.format(p), match_expression!(Self) => self.to_expression().format(p), - Self::Elision(elision) => p.text(""), + Self::Elision(elision) => text!(""), } } } @@ -2006,7 +1997,7 @@ impl<'a> Format<'a> for SpreadElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, SpreadElement, { let argument_doc = self.argument.format(p); - p.array(p_vec!(p, p.text("..."), argument_doc)) + array!(p, [text!("..."), argument_doc]) }) } } @@ -2038,15 +2029,15 @@ impl<'a> Format<'a> for ObjectProperty<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, ObjectProperty, { if self.method || self.kind == PropertyKind::Get || self.kind == PropertyKind::Set { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let mut method = self.method; match self.kind { PropertyKind::Get => { - parts.push(p.text("get ")); + parts.push(text!("get ")); method = true; } PropertyKind::Set => { - parts.push(p.text("set ")); + parts.push(text!("set ")); method = true; } PropertyKind::Init => (), @@ -2063,10 +2054,10 @@ impl<'a> Format<'a> for ObjectProperty<'a> { } } else { parts.push(self.key.format(p)); - parts.push(p.text(": ")); + parts.push(text!(": ")); parts.push(self.value.format(p)); } - return p.group(p.array(parts)); + return group!(p, parts); } if self.shorthand { @@ -2075,7 +2066,7 @@ impl<'a> Format<'a> for ObjectProperty<'a> { let left_doc = if self.computed { let key_doc = self.key.format(p); - p.array(p_vec!(p, p.text("["), key_doc, p.text("]"))) + array!(p, [text!("["), key_doc, text!("]")]) } else { self.key.format(p) }; @@ -2084,7 +2075,7 @@ impl<'a> Format<'a> for ObjectProperty<'a> { p, assignment::AssignmentLikeNode::ObjectProperty(self), left_doc, - p.text(":"), + text!(":"), Some(&self.value), ) }) @@ -2099,16 +2090,16 @@ impl<'a> Format<'a> for PropertyKey<'a> { _ => false, }; if is_parent_computed { - let mut parts = p.vec(); - parts.push(p.text("[")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("[")); let doc = match self { PropertyKey::StaticIdentifier(ident) => ident.format(p), PropertyKey::PrivateIdentifier(ident) => ident.format(p), match_expression!(PropertyKey) => self.to_expression().format(p), }; parts.push(doc); - parts.push(p.text("]")); - return p.array(parts); + parts.push(text!("]")); + return array!(p, parts); } wrap!(p, self, PropertyKey, { @@ -2133,7 +2124,10 @@ impl<'a> Format<'a> for PropertyKey<'a> { match self { PropertyKey::StaticIdentifier(ident) => { if need_quote { - p.dynamic_text(string::print_string(p, &ident.name, p.options.single_quote)) + dynamic_text!( + p, + string::print_string(p, &ident.name, p.options.single_quote) + ) } else { ident.format(p) } @@ -2147,25 +2141,27 @@ impl<'a> Format<'a> for PropertyKey<'a> { && (p.options.quote_props.as_needed() || (p.options.quote_props.consistent()/* && !needsQuoteProps.get(parent) */)) { - p.dynamic_text(literal.value.as_str()) + dynamic_text!(p, literal.value.as_str()) } else { - p.dynamic_text(string::print_string( + dynamic_text!( p, - literal.value.as_str(), - p.options.single_quote, - )) + string::print_string(p, literal.value.as_str(), p.options.single_quote,) + ) } } PropertyKey::NumericLiteral(literal) => { if need_quote { - p.dynamic_text(string::print_string(p, literal.raw, p.options.single_quote)) + dynamic_text!( + p, + string::print_string(p, literal.raw, p.options.single_quote) + ) } else { literal.format(p) } } PropertyKey::Identifier(ident) => { let ident_doc = ident.format(p); - p.array(p_vec!(p, p.text("["), ident_doc, p.text("]"))) + array!(p, [text!("["), ident_doc, text!("]")]) } match_expression!(PropertyKey) => self.to_expression().format(p), } @@ -2182,16 +2178,16 @@ impl<'a> Format<'a> for ArrowFunctionExpression<'a> { impl<'a> Format<'a> for YieldExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, YieldExpression, { - let mut parts = p.vec(); - parts.push(p.text("yield")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("yield")); if self.delegate { - parts.push(p.text("*")); + parts.push(text!("*")); } if let Some(argument) = &self.argument { - parts.push(p.space()); + parts.push(text!(" ")); parts.push(argument.format(p)); } - p.array(parts) + array!(p, parts) }) } } @@ -2201,10 +2197,10 @@ impl<'a> Format<'a> for UpdateExpression<'a> { wrap!(p, self, UpdateExpression, { if self.prefix { let argument_doc = self.argument.format(p); - p.array(p_vec!(p, p.text(self.operator.as_str()), argument_doc)) + array!(p, [text!(self.operator.as_str()), argument_doc]) } else { let argument_doc = self.argument.format(p); - p.array(p_vec!(p, argument_doc, p.text(self.operator.as_str()))) + array!(p, [argument_doc, text!(self.operator.as_str())]) } }) } @@ -2213,13 +2209,13 @@ impl<'a> Format<'a> for UpdateExpression<'a> { impl<'a> Format<'a> for UnaryExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, UnaryExpression, { - let mut parts = p.vec(); - parts.push(p.dynamic_text(self.operator.as_str())); + let mut parts = Vec::new_in(p.allocator); + parts.push(dynamic_text!(p, self.operator.as_str())); if self.operator.is_keyword() { - parts.push(p.space()); + parts.push(text!(" ")); } parts.push(self.argument.format(p)); - p.array(parts) + array!(p, parts) }) } } @@ -2234,7 +2230,7 @@ impl<'a> Format<'a> for BinaryExpression<'a> { &self.right, ); if misc::in_parentheses(p.parent_kind(), p.source_text, self.span) { - p.group(p.array(p_vec!(p, p.indent(p_vec!(p, p.softline(), doc)), p.softline(),))) + group!(p, [indent!(p, [softline!(), doc]), softline!(),]) } else { doc } @@ -2247,14 +2243,7 @@ impl<'a> Format<'a> for PrivateInExpression<'a> { wrap!(p, self, PrivateInExpression, { let left_doc = self.left.format(p); let right_doc = self.right.format(p); - p.array(p_vec!( - p, - left_doc, - p.space(), - p.text(self.operator.as_str()), - p.space(), - right_doc - )) + array!(p, [left_doc, text!(" "), text!(self.operator.as_str()), text!(" "), right_doc]) }) } } @@ -2270,7 +2259,7 @@ impl<'a> Format<'a> for LogicalExpression<'a> { ); if misc::in_parentheses(p.parent_kind(), p.source_text, self.span) { - p.group(p.array(p_vec!(p, p.indent(p_vec!(p, p.softline(), doc)), p.softline()))) + group!(p, [indent!(p, [softline!(), doc]), softline!()]) } else { doc } @@ -2349,7 +2338,7 @@ impl<'a> Format<'a> for AssignmentTargetWithDefault<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let binding_doc = self.binding.format(p); let init_doc = self.init.format(p); - p.array(p_vec!(p, binding_doc, p.text(" = "), init_doc)) + array!(p, [binding_doc, text!(" = "), init_doc]) } } @@ -2364,13 +2353,13 @@ impl<'a> Format<'a> for AssignmentTargetProperty<'a> { impl<'a> Format<'a> for AssignmentTargetPropertyIdentifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.binding.format(p)); if let Some(init) = &self.init { - parts.push(p.text(" = ")); + parts.push(text!(" = ")); parts.push(init.format(p)); } - p.array(parts) + array!(p, parts) } } @@ -2378,14 +2367,14 @@ impl<'a> Format<'a> for AssignmentTargetPropertyProperty<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let name_doc = self.name.format(p); let binding_doc = self.binding.format(p); - p.array(p_vec!(p, name_doc, p.text(": "), binding_doc)) + array!(p, [name_doc, text!(": "), binding_doc]) } } impl<'a> Format<'a> for AssignmentTargetRest<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let target_doc = self.target.format(p); - p.array(p_vec!(p, p.text("..."), target_doc)) + array!(p, [text!("..."), target_doc]) } } @@ -2394,7 +2383,7 @@ impl<'a> Format<'a> for SequenceExpression<'a> { wrap!(p, self, SequenceExpression, { let docs = self.expressions.iter().map(|expr| expr.format(p)).collect::>(); - p.group(p.array(p_vec!(p, p.array(p.join(Separator::CommaLine, docs))))) + group!(p, [join!(p, JoinSeparator::CommaLine, docs)]) }) } } @@ -2408,24 +2397,24 @@ impl<'a> Format<'a> for ParenthesizedExpression<'a> { impl<'a> Format<'a> for ImportExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, ImportExpression, { - let mut parts = p.vec(); - parts.push(p.text("import")); - parts.push(p.text("(")); - let mut indent_parts = p.vec(); - indent_parts.push(p.softline()); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("import")); + parts.push(text!("(")); + let mut indent_parts = Vec::new_in(p.allocator); + indent_parts.push(softline!()); indent_parts.push(self.source.format(p)); if !self.arguments.is_empty() { for arg in &self.arguments { - indent_parts.push(p.text(",")); - indent_parts.push(p.line()); + indent_parts.push(text!(",")); + indent_parts.push(line!()); indent_parts.push(arg.format(p)); } } - parts.push(p.group(p.indent(indent_parts))); - parts.push(p.softline()); - parts.push(p.text(")")); + parts.push(group!(p, [indent!(p, indent_parts)])); + parts.push(softline!()); + parts.push(text!(")")); - p.group(p.array(parts)) + group!(p, parts) }) } } @@ -2439,43 +2428,43 @@ impl<'a> Format<'a> for TemplateLiteral<'a> { impl<'a> Format<'a> for TemplateElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { // TODO: `replaceEndOfLine` - p.dynamic_text(self.value.raw.as_str()) + dynamic_text!(p, self.value.raw.as_str()) } } impl<'a> Format<'a> for TaggedTemplateExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, TaggedTemplateExpression, { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.tag.format(p)); if let Some(type_parameters) = &self.type_parameters { - parts.push(p.text("<")); + parts.push(text!("<")); parts.push(type_parameters.format(p)); - parts.push(p.text(">")); + parts.push(text!(">")); } parts.push(self.quasi.format(p)); - p.array(parts) + array!(p, parts) }) } } impl<'a> Format<'a> for Super { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("super") + text!("super") } } impl<'a> Format<'a> for AwaitExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, AwaitExpression, { - let mut parts = p.vec(); - parts.push(p.text("await ")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("await ")); parts.push(self.argument.format(p)); - p.array(parts) + array!(p, parts) }) } } @@ -2511,7 +2500,7 @@ impl<'a> Format<'a> for MetaProperty<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let meta_doc = self.meta.format(p); let property_doc = self.property.format(p); - p.array(p_vec!(p, meta_doc, p.text("."), property_doc)) + array!(p, [meta_doc, text!("."), property_doc]) } } @@ -2541,7 +2530,7 @@ impl<'a> Format<'a> for ClassElement<'a> { impl<'a> Format<'a> for TSClassImplements<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.expression.format(p)); @@ -2549,7 +2538,7 @@ impl<'a> Format<'a> for TSClassImplements<'a> { parts.push(type_parameters.format(p)); } - p.array(parts) + array!(p, parts) } } @@ -2557,7 +2546,7 @@ impl<'a> Format<'a> for TSTypeAssertion<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let type_annotation_doc = self.type_annotation.format(p); let expression_doc = self.expression.format(p); - p.array(p_vec!(p, p.text("<"), type_annotation_doc, p.text(">"), expression_doc)) + array!(p, [text!("<"), type_annotation_doc, text!(">"), expression_doc]) } } @@ -2565,7 +2554,7 @@ impl<'a> Format<'a> for TSSatisfiesExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let expression_doc = self.expression.format(p); let type_annotation_doc = self.type_annotation.format(p); - p.array(p_vec!(p, expression_doc, p.text(" satisfies "), type_annotation_doc)) + array!(p, [expression_doc, text!(" satisfies "), type_annotation_doc]) } } @@ -2573,20 +2562,20 @@ impl<'a> Format<'a> for TSInstantiationExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let expression_doc = self.expression.format(p); let type_parameters_doc = self.type_parameters.format(p); - p.array(p_vec!(p, expression_doc, type_parameters_doc)) + array!(p, [expression_doc, type_parameters_doc]) } } impl<'a> Format<'a> for TSNonNullExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let expression_doc = self.expression.format(p); - p.array(p_vec!(p, expression_doc, p.text("!"))) + array!(p, [expression_doc, text!("!")]) } } impl<'a> Format<'a> for JSXIdentifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.dynamic_text(self.name.as_str()) + dynamic_text!(p, self.name.as_str()) } } @@ -2604,7 +2593,7 @@ impl<'a> Format<'a> for JSXMemberExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let object_doc = self.object.format(p); let property_doc = self.property.format(p); - p.array(p_vec!(p, object_doc, p.text("."), property_doc)) + array!(p, [object_doc, text!("."), property_doc]) } } @@ -2624,7 +2613,7 @@ impl<'a> Format<'a> for JSXNamespacedName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let namespace_doc = self.namespace.format(p); let property_doc = self.property.format(p); - p.array(p_vec!(p, namespace_doc, p.text(":"), property_doc)) + array!(p, [namespace_doc, text!(":"), property_doc]) } } @@ -2639,21 +2628,21 @@ impl<'a> Format<'a> for JSXAttributeName<'a> { impl<'a> Format<'a> for JSXAttribute<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.name.format(p)); if let Some(value) = &self.value { - parts.push(p.text("=")); + parts.push(text!("=")); parts.push(value.format(p)); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for JSXEmptyExpression { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("") + text!("") } } @@ -2708,7 +2697,7 @@ impl<'a> Format<'a> for JSXExpression<'a> { impl<'a> Format<'a> for JSXExpressionContainer<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let expression_doc = self.expression.format(p); - p.array(p_vec!(p, p.text("{"), expression_doc, p.text("}"))) + array!(p, [text!("{"), expression_doc, text!("}")]) } } @@ -2726,7 +2715,7 @@ impl<'a> Format<'a> for JSXAttributeValue<'a> { impl<'a> Format<'a> for JSXSpreadAttribute<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let argument_doc = self.argument.format(p); - p.array(p_vec!(p, p.text("..."), argument_doc)) + array!(p, [text!("..."), argument_doc]) } } @@ -2741,9 +2730,9 @@ impl<'a> Format<'a> for JSXAttributeItem<'a> { impl<'a> Format<'a> for JSXOpeningElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); - parts.push(p.text("<")); + parts.push(text!("<")); parts.push(self.name.format(p)); if let Some(type_parameters) = &self.type_parameters { @@ -2751,31 +2740,31 @@ impl<'a> Format<'a> for JSXOpeningElement<'a> { } for attribute in &self.attributes { - parts.push(p.space()); + parts.push(text!(" ")); parts.push(attribute.format(p)); } if self.self_closing { - parts.push(p.space()); - parts.push(p.text("/")); + parts.push(text!(" ")); + parts.push(text!("/")); } - parts.push(p.text(">")); + parts.push(text!(">")); - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for JSXClosingElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let name_doc = self.name.format(p); - p.array(p_vec!(p, p.text(""))) + array!(p, [text!("")]) } } impl<'a> Format<'a> for JSXElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.opening_element.format(p)); @@ -2787,32 +2776,32 @@ impl<'a> Format<'a> for JSXElement<'a> { parts.push(closing_element.format(p)); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for JSXOpeningFragment { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("<>") + text!("<>") } } impl<'a> Format<'a> for JSXClosingFragment { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.text("") + text!("") } } impl<'a> Format<'a> for JSXText<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.dynamic_text(self.value.as_str()) + dynamic_text!(p, self.value.as_str()) } } impl<'a> Format<'a> for JSXSpreadChild<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let expression_doc = self.expression.format(p); - p.array(p_vec!(p, p.text("..."), expression_doc)) + array!(p, [text!("..."), expression_doc]) } } @@ -2830,7 +2819,7 @@ impl<'a> Format<'a> for JSXChild<'a> { impl<'a> Format<'a> for JSXFragment<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(self.opening_fragment.format(p)); @@ -2840,7 +2829,7 @@ impl<'a> Format<'a> for JSXFragment<'a> { parts.push(self.closing_fragment.format(p)); - p.array(parts) + array!(p, parts) } } @@ -2848,7 +2837,7 @@ impl<'a> Format<'a> for StaticBlock<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, StaticBlock, { let block_doc = block::print_block(p, &self.body, None); - p.array(p_vec!(p, p.text("static "), block_doc)) + array!(p, [text!("static "), block_doc]) }) } } @@ -2875,16 +2864,16 @@ impl<'a> Format<'a> for AccessorProperty<'a> { impl<'a> Format<'a> for PrivateIdentifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.text("#")); - parts.push(p.dynamic_text(self.name.as_str())); - p.array(parts) + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("#")); + parts.push(dynamic_text!(p, self.name.as_str())); + array!(p, parts) } } impl<'a> Format<'a> for BindingPattern<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); parts.push(match self.kind { BindingPatternKind::BindingIdentifier(ref ident) => ident.format(p), BindingPatternKind::ObjectPattern(ref pattern) => pattern.format(p), @@ -2893,14 +2882,14 @@ impl<'a> Format<'a> for BindingPattern<'a> { }); if self.optional { - parts.push(p.text("?")); + parts.push(text!("?")); } if let Some(typ) = &self.type_annotation { let type_annotation_doc = typ.type_annotation.format(p); - parts.push(p.array(p_vec!(p, p.text(": "), type_annotation_doc))); + parts.push(array!(p, [text!(": "), type_annotation_doc])); } - p.array(parts) + array!(p, parts) } } @@ -2919,7 +2908,7 @@ impl<'a> Format<'a> for BindingProperty<'a> { } else { let key_doc = self.key.format(p); let value_doc = self.value.format(p); - p.group(p.array(p_vec!(p, key_doc, p.text(": "), value_doc))) + group!(p, [key_doc, text!(": "), value_doc]) } } } @@ -2927,7 +2916,7 @@ impl<'a> Format<'a> for BindingProperty<'a> { impl<'a> Format<'a> for BindingRestElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let argument_doc = self.argument.format(p); - p.array(p_vec!(p, p.text("..."), argument_doc)) + array!(p, [text!("..."), argument_doc]) } } @@ -2942,7 +2931,7 @@ impl<'a> Format<'a> for AssignmentPattern<'a> { wrap!(p, self, AssignmentPattern, { let left_doc = self.left.format(p); let right_doc = self.right.format(p); - p.array(p_vec!(p, left_doc, p.text(" = "), right_doc)) + array!(p, [left_doc, text!(" = "), right_doc]) }) } } @@ -2975,58 +2964,58 @@ impl<'a> Format<'a> for RegExpFlags { string.push('y'); } let sorted = string.iter().collect::(); - p.dynamic_text(&sorted) + dynamic_text!(p, &sorted) } } impl<'a> Format<'a> for TSIndexSignature<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if self.readonly { - parts.push(p.text("readonly ")); + parts.push(text!("readonly ")); } - parts.push(p.text("[")); + parts.push(text!("[")); for param in &self.parameters { parts.push(param.format(p)); } - parts.push(p.text("]: ")); + parts.push(text!("]: ")); parts.push(self.type_annotation.type_annotation.format(p)); - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSIndexSignatureName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let type_annotation_doc = self.type_annotation.type_annotation.format(p); - p.array(p_vec!(p, p.dynamic_text(self.name.as_str()), p.text(": "), type_annotation_doc)) + array!(p, [dynamic_text!(p, self.name.as_str()), text!(": "), type_annotation_doc]) } } impl<'a> Format<'a> for TSPropertySignature<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if self.readonly { - parts.push(p.text("readonly ")); + parts.push(text!("readonly ")); } parts.push(self.key.format(p)); if let Some(ty) = &self.type_annotation { if self.optional { - parts.push(p.text("?")); + parts.push(text!("?")); } - parts.push(p.text(":")); - parts.push(p.space()); + parts.push(text!(":")); + parts.push(text!(" ")); parts.push(ty.type_annotation.format(p)); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSCallSignatureDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if let Some(type_parameters) = &self.type_parameters { parts.push(type_parameters.format(p)); @@ -3035,19 +3024,19 @@ impl<'a> Format<'a> for TSCallSignatureDeclaration<'a> { parts.push(self.params.format(p)); if let Some(return_type) = &self.return_type { - parts.push(p.text(": ")); + parts.push(text!(": ")); parts.push(return_type.type_annotation.format(p)); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSConstructSignatureDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); - parts.push(p.text("new ")); + parts.push(text!("new ")); if let Some(type_parameters) = &self.type_parameters { parts.push(type_parameters.format(p)); @@ -3056,30 +3045,30 @@ impl<'a> Format<'a> for TSConstructSignatureDeclaration<'a> { parts.push(self.params.format(p)); if let Some(return_type) = &self.return_type { - parts.push(p.text(": ")); + parts.push(text!(": ")); parts.push(return_type.type_annotation.format(p)); } - p.array(parts) + array!(p, parts) } } impl<'a> Format<'a> for TSMethodSignature<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if self.computed { - parts.push(p.text("[")); + parts.push(text!("[")); } parts.push(self.key.format(p)); if self.computed { - parts.push(p.text("]")); + parts.push(text!("]")); } if self.optional { - parts.push(p.text("?")); + parts.push(text!("?")); } if let Some(type_parameters) = &self.type_parameters { @@ -3089,11 +3078,11 @@ impl<'a> Format<'a> for TSMethodSignature<'a> { parts.push(self.params.format(p)); if let Some(return_type) = &self.return_type { - parts.push(p.text(": ")); + parts.push(text!(": ")); parts.push(return_type.type_annotation.format(p)); } - p.array(parts) + array!(p, parts) } } @@ -3113,6 +3102,6 @@ impl<'a> Format<'a> for TSAsExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let expression_doc = self.expression.format(p); let type_annotation_doc = self.type_annotation.format(p); - p.array(p_vec!(p, expression_doc, p.text(" as "), type_annotation_doc)) + array!(p, [expression_doc, text!(" as "), type_annotation_doc]) } } diff --git a/crates/oxc_prettier/src/format/module.rs b/crates/oxc_prettier/src/format/module.rs index 2c554c828e494d..c044d90bf3a3b0 100644 --- a/crates/oxc_prettier/src/format/module.rs +++ b/crates/oxc_prettier/src/format/module.rs @@ -4,8 +4,9 @@ use oxc_allocator::Vec; use oxc_ast::ast::*; use crate::{ - ir::{Doc, DocBuilder, Separator}, - p_vec, Format, Prettier, + array, group, if_break, indent, + ir::{Doc, JoinSeparator}, + join, line, softline, text, Format, Prettier, }; pub(super) fn print_export_declaration<'a>( @@ -14,11 +15,11 @@ pub(super) fn print_export_declaration<'a>( ) -> Doc<'a> { debug_assert!(decl.is_export()); - let mut parts = p.vec(); - parts.push(p.text("export")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("export")); if decl.is_default_export() { - parts.push(p.text(" default ")); + parts.push(text!(" default ")); } parts.push(match decl { @@ -31,12 +32,12 @@ pub(super) fn print_export_declaration<'a>( }); if let Some(source) = decl.source() { - parts.push(p.text(" from ")); + parts.push(text!(" from ")); parts.push(source.format(p)); } if let Some(with_clause) = decl.with_clause() { - parts.push(p.space()); + parts.push(text!(" ")); parts.push(with_clause.format(p)); } @@ -44,7 +45,7 @@ pub(super) fn print_export_declaration<'a>( parts.push(doc); } - p.array(parts) + array!(p, parts) } fn print_semicolon_after_export_declaration<'a>( @@ -57,12 +58,12 @@ fn print_semicolon_after_export_declaration<'a>( match decl { ModuleDeclaration::ExportDefaultDeclaration(decl) => match decl.declaration { - match_expression!(ExportDefaultDeclarationKind) => Some(p.text(";")), + match_expression!(ExportDefaultDeclarationKind) => Some(text!(";")), _ => None, }, ModuleDeclaration::ExportNamedDeclaration(decl) => { let Some(declaration) = &decl.declaration else { - return Some(p.text(";")); + return Some(text!(";")); }; match declaration { @@ -70,11 +71,11 @@ fn print_semicolon_after_export_declaration<'a>( | Declaration::VariableDeclaration(_) | Declaration::ClassDeclaration(_) | Declaration::TSModuleDeclaration(_) => None, - _ => Some(p.text(";")), + _ => Some(text!(";")), } } ModuleDeclaration::ExportAllDeclaration(_) | ModuleDeclaration::TSExportAssignment(_) => { - Some(p.text(";")) + Some(text!(";")) } _ => None, } @@ -86,24 +87,24 @@ pub fn print_module_specifiers<'a, T: Format<'a>>( include_default: bool, include_namespace: bool, ) -> Doc<'a> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); if specifiers.is_empty() { - parts.push(p.text(" {}")); + parts.push(text!(" {}")); } else { - parts.push(p.space()); + parts.push(text!(" ")); let mut specifiers_iter: VecDeque<_> = specifiers.iter().collect(); if include_default { parts.push(specifiers_iter.pop_front().unwrap().format(p)); if !specifiers_iter.is_empty() { - parts.push(p.text(", ")); + parts.push(text!(", ")); } } if include_namespace { parts.push(specifiers_iter.pop_front().unwrap().format(p)); if !specifiers_iter.is_empty() { - parts.push(p.text(", ")); + parts.push(text!(", ")); } } @@ -113,35 +114,35 @@ pub fn print_module_specifiers<'a, T: Format<'a>>( if can_break { let docs = specifiers_iter.iter().map(|s| s.format(p)).collect::>(); - parts.push(p.group(p.array(p_vec!( + parts.push(group!( p, - p.text("{"), - p.indent(p_vec!( - p, - if p.options.bracket_spacing { p.line() } else { p.softline() }, - p.array(p.join(Separator::CommaLine, docs)) - )), - p.if_break( - p.text(if p.should_print_es5_comma() { "," } else { "" }), - p.text(""), - None, - ), - if p.options.bracket_spacing { p.line() } else { p.softline() }, - p.text("}"), - )))); + [ + text!("{"), + indent!( + p, + [ + if p.options.bracket_spacing { line!() } else { softline!() }, + join!(p, JoinSeparator::CommaLine, docs) + ] + ), + if_break!(p, text!(if p.should_print_es5_comma() { "," } else { "" })), + if p.options.bracket_spacing { line!() } else { softline!() }, + text!("}"), + ] + )); } else { - parts.push(p.text("{")); + parts.push(text!("{")); if p.options.bracket_spacing { - parts.push(p.space()); + parts.push(text!(" ")); } parts.extend(specifiers_iter.iter().map(|s| s.format(p))); if p.options.bracket_spacing { - parts.push(p.space()); + parts.push(text!(" ")); } - parts.push(p.text("}")); + parts.push(text!("}")); } } } - p.array(parts) + array!(p, parts) } diff --git a/crates/oxc_prettier/src/format/object.rs b/crates/oxc_prettier/src/format/object.rs index 6480142bc9e98c..aa411fb2b9b8e8 100644 --- a/crates/oxc_prettier/src/format/object.rs +++ b/crates/oxc_prettier/src/format/object.rs @@ -1,3 +1,4 @@ +use oxc_allocator::Vec; use oxc_ast::{ ast::{ObjectAssignmentTarget, ObjectExpression, ObjectPattern, TSTypeLiteral, WithClause}, AstKind, @@ -5,9 +6,11 @@ use oxc_ast::{ use oxc_span::Span; use crate::{ + array, format::{function_parameters, misc}, - ir::{Doc, DocBuilder}, - p_vec, Format, Prettier, + group, if_break, indent, + ir::Doc, + line, softline, text, Format, Prettier, }; #[derive(Debug, Clone, Copy)] @@ -99,34 +102,34 @@ pub(super) fn print_object_properties<'a>( p: &mut Prettier<'a>, object: ObjectLike<'a, '_>, ) -> Doc<'a> { - let left_brace = p.text("{"); - let right_brace = p.text("}"); + let left_brace = text!("{"); + let right_brace = text!("}"); let should_break = false; let member_separator = object.member_separator(p); let content = if object.is_empty() { - p.group(p.array(p_vec!(p, left_brace, p.softline(), right_brace))) + group!(p, [left_brace, softline!(), right_brace]) } else { - let mut parts = p.vec(); - parts.push(p.text("{")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("{")); let indent_parts = { let len = object.len(); let has_rest = object.has_rest(); - let mut indent_parts = p.vec(); + let mut indent_parts = Vec::new_in(p.allocator); - indent_parts.push(if p.options.bracket_spacing { p.line() } else { p.softline() }); + indent_parts.push(if p.options.bracket_spacing { line!() } else { softline!() }); - let object_docs = object.iter(p).collect::>(); + let object_docs = object.iter(p).collect::>(); for (i, doc) in object_docs.into_iter().enumerate() { indent_parts.push(doc); if i == len - 1 && !has_rest { break; } - indent_parts.push(p.text(member_separator)); - indent_parts.push(p.line()); + indent_parts.push(text!(member_separator)); + indent_parts.push(line!()); } match object { @@ -146,22 +149,22 @@ pub(super) fn print_object_properties<'a>( } indent_parts }; - parts.push(p.indent(indent_parts)); + parts.push(indent!(p, indent_parts)); if p.should_print_es5_comma() && match object { ObjectLike::Pattern(pattern) => pattern.rest.is_none(), _ => true, } { - parts.push(p.if_break(p.text(member_separator), p.text(""), None)); + parts.push(if_break!(p, text!(member_separator))); } - parts.push(if p.options.bracket_spacing { p.line() } else { p.softline() }); - parts.push(p.text("}")); + parts.push(if p.options.bracket_spacing { line!() } else { softline!() }); + parts.push(text!("}")); if matches!(p.current_kind(), AstKind::Program(_)) { let should_break = misc::has_new_line_in_range(p.source_text, object.span().start, object.span().end); - return p.group_with_opts(p.array(parts), should_break, None); + return group!(p, parts, should_break, None); } let parent_kind = p.parent_kind(); @@ -173,11 +176,11 @@ pub(super) fn print_object_properties<'a>( AstKind::AssignmentExpression(_) | AstKind::VariableDeclarator(_) )) { - p.array(parts) + array!(p, parts) } else { let should_break = misc::has_new_line_in_range(p.source_text, object.span().start, object.span().end); - p.group_with_opts(p.array(parts), should_break, None) + group!(p, parts, should_break, None) } }; diff --git a/crates/oxc_prettier/src/format/statement.rs b/crates/oxc_prettier/src/format/statement.rs index b44cf06ca913bd..15b65e0b1a4ab5 100644 --- a/crates/oxc_prettier/src/format/statement.rs +++ b/crates/oxc_prettier/src/format/statement.rs @@ -2,10 +2,7 @@ use oxc_allocator::Vec; use oxc_ast::ast::Statement; use oxc_span::GetSpan; -use crate::{ - ir::{Doc, DocBuilder}, - Format, Prettier, -}; +use crate::{hardline, ir::Doc, Format, Prettier}; pub(super) fn print_statement_sequence<'a>( p: &mut Prettier<'a>, @@ -13,7 +10,7 @@ pub(super) fn print_statement_sequence<'a>( remove_last_statement_hardline: bool, skip_empty_statement: bool, ) -> Vec<'a, Doc<'a>> { - let mut parts = p.vec(); + let mut parts = Vec::new_in(p.allocator); let last_statement_span = stmts.iter().rev().find(|s| !matches!(s, Statement::EmptyStatement(_))).map(GetSpan::span); @@ -26,9 +23,9 @@ pub(super) fn print_statement_sequence<'a>( parts.push(stmt.format(p)); if Some(stmt.span()) != last_statement_span { - parts.extend(p.hardline()); + parts.extend(hardline!()); if p.is_next_line_empty(stmt.span()) { - parts.extend(p.hardline()); + parts.extend(hardline!()); } } } diff --git a/crates/oxc_prettier/src/format/template_literal.rs b/crates/oxc_prettier/src/format/template_literal.rs index e7d23b1b6998b1..e3e2cb3d462134 100644 --- a/crates/oxc_prettier/src/format/template_literal.rs +++ b/crates/oxc_prettier/src/format/template_literal.rs @@ -1,10 +1,7 @@ +use oxc_allocator::Vec; use oxc_ast::ast::*; -use crate::{ - format::Format, - ir::{Doc, DocBuilder}, - Prettier, -}; +use crate::{array, format::Format, ir::Doc, text, Prettier}; #[allow(clippy::enum_variant_names)] pub enum TemplateLiteralPrinter<'a, 'b> { @@ -36,8 +33,8 @@ pub(super) fn print_template_literal<'a, 'b>( p: &mut Prettier<'a>, template_literal: &'b TemplateLiteralPrinter<'a, 'b>, ) -> Doc<'a> { - let mut parts = p.vec(); - parts.push(p.text("`")); + let mut parts = Vec::new_in(p.allocator); + parts.push(text!("`")); for (index, quais) in template_literal.quasis().iter().enumerate() { parts.push(quais.format(p)); @@ -45,12 +42,12 @@ pub(super) fn print_template_literal<'a, 'b>( break; }; - parts.push(p.text("${")); + parts.push(text!("${")); parts.push(expr_doc); - parts.push(p.text("}")); + parts.push(text!("}")); } - parts.push(p.text("`")); + parts.push(text!("`")); - p.array(parts) + array!(p, parts) } diff --git a/crates/oxc_prettier/src/format/ternary.rs b/crates/oxc_prettier/src/format/ternary.rs index 91ec0f1877ba7d..0e9551ff5391e1 100644 --- a/crates/oxc_prettier/src/format/ternary.rs +++ b/crates/oxc_prettier/src/format/ternary.rs @@ -1,23 +1,17 @@ use oxc_ast::ast::*; -use crate::{ir::Doc, p_vec, DocBuilder, Format, Prettier}; +use crate::{group, indent, ir::Doc, line, text, Format, Prettier}; pub(super) fn print_ternary<'a>(p: &mut Prettier<'a>, expr: &ConditionalExpression<'a>) -> Doc<'a> { let test_doc = expr.test.format(p); let consequent_doc = expr.consequent.format(p); let alternate_doc = expr.alternate.format(p); - p.group(p.array(p_vec!( + group!( p, - test_doc, - p.indent(p_vec!( - p, - p.line(), - p.text("? "), - consequent_doc, - p.line(), - p.text(": "), - alternate_doc - )) - ))) + [ + test_doc, + indent!(p, [line!(), text!("? "), consequent_doc, line!(), text!(": "), alternate_doc]) + ] + ) } diff --git a/crates/oxc_prettier/src/ir/builder.rs b/crates/oxc_prettier/src/ir/builder.rs deleted file mode 100644 index 662d648aba0d78..00000000000000 --- a/crates/oxc_prettier/src/ir/builder.rs +++ /dev/null @@ -1,146 +0,0 @@ -use oxc_allocator::{Allocator, Box, IntoIn, String, Vec}; - -use crate::{ - ir::{Doc, Fill, Group, IfBreak, IndentIfBreak, Line}, - p_vec, GroupId, -}; - -#[derive(Clone, Copy)] -pub enum Separator { - Softline, - Hardline, - CommaLine, // [",", line] -} - -pub trait DocBuilder<'a> { - fn allocator(&self) -> &'a Allocator; - - #[inline] - fn vec(&self) -> Vec<'a, T> { - Vec::new_in(self.allocator()) - } - #[inline] - fn vec_single(&self, value: T) -> Vec<'a, T> { - let mut vec = Vec::with_capacity_in(1, self.allocator()); - vec.push(value); - vec - } - - fn text(&self, s: &'static str) -> Doc<'a> { - Doc::Str(s) - } - fn dynamic_text(&self, s: &str) -> Doc<'a> { - let s = String::from_str_in(s, self.allocator()).into_bump_str(); - Doc::Str(s) - } - fn space(&self) -> Doc<'a> { - Doc::Str(" ") - } - - fn line(&self) -> Doc<'a> { - Doc::Line(Line::default()) - } - /// Specify a line break. - /// The difference from line is that if the expression fits on one line, it will be replaced with nothing. - fn softline(&self) -> Doc<'a> { - Doc::Line(Line { soft: true, ..Line::default() }) - } - /// Specify a line break that is **always** included in the output, - /// no matter if the expression fits on one line or not. - fn hardline(&self) -> [Doc<'a>; 2] { - let hardline = Doc::Line(Line { hard: true, ..Line::default() }); - [hardline, Doc::BreakParent] - } - - fn indent(&self, contents: Vec<'a, Doc<'a>>) -> Doc<'a> { - Doc::Indent(contents) - } - - fn array(&self, contents: Vec<'a, Doc<'a>>) -> Doc<'a> { - Doc::Array(contents) - } - - fn fill(&self, contents: Vec<'a, Doc<'a>>) -> Doc<'a> { - Doc::Fill(Fill { contents }) - } - - fn if_break( - &self, - break_contents: Doc<'a>, - flat_contents: Doc<'a>, - group_id: Option, - ) -> Doc<'a> { - Doc::IfBreak(IfBreak { - break_contents: Box::new_in(break_contents, self.allocator()), - flat_contents: Box::new_in(flat_contents, self.allocator()), - group_id, - }) - } - - fn indent_if_break(&self, contents: Doc<'a>, group_id: GroupId) -> Doc<'a> { - Doc::IndentIfBreak(IndentIfBreak { - contents: Box::new_in(contents, self.allocator()), - group_id, - }) - } - - fn group(&self, contents: Doc<'a>) -> Doc<'a> { - Doc::Group(Group { - contents: self.vec_single(contents), - should_break: false, - expanded_states: None, - group_id: None, - }) - } - fn group_with_opts( - &self, - contents: Doc<'a>, - should_break: bool, - group_id: Option, - ) -> Doc<'a> { - Doc::Group(Group { - contents: self.vec_single(contents), - should_break, - expanded_states: None, - group_id, - }) - } - - fn conditional_group( - &self, - contents: Doc<'a>, - alternatives: std::vec::Vec>, - group_id: Option, - ) -> Doc<'a> { - let contents = self.vec_single(contents); - let expanded_states = Vec::from_iter_in(alternatives, self.allocator()); - Doc::Group(Group { - contents, - should_break: false, - expanded_states: Some(expanded_states), - group_id, - }) - } - - fn break_parent(&self) -> Doc<'a> { - Doc::BreakParent - } - - // TODO: Just use `Doc` instead of `Separator`...? - fn join(&self, separator: Separator, docs: std::vec::Vec>) -> Vec<'a, Doc<'a>> { - let mut parts = self.vec(); - for (i, doc) in docs.into_iter().enumerate() { - if i != 0 { - match separator { - Separator::Softline => parts.push(self.softline()), - Separator::Hardline => parts.extend(self.hardline()), - Separator::CommaLine => { - parts.push(self.array(p_vec!(self, self.text(","), self.line()))); - } - } - } - parts.push(doc); - } - parts - } -} diff --git a/crates/oxc_prettier/src/ir/doc.rs b/crates/oxc_prettier/src/ir/doc.rs index c6bfd50020a88d..6338e8ae938e31 100644 --- a/crates/oxc_prettier/src/ir/doc.rs +++ b/crates/oxc_prettier/src/ir/doc.rs @@ -1,49 +1,20 @@ -//! Prettier IR -//! -//! References: -//! * - use oxc_allocator::{Box, Vec}; use crate::GroupId; /// IR for the pretty printing. -/// To produce, use the `DocBuilder` trait. +/// Direct use is discouraged, use the macro instead. #[derive(Debug)] pub enum Doc<'a> { - /// Strings are printed directly as is. - /// (however for the algorithm to work properly they shouldn't contain line break characters) Str(&'a str), - /// Arrays are used to concatenate a list of docs to be printed sequentially into a single doc. Array(Vec<'a, Doc<'a>>), - /// Mark a group of items which the printer should try to fit on one line. - /// This is the basic command to tell the printer when to break. - /// Groups are usually nested, and the printer will try to fit everything on one line, - /// but if it doesn't fit it will break the outermost group first and try again. - /// It will continue breaking groups until everything fits (or there are no more groups to break). Group(Group<'a>), - /// This is an alternative type of group which behaves like text layout: - /// it's going to add a break whenever the next element doesn't fit in the line anymore. - /// The difference with `group` is that it's not going to break all the separators, just the ones that are at the end of lines. Fill(Fill<'a>), - /// Print something if the current `group` or the current element of `fill` breaks and something else if it doesn't. IfBreak(IfBreak<'a>), - /// Include this anywhere to force all parent groups to break. BreakParent, - /// Specify a line break. - /// If an expression fits on one line, the line break will be replaced with a space. - /// Line breaks always indent the next line with the current level of indentation. Line(Line), - /// Increase the level of indentation. Indent(Vec<'a, Doc<'a>>), - /// An optimized version of `if_break(indent(doc), doc, { groupId })`. - /// It doesn't make sense to apply `indent_if_break` to the current group. - /// Because "indent if the current group is broken" is the normal behavior of indent. - /// That's why groupId is required. IndentIfBreak(IndentIfBreak<'a>), - /// This is used to implement trailing comments. - /// It's not practical to constantly check where the line ends to avoid accidentally printing some code at the end of a comment. - /// `lineSuffix` buffers docs passed to it and flushes them before any new line. LineSuffix(Vec<'a, Doc<'a>>), } @@ -109,3 +80,11 @@ impl<'a> Fill<'a> { self.contents } } + +// NOTE: Really needed? Just use `Doc` as a separator? +#[derive(Clone, Copy)] +pub enum JoinSeparator { + Softline, + Hardline, + CommaLine, // [",", line] +} diff --git a/crates/oxc_prettier/src/ir/mod.rs b/crates/oxc_prettier/src/ir/mod.rs index ab05969eb72954..6dcefa4e62d52e 100644 --- a/crates/oxc_prettier/src/ir/mod.rs +++ b/crates/oxc_prettier/src/ir/mod.rs @@ -1,6 +1,4 @@ -mod builder; mod display; mod doc; -pub use builder::*; pub use doc::*; diff --git a/crates/oxc_prettier/src/lib.rs b/crates/oxc_prettier/src/lib.rs index 778c18d060eb23..e3abeafd47ffba 100644 --- a/crates/oxc_prettier/src/lib.rs +++ b/crates/oxc_prettier/src/lib.rs @@ -13,19 +13,13 @@ mod options; mod printer; mod utils; -use std::vec; - -use oxc_allocator::Allocator; +use oxc_allocator::{Allocator, Vec}; use oxc_ast::{ast::Program, AstKind}; use oxc_span::Span; use oxc_syntax::identifier::is_line_terminator; pub use crate::options::{ArrowParens, EndOfLine, PrettierOptions, QuoteProps, TrailingComma}; -use crate::{ - format::Format, - ir::{Doc, DocBuilder}, - printer::Printer, -}; +use crate::{format::Format, ir::Doc, printer::Printer}; type GroupId = u32; #[derive(Default)] @@ -55,19 +49,12 @@ pub struct Prettier<'a> { /// The stack of AST Nodes /// See - stack: Vec>, + stack: Vec<'a, AstKind<'a>>, group_id_builder: GroupIdBuilder, args: PrettierArgs, } -impl<'a> DocBuilder<'a> for Prettier<'a> { - #[inline] - fn allocator(&self) -> &'a Allocator { - self.allocator - } -} - impl<'a> Prettier<'a> { #[allow(clippy::needless_pass_by_value)] pub fn new(allocator: &'a Allocator, options: PrettierOptions) -> Self { @@ -75,7 +62,7 @@ impl<'a> Prettier<'a> { allocator, source_text: "", options, - stack: vec![], + stack: Vec::new_in(allocator), group_id_builder: GroupIdBuilder::default(), args: PrettierArgs::default(), } diff --git a/crates/oxc_prettier/src/macros.rs b/crates/oxc_prettier/src/macros.rs index ae0f967d219f55..ced992902c83d9 100644 --- a/crates/oxc_prettier/src/macros.rs +++ b/crates/oxc_prettier/src/macros.rs @@ -1,15 +1,303 @@ -/// `p_vec!` macro, to support dynamic length of arguments. +//! Prettier IR builder macros +//! Ref: + +// NOTE: In addition to those defined here, there are still some that are not yet implemented. +// In terms of macro implementation, there are 2 forms: the most frequently used form and the most flexible form. + +/// Arrays are used to concatenate a list of `Doc`s to be printed sequentially into a single doc. +/// (In Prettier(.js) this is just an array literal.) +/// ``` +/// array!(p, [a, b, c]); +/// array!(p, vec); +/// ``` #[macro_export] -macro_rules! p_vec { - ($p:ident, $( $x:expr ),* $(,)?) => {{ - let mut temp_vec = $p.vec(); +macro_rules! array { + ($p:ident, [$( $x:expr ),* $(,)?]) => {{ + let mut temp_vec = oxc_allocator::Vec::new_in($p.allocator); $( temp_vec.push($x); )* - temp_vec + array!($p, temp_vec) + }}; + ($p:ident, $vec:expr) => {{ + $crate::ir::Doc::Array($vec) + }}; +} + +/// Strings are printed directly as is. +/// However for the algorithm to work properly they shouldn't contain line break characters. +/// (In Prettier(.js) this is just a string literal.) +/// ``` +/// text!("const"); +/// ``` +#[macro_export] +macro_rules! text { + ($str:expr) => {{ + let s: &'static str = $str; + $crate::ir::Doc::Str(s) + }}; +} + +/// Strings are printed directly as is. +/// However for the algorithm to work properly they shouldn't contain line break characters. +/// (In Prettier(.js) this is just a string literal.) +/// ``` +/// dynamic_text!(value.as_str()); +/// ``` +#[macro_export] +macro_rules! dynamic_text { + ($p:ident, $str:expr) => {{ + let s = oxc_allocator::String::from_str_in($str, $p.allocator).into_bump_str(); + $crate::ir::Doc::Str(s) }}; } +/// Mark a group of items which the printer should try to fit on one line. +/// This is the basic command to tell the printer when to break. +/// Groups are usually nested, and the printer will try to fit everything on one line, +/// but if it doesn't fit it will break the outermost group first and try again. +/// It will continue breaking groups until everything fits (or there are no more groups to break). +/// +/// A group is forced to break if it's created with the `should_break` option set to true or if it includes `break_parent`. +/// A hard and literal line breaks automatically include this so they always break parent groups. +/// Breaks are propagated to all parent groups, so if a deeply nested expression has a hard break, everything will break. +/// This only matters for "hard" breaks, i.e. newlines that are printed no matter what and can be statically analyzed. +/// +/// ``` +/// group!(p, [a, b, c], true, Some(group_id)); +/// group!(p, vec, true, None); +/// group!(p, [a, b, c]); +/// group!(p, vec); +/// ``` +#[macro_export] +macro_rules! group { + ($p:ident, [$( $x:expr ),* $(,)?], $should_break:expr, $group_id:expr) => {{ + let mut temp_vec = oxc_allocator::Vec::new_in($p.allocator); + $( + temp_vec.push($x); + )* + $crate::ir::Doc::Group($crate::ir::Group { + contents: temp_vec, + should_break: $should_break, + expanded_states: None, + group_id: $group_id, + }) + }}; + ($p:ident, $vec:expr, $should_break:expr, $group_id:expr) => {{ + $crate::ir::Doc::Group($crate::ir::Group { + contents: $vec, + should_break: $should_break, + expanded_states: None, + group_id: $group_id, + }) + }}; + ($p:ident, [$( $x:expr ),* $(,)?]) => {{ + let mut temp_vec = oxc_allocator::Vec::new_in($p.allocator); + $( + temp_vec.push($x); + )* + group!($p, temp_vec, false, None) + }}; + ($p:ident, $vec:expr) => {{ + group!($p, $vec, false, None) + }}; +} + +/// This should be used as last resort as it triggers an exponential complexity when nested. +/// This will try to print the first alternative, if it fit use it, otherwise go to the next one and so on. +/// The alternatives is an array of documents going from the least expanded (most flattened) representation first to the most expanded. +/// +/// ``` +/// conditional_group!(p, [a, b, c]); +/// ``` +#[macro_export] +macro_rules! conditional_group { + ($p:ident, [$doc:expr, $( $x:expr ),* $(,)?]) => {{ + let mut temp_single = oxc_allocator::Vec::with_capacity_in(1, $p.allocator); + temp_single.push($doc); + let mut temp_vec = oxc_allocator::Vec::new_in($p.allocator); + $( + temp_vec.push($x); + )* + + $crate::ir::Doc::Group($crate::ir::Group { + contents: temp_single, + should_break: false, + expanded_states: Some(temp_vec), + group_id: None, + }) + }}; +} + +/// This is an alternative type of group which behaves like text layout: +/// it's going to add a break whenever the next element doesn't fit in the line anymore. +/// The difference with `group` is that it's not going to break all the separators, just the ones that are at the end of lines. +/// +/// Expects the arguments to be an array of alternating content and line breaks. +/// In other words, elements with odd indices must be line breaks (e.g., `softline`). +/// +/// ``` +/// fill!(p, [a, line!(), b, line!(), c]); +/// fill!(p, vec); +/// ``` +#[macro_export] +macro_rules! fill { + ($p:ident, [$( $x:expr ),* $(,)?]) => {{ + let mut temp_vec = oxc_allocator::Vec::new_in($p.allocator); + $( + temp_vec.push($x); + )* + fill!($p, temp_vec) + }}; + ($p:ident, $vec:expr) => {{ + $crate::ir::Doc::Fill($crate::ir::Fill { contents: $vec }) + }}; +} + +/// Print something if the current group or the current element of fill breaks and something else if it doesn't. +/// `group_id` can be used to check another already printed group instead of the current group. +/// +/// If a `hardline` or `break_parent` is present within the possible contents, +/// the parent groups will be broken regardless of said content being printed, which might not be desirable. +/// This behaviour is a design limitation. +/// Usually the desired result can be achieved in a different way. +/// +/// ``` +/// if_break!(p, a, b, Some(group_id)); +/// if_break!(p, a); +/// ``` +#[macro_export] +macro_rules! if_break { + ($p:ident, $break:expr, $flat:expr, $group_id:expr) => {{ + $crate::ir::Doc::IfBreak($crate::ir::IfBreak { + break_contents: oxc_allocator::Box::new_in($break, $p.allocator), + flat_contents: oxc_allocator::Box::new_in($flat, $p.allocator), + group_id: $group_id, + }) + }}; + ($p:ident, $break:expr) => {{ + use $crate::text; + if_break!($p, $break, text!(""), None) + }}; +} + +/// Include this anywhere to force all parent groups to break. See `group` for more info. +/// +/// ``` +/// break_parent!(); +/// ``` +#[macro_export] +macro_rules! break_parent { + () => {{ + $crate::ir::Doc::BreakParent + }}; +} + +/// Join an array of docs with a separator. +/// +/// ``` +/// join!(p, JoinSeparator::Softline, vec); +/// ``` +#[macro_export] +macro_rules! join { + ($p:ident, $sep:expr, $vec:expr) => {{ + let mut parts = oxc_allocator::Vec::new_in($p.allocator); + for (i, doc) in $vec.into_iter().enumerate() { + if i != 0 { + match $sep { + $crate::ir::JoinSeparator::Softline => parts.push($crate::softline!()), + $crate::ir::JoinSeparator::Hardline => parts.extend($crate::hardline!()), + $crate::ir::JoinSeparator::CommaLine => { + parts.extend([$crate::text!(","), $crate::line!()]); + } + } + } + parts.push(doc); + } + $crate::ir::Doc::Array(parts) + }}; +} + +/// Specify a line break. +/// If an expression fits on one line, the line break will be replaced with a space. +/// Line breaks always indent the next line with the current level of indentation. +/// +/// ``` +/// line!(); +/// ``` +#[macro_export] +macro_rules! line { + () => {{ + $crate::ir::Doc::Line($crate::ir::Line::default()) + }}; +} + +/// Specify a line break. +/// The difference from line is that if the expression fits on one line, it will be replaced with nothing. +/// +/// ``` +/// softline!(); +/// ``` +#[macro_export] +macro_rules! softline { + () => {{ + $crate::ir::Doc::Line($crate::ir::Line { soft: true, ..Default::default() }) + }}; +} + +/// Specify a line break that is always included in the output, no matter if the expression fits on one line or not. +/// +/// ``` +/// hardline!(); +/// ``` +#[macro_export] +macro_rules! hardline { + () => {{ + let hardline = $crate::ir::Doc::Line($crate::ir::Line { hard: true, ..Default::default() }); + [hardline, $crate::ir::Doc::BreakParent] + }}; +} + +/// Increase the level of indentation. +/// +/// ``` +/// indent!(p, [a, b, c]); +/// indent!(p, vec); +/// ``` +#[macro_export] +macro_rules! indent { + ($p:ident, [$( $x:expr ),* $(,)?]) => {{ + let mut temp_vec = oxc_allocator::Vec::new_in($p.allocator); + $( + temp_vec.push($x); + )* + $crate::ir::Doc::Indent(temp_vec) + }}; + ($p:ident, $vec:expr) => {{ + $crate::ir::Doc::Indent($vec) + }}; +} + +/// An optimized version of `if_break(indent(doc), doc, group_id)`. +/// It doesn't make sense to apply `indent_if_break` to the current group, +/// because "indent if the current group is broken" is the normal behavior of indent. +/// That's why `group_id` is required. +/// +/// ``` +/// indent_if_break!(p, a, group_id); +/// ``` +#[macro_export] +macro_rules! indent_if_break { + ($p:ident, $doc:expr, $group_id:expr) => {{ + $crate::ir::Doc::IndentIfBreak($crate::ir::IndentIfBreak { + contents: oxc_allocator::Box::new_in($doc, $p.allocator), + group_id: $group_id, + }) + }}; +} + +// --- + /// `wrap!` macro, /// - to save the reference of the current node to be used as parent node later /// - to print parens and comments @@ -19,14 +307,14 @@ macro_rules! p_vec { #[macro_export] macro_rules! wrap { ($p:ident, $self:expr, $kind:ident, $block:block) => {{ - let kind = AstKind::$kind($p.alloc($self)); + let kind = oxc_ast::AstKind::$kind($p.alloc($self)); $p.enter_node(kind); let leading = $p.print_leading_comments(kind.span()); let doc = $block; let doc = if $p.need_parens(kind) { - $p.array(p_vec!($p, $p.text("("), doc, $p.text(")"))) + $crate::array!($p, [$crate::text!("("), doc, $crate::text!(")")]) } else { doc }; diff --git a/crates/oxc_prettier/src/printer/mod.rs b/crates/oxc_prettier/src/printer/mod.rs index 8a9a7690e19458..0adc776381fe31 100644 --- a/crates/oxc_prettier/src/printer/mod.rs +++ b/crates/oxc_prettier/src/printer/mod.rs @@ -11,7 +11,7 @@ use oxc_allocator::Allocator; use rustc_hash::FxHashMap; use crate::{ - ir::{Doc, DocBuilder, Fill, IfBreak, IndentIfBreak, Line}, + ir::{Doc, Fill, IfBreak, IndentIfBreak, Line}, printer::command::{Command, Indent, Mode}, GroupId, PrettierOptions, }; @@ -36,13 +36,6 @@ pub struct Printer<'a> { allocator: &'a Allocator, } -impl<'a> DocBuilder<'a> for Printer<'a> { - #[inline] - fn allocator(&self) -> &'a Allocator { - self.allocator - } -} - impl<'a> Printer<'a> { pub fn new( doc: Doc<'a>, @@ -292,7 +285,7 @@ impl<'a> Printer<'a> { let Some(second_content) = fill.dequeue() else { return; }; - let mut docs = self.vec(); + let mut docs = oxc_allocator::Vec::new_in(self.allocator); let content = content_flat_cmd.doc; docs.push(content); docs.push(whitespace_flat_cmd.doc); diff --git a/crates/oxc_regular_expression/Cargo.toml b/crates/oxc_regular_expression/Cargo.toml index b24b27484adb35..4f07bc0f1b0153 100644 --- a/crates/oxc_regular_expression/Cargo.toml +++ b/crates/oxc_regular_expression/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_regular_expression" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_semantic/CHANGELOG.md b/crates/oxc_semantic/CHANGELOG.md index 68956ff56cfedf..fe82279bda0c09 100644 --- a/crates/oxc_semantic/CHANGELOG.md +++ b/crates/oxc_semantic/CHANGELOG.md @@ -4,6 +4,31 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +- 0be5233 semantic: [**BREAKING**] Remove `ModuleRecord` from `Semantic` (#7548) (Boshen) + +- 8a788b8 parser: [**BREAKING**] Build `ModuleRecord` directly in parser (#7546) (Boshen) + +### Features + +- d2767be semantic: Syntax error for `delete object?.#a` (#7636) (Boshen) +- 206de91 semantic: Add `SymbolTable::add_resolved_reference` method (#7608) (overlookmotel) + +### Bug Fixes + +- 1486849 semantic: Syntax error for undeclared private field access (#7635) (Boshen) +- f3850eb semantic: Correctly resolve binding for return type of functions (#6388) (overlookmotel) + +### Refactor + +- d21448b semantic, transformer: Simplify `FxIndexMap` type aliases (#7524) (overlookmotel) + +### Testing + +- fed8327 semantic: Add a test for `UpdateExpression` (#7495) (Dunqing) +- 4b0720a semantic: Re-organize snapshot-based tests (#7494) (Dunqing) + ## [0.38.0] - 2024-11-26 - 27b2268 semantic: [**BREAKING**] Remove `SymbolFlags::Export` (#7414) (Dunqing) diff --git a/crates/oxc_semantic/Cargo.toml b/crates/oxc_semantic/Cargo.toml index 949cdca215fdb4..7c8f8733077a7f 100644 --- a/crates/oxc_semantic/Cargo.toml +++ b/crates/oxc_semantic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_semantic" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_semantic/src/checker/javascript.rs b/crates/oxc_semantic/src/checker/javascript.rs index a1457ef6f88aea..c64465fe8f6e66 100644 --- a/crates/oxc_semantic/src/checker/javascript.rs +++ b/crates/oxc_semantic/src/checker/javascript.rs @@ -195,17 +195,13 @@ fn private_field_undeclared(x0: &str, span1: Span) -> OxcDiagnostic { fn check_private_identifier(ctx: &SemanticBuilder<'_>) { if let Some(class_id) = ctx.class_table_builder.current_class_id { - ctx.class_table_builder.classes.iter_private_identifiers(class_id).for_each(|reference| { - if reference.element_ids.is_empty() - && !ctx.class_table_builder.classes.ancestors(class_id).skip(1).any(|class_id| { - ctx.class_table_builder - .classes - .has_private_definition(class_id, &reference.name) - }) - { + for reference in ctx.class_table_builder.classes.iter_private_identifiers(class_id) { + if !ctx.class_table_builder.classes.ancestors(class_id).any(|class_id| { + ctx.class_table_builder.classes.has_private_definition(class_id, &reference.name) + }) { ctx.error(private_field_undeclared(&reference.name, reference.span)); } - }); + } } } @@ -1003,7 +999,8 @@ fn delete_of_unqualified(span: Span) -> OxcDiagnostic { } fn delete_private_field(span: Span) -> OxcDiagnostic { - OxcDiagnostic::error("Private fields can not be deleted").with_label(span) + OxcDiagnostic::error("The operand of a 'delete' operator cannot be a private identifier.") + .with_label(span) } pub fn check_unary_expression<'a>( @@ -1020,6 +1017,11 @@ pub fn check_unary_expression<'a>( Expression::PrivateFieldExpression(expr) => { ctx.error(delete_private_field(expr.span)); } + Expression::ChainExpression(chain_expr) => { + if let ChainElement::PrivateFieldExpression(e) = &chain_expr.expression { + ctx.error(delete_private_field(e.field.span)); + } + } _ => {} } } diff --git a/crates/oxc_semantic/src/class/table.rs b/crates/oxc_semantic/src/class/table.rs index 654ff5b012362b..e1901541325976 100644 --- a/crates/oxc_semantic/src/class/table.rs +++ b/crates/oxc_semantic/src/class/table.rs @@ -49,8 +49,7 @@ pub struct ClassTable { pub parent_ids: FxHashMap, pub declarations: IndexVec, pub elements: IndexVec>, - // PrivateIdentifier reference - pub private_identifiers: IndexVec>, + pub private_identifier_references: IndexVec>, } impl ClassTable { @@ -70,7 +69,7 @@ impl ClassTable { &self, class_id: ClassId, ) -> impl Iterator + '_ { - self.private_identifiers[class_id].iter() + self.private_identifier_references[class_id].iter() } pub fn get_node_id(&self, class_id: ClassId) -> NodeId { @@ -108,7 +107,7 @@ impl ClassTable { self.parent_ids.insert(class_id, parent_id); }; self.elements.push(IndexVec::default()); - self.private_identifiers.push(Vec::new()); + self.private_identifier_references.push(Vec::new()); class_id } @@ -121,6 +120,6 @@ impl ClassTable { class_id: ClassId, private_identifier_reference: PrivateIdentifierReference, ) { - self.private_identifiers[class_id].push(private_identifier_reference); + self.private_identifier_references[class_id].push(private_identifier_reference); } } diff --git a/crates/oxc_semantic/tests/integration/util/mod.rs b/crates/oxc_semantic/tests/integration/util/mod.rs index ac53740f5175bc..cc87ca60c462c9 100644 --- a/crates/oxc_semantic/tests/integration/util/mod.rs +++ b/crates/oxc_semantic/tests/integration/util/mod.rs @@ -133,7 +133,8 @@ impl<'a> SemanticTester<'a> { match (self.expect_errors, semantic_ret.errors.is_empty()) { (true, true) => panic!("Expected errors, but none were produced"), (false, false) => panic!( - "Semantic analysis failed:\n\n{}", + "Semantic analysis failed:\n\n{}\n\n{}", + self.source_text, semantic_ret .errors .iter() diff --git a/crates/oxc_span/CHANGELOG.md b/crates/oxc_span/CHANGELOG.md index f46055c0cf98bc..3e3aa24ddd850f 100644 --- a/crates/oxc_span/CHANGELOG.md +++ b/crates/oxc_span/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +### Bug Fixes + +- b553d6f span: Fix clippy warning (#7591) (overlookmotel) + +### Refactor + +- 823353a linter: Clean up APIs for `ModuleRecord` (#7556) (Boshen) + ## [0.36.0] - 2024-11-09 ### Features diff --git a/crates/oxc_span/Cargo.toml b/crates/oxc_span/Cargo.toml index bcc7248c85d3c5..49765b8e943d94 100644 --- a/crates/oxc_span/Cargo.toml +++ b/crates/oxc_span/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_span" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_syntax/CHANGELOG.md b/crates/oxc_syntax/CHANGELOG.md index 5faadc26338a9d..b41fdf3d215583 100644 --- a/crates/oxc_syntax/CHANGELOG.md +++ b/crates/oxc_syntax/CHANGELOG.md @@ -4,6 +4,30 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +- c2ced15 parser,linter: [**BREAKING**] Use a different `ModuleRecord` for linter (#7554) (Boshen) + +- 8a788b8 parser: [**BREAKING**] Build `ModuleRecord` directly in parser (#7546) (Boshen) + +### Features + +- 7c62a33 napi/parser: Return esm info (#7602) (Boshen) +- 33e5a49 syntax: Add statement span to `ImportEntry` and `ExportEntry` (#7583) (Boshen) + +### Performance + +- 4a98230 syntax: Use `FxDashMap` for exported bindings (#7525) (overlookmotel) + +### Refactor + +- 169b8bf linter, syntax: Introduce type alias `FxDashMap` (#7520) (overlookmotel) +- f0e7acc syntax: Change `ModuleRecord::not_esm` to `has_module_syntax` (#7579) (Boshen) +- 18519de syntax: Remove `ModuleRecord::export_default` (#7578) (Boshen) +- d476660 syntax: Remove `ModuleRecord::exported_bindings_duplicated` because it is a syntax error (#7577) (Boshen) +- 17663f5 syntax: Remove `ModuleRecord::export_default_duplicated` because it is a syntax error (#7576) (Boshen) +- 79014ff syntax: Clean up `ModuleRecord` (#7568) (Boshen) + ## [0.38.0] - 2024-11-26 - 27b2268 semantic: [**BREAKING**] Remove `SymbolFlags::Export` (#7414) (Dunqing) diff --git a/crates/oxc_syntax/Cargo.toml b/crates/oxc_syntax/Cargo.toml index f1e22850f90338..28c063a2547d4f 100644 --- a/crates/oxc_syntax/Cargo.toml +++ b/crates/oxc_syntax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_syntax" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_transformer/CHANGELOG.md b/crates/oxc_transformer/CHANGELOG.md index 4538b31175adba..79e619eb6b0b72 100644 --- a/crates/oxc_transformer/CHANGELOG.md +++ b/crates/oxc_transformer/CHANGELOG.md @@ -4,6 +4,61 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +- f2f31a8 traverse: [**BREAKING**] Remove unsound APIs (#7514) (overlookmotel) + +- b0e1c03 ast: [**BREAKING**] Add `StringLiteral::raw` field (#7393) (Boshen) + +### Features + +- a784a82 oxc_transformer: Support jsx pragma that are long member expressions (#7538) (IWANABETHATGUY) +- a23ce15 oxc_transformer: Replace_global_define for assignmentTarget (#7505) (IWANABETHATGUY) +- 3539f56 transformer/class-properties: Support for transforming `TaggedTemplateExpresssion` (#7504) (Dunqing) + +### Bug Fixes + +- 64f92e9 oxc_transform: Oxc dot define is postfix of some MemberExpr (#7640) (IWANABETHATGUY) +- 6af8659 oxc_transformer: Correct generate `ThisExpr` and `import.meta` in jsx pragma (#7553) (IWANABETHATGUY) +- 58a125f transformer/async-to-generator: Correct the `SymbolFlags` of function id in module (#7470) (Dunqing) +- eb825ed transformer/class-properties: Replace references to class name with temp var in static prop initializers (#7610) (overlookmotel) +- 0eadd9f transformer/class-properties: Create temp var for class where required (#7516) (overlookmotel) +- 199076b transformer/class-properties: Transform private property accesses in static prop initializers (#7483) (overlookmotel) +- 37842c1 transformer/object-rest-spread: Generate catch variable binding with correct `SymbolFlags` (#7469) (Dunqing) + +### Performance + +- 7ebe8c2 transformer: Use `FxDashMap` for browser query cache (#7521) (overlookmotel) +- 5ca6eea transformer/class-properties: Inline visitor methods (#7485) (overlookmotel) +- 3b1e63e transformer/jsx: No string comparisons generating pragma expression (#7620) (overlookmotel) + +### Documentation + +- 370d4b9 transformer/class-properties: Add missing docs (#7588) (overlookmotel) + +### Refactor + +- d21448b semantic, transformer: Simplify `FxIndexMap` type aliases (#7524) (overlookmotel) +- 7d1c12e transformer/class-properties: Rename misleadingly-named method (#7609) (overlookmotel) +- 802233d transformer/class-properties: Remove pointless method (#7592) (overlookmotel) +- a07f278 transformer/class-properties: `PrivatePropsStack` type (#7589) (overlookmotel) +- 7bd6350 transformer/class-properties: Move creating temp var out of main loop (#7587) (overlookmotel) +- ebd11fb transformer/class-properties: Exit `transform_class` faster if nothing to do (#7586) (overlookmotel) +- dccff38 transformer/class-properties: `ResolvedPrivateProp` type (#7532) (overlookmotel) +- 367b6c8 transformer/class-properties: `shortcut_static_class` take `SymbolId` (#7531) (overlookmotel) +- ab1214d transformer/class-properties: Rename `class_binding` (#7533) (overlookmotel) +- d5aaee7 transformer/class-properties: Remove defunct comments (#7527) (overlookmotel) +- 968863b transformer/class-properties: Move transform logic of `callee` of `CallExpression` to `transform_private_field_callee` (#7503) (Dunqing) +- 5261547 transformer/class-properties: Remove a branch from `transform_call_expression_impl` (#7507) (overlookmotel) +- 1c4b29c transformer/class-properties: Correct comments (#7506) (overlookmotel) +- 8ad52be transformer/jsx: `Pragma::parse` take a `&str` (#7619) (overlookmotel) +- ef62b9d transformer/react-refresh: Use `generate_uid_in_current_hoist_scope` to add hoisted binding (#7492) (Dunqing) + +### Testing + +- 71b3437 oxc_transformer: Define works differently with esbuild (#7593) (翠 / green) +- 2158c38 transformer/jsx: Move tests setup into a macro (#7618) (overlookmotel) + ## [0.38.0] - 2024-11-26 - bb2c0c2 transformer: [**BREAKING**] Return `String` as error instead of OxcDiagnostic (#7424) (Boshen) diff --git a/crates/oxc_transformer/Cargo.toml b/crates/oxc_transformer/Cargo.toml index ff8e8ab166d0ab..6a21fc190e7d6e 100644 --- a/crates/oxc_transformer/Cargo.toml +++ b/crates/oxc_transformer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_transformer" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/crates/oxc_transformer/src/es2022/class_properties/mod.rs b/crates/oxc_transformer/src/es2022/class_properties/mod.rs index 103ed885f9b3db..3f4909a3a3f584 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/mod.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/mod.rs @@ -276,9 +276,15 @@ impl<'a, 'ctx> Traverse<'a> for ClassProperties<'a, 'ctx> { } // `object?.#prop` Expression::ChainExpression(_) => { - // TODO: `transform_chain_expression` is no-op at present self.transform_chain_expression(expr, ctx); } + // `delete object?.#prop.xyz` + Expression::UnaryExpression(unary) + if unary.operator == UnaryOperator::Delete + && matches!(unary.argument, Expression::ChainExpression(_)) => + { + self.transform_unary_expression(expr, ctx); + } // "object.#prop`xyz`" Expression::TaggedTemplateExpression(_) => { // TODO: `transform_tagged_template_expression` is no-op at present diff --git a/crates/oxc_transformer/src/es2022/class_properties/private.rs b/crates/oxc_transformer/src/es2022/class_properties/private.rs index b1941f92b62a30..e7d0012dfb48cc 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/private.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/private.rs @@ -10,13 +10,19 @@ use oxc_syntax::{ reference::{ReferenceFlags, ReferenceId}, symbol::{SymbolFlags, SymbolId}, }; -use oxc_traverse::{ast_operations::get_var_name_from_node, BoundIdentifier, TraverseCtx}; +use oxc_traverse::{ + ast_operations::get_var_name_from_node, Ancestor, BoundIdentifier, MaybeBoundIdentifier, + TraverseCtx, +}; use crate::common::helper_loader::Helper; use super::{ private_props::ResolvedPrivateProp, - utils::{create_assignment, create_underscore_ident_name}, + utils::{ + assert_expr_neither_parenthesis_nor_typescript_syntax, create_assignment, + create_underscore_ident_name, + }, ClassProperties, }; @@ -157,16 +163,32 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { let Some((callee, object)) = self.transform_private_field_callee(field_expr, ctx) else { return; }; + Self::substitute_callee_and_insert_context(call_expr, callee, object, ctx); + } + /// Substitute callee and add object as first argument to call expression. + /// + /// Non-Optional: + /// * `callee(...arguments)` -> `callee.call(object, ...arguments)` + /// + /// Optional: + /// * `callee?.(...arguments)` -> `callee?.call(object, ...arguments)` + fn substitute_callee_and_insert_context( + call_expr: &mut CallExpression<'a>, + callee: Expression<'a>, + context: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { // Substitute `.call` as callee of call expression call_expr.callee = Expression::from(ctx.ast.member_expression_static( SPAN, callee, ctx.ast.identifier_name(SPAN, Atom::from("call")), - false, + // Make sure the `callee` can access `call` safely. i.e `callee?.()` -> `callee?.call()` + mem::replace(&mut call_expr.optional, false), )); - // Add `object` to call arguments - call_expr.arguments.insert(0, Argument::from(object)); + // Insert `context` to call arguments + call_expr.arguments.insert(0, Argument::from(context)); } /// Transform [`CallExpression::callee`] or [`TaggedTemplateExpression::tag`] that is a private field. @@ -198,7 +220,9 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { self.private_props_stack.find(&field_expr.field)?; let prop_ident = prop_binding.create_read_expression(ctx); - let object = ctx.ast.move_expression(&mut field_expr.object); + // `(object.#method)()` + // ^^^^^^^^^^^^^^^^ is a parenthesized expression + let object = ctx.ast.move_expression(field_expr.object.get_inner_expression_mut()); // Get replacement for callee let replacement = if is_static { @@ -850,16 +874,496 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { // // `#[inline]` so that compiler sees that `expr` is an `Expression::ChainExpression` #[inline] - #[expect(clippy::unused_self)] pub(super) fn transform_chain_expression( &mut self, expr: &mut Expression<'a>, - _ctx: &mut TraverseCtx<'a>, + ctx: &mut TraverseCtx<'a>, ) { - let Expression::ChainExpression(_chain_expr) = expr else { unreachable!() }; + if let Some((result, chain_expr)) = self.transform_chain_expression_impl(expr, ctx) { + *expr = Self::wrap_conditional_check(result, chain_expr, ctx); + } + } + + fn transform_chain_expression_impl( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option<(Expression<'a>, Expression<'a>)> { + let Expression::ChainExpression(chain_expr) = expr else { unreachable!() }; + + let element = &mut chain_expr.expression; + if matches!(element, ChainElement::PrivateFieldExpression(_)) { + // The PrivateFieldExpression must be transformed, so we can convert it to a normal expression here. + let mut chain_expr = Self::convert_chain_expression_to_expression(expr, ctx); + let result = + self.transform_private_field_expression_of_chain_expression(&mut chain_expr, ctx); + Some((result, chain_expr)) + } else if let Some(result) = self.transform_chain_expression_element(element, ctx) { + let chain_expr = Self::convert_chain_expression_to_expression(expr, ctx); + Some((result, chain_expr)) + } else { + // "Entering this branch indicates that the chain element has been changed and updated directly in + // `element` or do nothing because haven't found any private field." + None + } + } + + /// Transform non-private field expression of chain element. + /// + /// [`ChainElement::PrivateFieldExpression`] is handled in [`Self::transform_chain_expression`]. + fn transform_chain_expression_element( + &mut self, + element: &mut ChainElement<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { + match element { + expression @ match_member_expression!(ChainElement) => self + .transform_member_expression_of_chain_expression( + expression.to_member_expression_mut(), + ctx, + ), + ChainElement::CallExpression(call) => { + self.transform_call_expression_of_chain_expression(call, ctx) + } + ChainElement::TSNonNullExpression(non_null) => { + self.transform_chain_element_recursively(&mut non_null.expression, ctx) + } + } + } + + /// Recursively find the first private field expression in the chain element and transform it. + fn transform_chain_element_recursively( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { + assert_expr_neither_parenthesis_nor_typescript_syntax(expr); + match expr { + Expression::PrivateFieldExpression(_) => { + Some(self.transform_private_field_expression_of_chain_expression(expr, ctx)) + } + match_member_expression!(Expression) => self + .transform_member_expression_of_chain_expression( + expr.to_member_expression_mut(), + ctx, + ), + Expression::CallExpression(call) => { + self.transform_call_expression_of_chain_expression(call, ctx) + } + _ => { + assert_expr_neither_parenthesis_nor_typescript_syntax(expr); + None + } + } + } + + /// Go through the part of chain element and transform the object/callee of first encountered optional member/call. + /// + /// Ident: + /// * `Foo?.bar`: + /// - Passed-in `expr` will be mutated to `Foo.bar` + /// - Returns `Foo === null || Foo === void 0 ? void 0` + /// + /// MemberExpression: + /// * `Foo?.bar?.baz`: + /// - Passed-in `expr` will be mutated to `_Foo$bar.baz` + /// - Returns `Foo === null || Foo === void 0 ? void 0` + /// + /// CallExpression: + /// See [`Self::transform_call_expression_to_bind_proper_context`] + /// + fn transform_first_optional_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { + let object = match expr { + Expression::CallExpression(call) => { + if call.optional { + call.optional = false; + if call.callee.is_member_expression() { + // Special case for call expression because we need to make sure it has a proper context + return Some( + self.transform_call_expression_to_bind_proper_context(expr, ctx), + ); + } + &mut call.callee + } else { + return self.transform_first_optional_expression(&mut call.callee, ctx); + } + } + Expression::StaticMemberExpression(member) => { + if member.optional { + member.optional = false; + &mut member.object + } else { + return self.transform_first_optional_expression(&mut member.object, ctx); + } + } + Expression::ComputedMemberExpression(member) => { + if member.optional { + member.optional = false; + &mut member.object + } else { + return self.transform_first_optional_expression(&mut member.object, ctx); + } + } + Expression::PrivateFieldExpression(member) => { + if member.optional { + member.optional = false; + &mut member.object + } else { + return self.transform_first_optional_expression(&mut member.object, ctx); + } + } + _ => return None, + }; + + let result = self.transform_expression_to_wrap_nullish_check(object, ctx); + Some(result) + } + + fn transform_private_field_expression_of_chain_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let Expression::PrivateFieldExpression(field_expr) = expr else { unreachable!() }; + + let object = &mut field_expr.object; + let left = self.transform_first_optional_expression(object, ctx).unwrap_or_else(|| { + // Even though no optional expression, we still need to transform the object + self.transform_expression_to_wrap_nullish_check(object, ctx) + }); + + if matches!(ctx.ancestor(1), Ancestor::CallExpressionCallee(_)) { + // `(Foo?.#m)();` -> `(Foo === null || Foo === void 0 ? void 0 : _m._.bind(Foo))();` + // ^^^^^^^^^^^^ is a call expression, we need to bind the proper context + *expr = self + .transform_bindable_private_field(field_expr, ctx) + .unwrap_or_else(|| unreachable!()); + } else { + self.transform_private_field_expression(expr, ctx); + } + + left + } + + fn transform_member_expression_of_chain_expression( + &mut self, + member: &mut MemberExpression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { + let is_optional = member.optional(); + let object = member.object_mut(); + let result = self.transform_chain_element_recursively(object, ctx)?; + if is_optional { + // `o?.Foo.#self.self?.self.unicorn;` -> `(result ? void 0 : object)?.self.unicorn` + // ^^^^^^^^^^^^^^^^^ the object has transformed, if the current member is optional, + // then we need to wrap it to a conditional expression + let object_owner = ctx.ast.move_expression(object); + *object = Self::wrap_conditional_check(result, object_owner, ctx); + None + } else { + Some(result) + } + } + + fn transform_call_expression_of_chain_expression( + &mut self, + call_expr: &mut CallExpression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { + let is_optional = call_expr.optional; + let callee = call_expr.callee.get_inner_expression_mut(); + if matches!(callee, Expression::PrivateFieldExpression(_)) { + let result = self.transform_first_optional_expression(callee, ctx); + // If the `callee` has no optional expression, we need to transform it using `transform_call_expression_impl` directly. + // `Foo.bar.#m?.();` -> `_assertClassBrand(Foo, _Foo$bar = Foo.bar, _m)._?.call(_Foo$bar);` + // ^^^^ only the private field is optional + // Move out parenthesis and typescript syntax + call_expr.callee = ctx.ast.move_expression(callee); + self.transform_call_expression_impl(call_expr, ctx); + return result; + } + + let result = self.transform_chain_element_recursively(callee, ctx)?; + if !is_optional { + return Some(result); + } + + // `o?.Foo.#self.getSelf?.()?.self.#m();` + // ^^^^^^^^^^^ this is a optional function call, to make sure it has a proper context, + // we also need to assign `o?.Foo.#self` to a temp variable, and + // then use it as a first argument of `getSelf` call. + // + // TODO(improve-on-babel): Consider remove this logic, because it seems no runtime behavior change. + let object = callee.to_member_expression_mut().object_mut(); + let (assignment, context) = self.duplicate_object(ctx.ast.move_expression(object), ctx); + *object = assignment; + let callee = ctx.ast.move_expression(&mut call_expr.callee); + let callee = Self::wrap_conditional_check(result, callee, ctx); + Self::substitute_callee_and_insert_context(call_expr, callee, context, ctx); - // TODO: `object?.#prop` - // TODO: `object?.#prop()` + None + } + + /// Transform expression to wrap nullish check. + /// + /// Returns: + /// * Bound Identifier: `A` -> `A === null || A === void 0` + /// * Unbound Identifier or anything else: `A.B` -> `(_A$B = A.B) === null || _A$B === void 0` + /// + /// NOTE: This method will mutate the passed-in `object` to a temp variable identifier. + fn transform_expression_to_wrap_nullish_check( + &mut self, + object: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + // `A` -> `A === null || A === void 0` + if let Expression::Identifier(ident) = object { + if let Some(binding) = self.get_existing_binding_for_identifier(ident, ctx) { + let left1 = binding.create_read_expression(ctx); + let left2 = binding.create_read_expression(ctx); + return self.wrap_nullish_check(left1, left2, ctx); + } + } + + // `A.B` -> `(_A$B = A.B) === null || _A$B === void 0` + // TODO: should add an API `generate_uid_in_current_hoist_scope_based_on_node` to instead this + let temp_var_binding = ctx.generate_uid_in_current_scope_based_on_node( + object, + SymbolFlags::FunctionScopedVariable, + ); + self.ctx.var_declarations.insert_var(&temp_var_binding, None, ctx); + + let object = mem::replace(object, temp_var_binding.create_read_expression(ctx)); + let assignment = create_assignment( + &temp_var_binding, + Self::ensure_optional_expression_wrapped_by_chain_expression(object, ctx), + ctx, + ); + let reference = temp_var_binding.create_read_expression(ctx); + self.wrap_nullish_check(assignment, reference, ctx) + } + + /// Converts chain expression to expression + /// + /// - [ChainElement::CallExpression] -> [Expression::CallExpression] + /// - [ChainElement::StaticMemberExpression] -> [Expression::StaticMemberExpression] + /// - [ChainElement::ComputedMemberExpression] -> [Expression::ComputedMemberExpression] + /// - [ChainElement::PrivateFieldExpression] -> [Expression::PrivateFieldExpression] + /// - [ChainElement::TSNonNullExpression] -> [TSNonNullExpression::expression] + // + // `#[inline]` so that compiler sees that `expr` is an [`Expression::ChainExpression`]. + #[inline] + fn convert_chain_expression_to_expression( + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let Expression::ChainExpression(chain_expr) = ctx.ast.move_expression(expr) else { + unreachable!() + }; + match chain_expr.unbox().expression { + element @ match_member_expression!(ChainElement) => { + Expression::from(element.into_member_expression()) + } + ChainElement::CallExpression(call) => Expression::CallExpression(call), + ChainElement::TSNonNullExpression(non_null) => non_null.unbox().expression, + } + } + + /// Get an MaybeBoundIdentifier from an bound identifier reference. + /// + /// If no temp variable required, returns `MaybeBoundIdentifier` for existing variable/global. + /// If temp variable is required, returns `None`. + fn get_existing_binding_for_identifier( + &self, + ident: &IdentifierReference<'a>, + ctx: &TraverseCtx<'a>, + ) -> Option> { + let binding = MaybeBoundIdentifier::from_identifier_reference(ident, ctx); + if self.ctx.assumptions.pure_getters || binding.to_bound_identifier().is_some() { + Some(binding) + } else { + None + } + } + + /// Ensure that the expression is wrapped by a chain expression. + /// + /// If the given expression contains optional expression, it will be wrapped by + /// a chain expression, this way we can ensure the remain optional expression can + /// be handled by optional-chaining plugin correctly. + fn ensure_optional_expression_wrapped_by_chain_expression( + expr: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + if Self::has_optional_expression(&expr) { + let chain_element = match expr { + Expression::CallExpression(call) => ChainElement::CallExpression(call), + expr @ match_member_expression!(Expression) => { + ChainElement::from(expr.into_member_expression()) + } + _ => unreachable!(), + }; + ctx.ast.expression_chain(SPAN, chain_element) + } else { + expr + } + } + + /// Recursively check if the expression has optional expression. + #[inline] + fn has_optional_expression(expr: &Expression<'a>) -> bool { + match expr { + Expression::CallExpression(call) => { + call.optional || Self::has_optional_expression(call.callee.get_inner_expression()) + } + Expression::StaticMemberExpression(member) => { + member.optional || Self::has_optional_expression(&member.object) + } + Expression::ComputedMemberExpression(member) => { + member.optional || Self::has_optional_expression(&member.object) + } + Expression::PrivateFieldExpression(member) => { + member.optional || Self::has_optional_expression(&member.object) + } + _ => false, + } + } + + /// Transform call expression to bind a proper context. + /// + /// * Callee without a private field: + /// `Foo?.bar()?.zoo?.().#x;` + /// -> `(_Foo$bar$zoo = (_Foo$bar = Foo?.bar())?.zoo) === null || _Foo$bar$zoo === void 0 ? void 0 + /// : babelHelpers.assertClassBrand(Foo, _Foo$bar$zoo.call(_Foo$bar), _x)._;` + /// + /// * Callee has a private field: + /// `o?.Foo.#self.getSelf?.().#m?.();` + /// -> `(_ref = o === null || o === void 0 ? void 0 : (_babelHelpers$assertC = + /// babelHelpers.assertClassBrand(Foo, o.Foo, _self)._).getSelf) === null || + /// _ref === void 0 ? void 0 : babelHelpers.assertClassBrand(Foo, _ref$call + /// = _ref.call(_babelHelpers$assertC), _m)._?.call(_ref$call);` + /// + fn transform_call_expression_to_bind_proper_context( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let Expression::CallExpression(call) = expr else { unreachable!() }; + + let callee = &mut call.callee; + // `Foo?.bar()?.zoo?.()` + // ^^^^^^^^^^^^^^^^^ callee is a member expression + // ^^^^^^^^^^^ object + let object = callee.to_member_expression_mut().object_mut(); + + let context = if let Some(result) = self.transform_chain_element_recursively(object, ctx) { + // `o?.Foo.#self.getSelf?.().#m?.();` -> `(_ref = o === null || o === void 0 ? void 0 : (_babelHelpers$assertC = + // babelHelpers.assertClassBrand(Foo, o.Foo, _self)._).getSelf)` + // ^^^^^^^^^^^^^^^^^^^^^^ to make sure get `getSelf` call has a proper context, we need to assign + // the parent of callee (i.e `o?.Foo.#self`) to a temp variable, + // and then use it as a first argument of `_ref.call`. + let (assignment, context) = self.duplicate_object(ctx.ast.move_expression(object), ctx); + *object = assignment; + *callee = Self::wrap_conditional_check(result, ctx.ast.move_expression(callee), ctx); + context + } else { + // `Foo?.bar()?.zoo?.().#x;` -> `(_Foo$bar$zoo = (_Foo$bar = Foo?.bar())?.zoo)` + // ^^^^^^^^^^^^^^^^ this is a optional function call, to make sure it has a proper context, + // we also need to assign `Foo?.bar()` to a temp variable, and then use + // it as a first argument of `_Foo$bar$zoo`. + let (assignment, context) = self.duplicate_object(ctx.ast.move_expression(object), ctx); + *object = assignment; + context + }; + + // After the below transformation, the `callee` will be a temp variable. + let result = self.transform_expression_to_wrap_nullish_check(callee, ctx); + let callee_owner = ctx.ast.move_expression(callee); + Self::substitute_callee_and_insert_context(call, callee_owner, context, ctx); + result + } + + /// Returns `left === null` + fn wrap_null_check(&self, left: Expression<'a>, ctx: &TraverseCtx<'a>) -> Expression<'a> { + let operator = if self.ctx.assumptions.no_document_all { + BinaryOperator::Equality + } else { + BinaryOperator::StrictEquality + }; + ctx.ast.expression_binary(SPAN, left, operator, ctx.ast.expression_null_literal(SPAN)) + } + + /// Returns `left === void 0` + fn wrap_void0_check(left: Expression<'a>, ctx: &TraverseCtx<'a>) -> Expression<'a> { + let operator = BinaryOperator::StrictEquality; + ctx.ast.expression_binary(SPAN, left, operator, ctx.ast.void_0(SPAN)) + } + + /// Returns `left1 === null || left2 === void 0` + fn wrap_nullish_check( + &self, + left1: Expression<'a>, + left2: Expression<'a>, + ctx: &TraverseCtx<'a>, + ) -> Expression<'a> { + let null_check = self.wrap_null_check(left1, ctx); + if self.ctx.assumptions.no_document_all { + null_check + } else { + let void0_check = Self::wrap_void0_check(left2, ctx); + ctx.ast.expression_logical(SPAN, null_check, LogicalOperator::Or, void0_check) + } + } + + /// Returns `test ? void 0 : alternative` + fn wrap_conditional_check( + test: Expression<'a>, + alternative: Expression<'a>, + ctx: &TraverseCtx<'a>, + ) -> Expression<'a> { + ctx.ast.expression_conditional(SPAN, test, ctx.ast.void_0(SPAN), alternative) + } + + /// Transform chain expression inside unary expression. + /// + /// Instance prop: + /// * `delete object?.#prop.xyz` + /// -> `object === null || object === void 0 ? true : delete _classPrivateFieldGet(_prop, object).xyz;` + /// * `delete object?.#prop?.xyz;` + /// -> `delete (object === null || object === void 0 ? void 0 : _classPrivateFieldGet(_prop, object))?.xyz;` + /// + /// Static prop: + /// * `delete object?.#prop.xyz` + /// -> `object === null || object === void 0 ? true : delete _assertClassBrand(Foo, object, _prop)._.xyz;` + /// * `delete object?.#prop?.xyz;` + /// -> `delete (object === null || object === void 0 ? void 0 : _assertClassBrand(Foo, object, _prop)._)?.xyz;` + // + // `#[inline]` so that compiler sees that `expr` is an `Expression::UnaryExpression`. + #[inline] + pub(super) fn transform_unary_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let Expression::UnaryExpression(unary_expr) = expr else { unreachable!() }; + if let Some((result, chain_expr)) = + self.transform_chain_expression_impl(&mut unary_expr.argument, ctx) + { + *expr = ctx.ast.expression_conditional( + unary_expr.span, + result, + ctx.ast.expression_boolean_literal(SPAN, true), + { + // We still need this unary expr, but it needs to be used as the alternative of the conditional + unary_expr.argument = chain_expr; + ctx.ast.move_expression(expr) + }, + ); + } } /// Transform tagged template expression where tag is a private field. @@ -887,17 +1391,17 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { let Expression::PrivateFieldExpression(field_expr) = &mut tagged_temp_expr.tag else { return; }; - if let Some(tag) = self.transform_tagged_template_expression_impl(field_expr, ctx) { + if let Some(tag) = self.transform_bindable_private_field(field_expr, ctx) { tagged_temp_expr.tag = tag; }; } - pub(super) fn transform_tagged_template_expression_impl( + fn transform_bindable_private_field( &mut self, field_expr: &mut PrivateFieldExpression<'a>, ctx: &mut TraverseCtx<'a>, ) -> Option> { - let (callee, object) = self.transform_private_field_callee(field_expr, ctx)?; + let (callee, context) = self.transform_private_field_callee(field_expr, ctx)?; // Return `.bind(object)`, to be substituted as tag of tagged template expression let callee = Expression::from(ctx.ast.member_expression_static( @@ -906,7 +1410,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { ctx.ast.identifier_name(SPAN, Atom::from("bind")), false, )); - let arguments = ctx.ast.vec1(Argument::from(object)); + let arguments = ctx.ast.vec1(Argument::from(context)); Some(ctx.ast.expression_call(field_expr.span, callee, NONE, arguments, false)) } @@ -967,6 +1471,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { object: Expression<'a>, ctx: &mut TraverseCtx<'a>, ) -> (Expression<'a>, Expression<'a>) { + assert_expr_neither_parenthesis_nor_typescript_syntax(&object); // TODO: Handle if in a function's params let temp_var_binding = match &object { Expression::Identifier(ident) => { diff --git a/crates/oxc_transformer/src/es2022/class_properties/utils.rs b/crates/oxc_transformer/src/es2022/class_properties/utils.rs index 3b7e923dfffbe5..9324a433307ae0 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/utils.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/utils.rs @@ -53,3 +53,11 @@ where pub(super) fn create_underscore_ident_name<'a>(ctx: &mut TraverseCtx<'a>) -> IdentifierName<'a> { ctx.ast.identifier_name(SPAN, Atom::from("_")) } + +#[inline] +pub(super) fn assert_expr_neither_parenthesis_nor_typescript_syntax(expr: &Expression) { + debug_assert!( + !(matches!(expr, Expression::ParenthesizedExpression(_)) || expr.is_typescript_syntax()), + "Should not be: {expr:?}", + ); +} diff --git a/crates/oxc_transformer/src/jsx/jsx_impl.rs b/crates/oxc_transformer/src/jsx/jsx_impl.rs index c766ea1bff25d3..e85cad1047e611 100644 --- a/crates/oxc_transformer/src/jsx/jsx_impl.rs +++ b/crates/oxc_transformer/src/jsx/jsx_impl.rs @@ -312,9 +312,20 @@ fn get_import_source(jsx_runtime_importer: &str, react_importer_len: u32) -> Ato Atom::from(&jsx_runtime_importer[..react_importer_len as usize]) } -/// Pragma used in classic mode -struct Pragma<'a> { - ident_parts: Vec>, +/// Pragma used in classic mode. +/// +/// `Double` is first as it's most common. +enum Pragma<'a> { + /// `React.createElement` + Double(Atom<'a>, Atom<'a>), + /// `createElement` + Single(Atom<'a>), + /// `foo.bar.qux` + Multiple(Vec>), + /// `this`, `this.foo`, `this.foo.bar.qux` + This(Vec>), + /// `import.meta`, `import.meta.foo`, `import.meta.foo.bar.qux` + ImportMeta(Vec>), } impl<'a> Pragma<'a> { @@ -322,50 +333,71 @@ impl<'a> Pragma<'a> { /// /// If provided option is invalid, raise an error and use default. fn parse( - pragma: Option<&String>, + pragma: Option<&str>, default_property_name: &'static str, ast: AstBuilder<'a>, ctx: &TransformCtx<'a>, ) -> Self { if let Some(pragma) = pragma { if pragma.is_empty() { - return Self::invalid(default_property_name, ctx); + ctx.error(diagnostics::invalid_pragma()); + } else { + return Self::parse_impl(pragma, ast); } - - let ident_parts = pragma.split('.').map(|item| ast.atom(item)).collect::>(); - Self { ident_parts } - } else { - Self::default(default_property_name) } - } - fn invalid(default_property_name: &'static str, ctx: &TransformCtx<'a>) -> Self { - ctx.error(diagnostics::invalid_pragma()); - Self::default(default_property_name) + Self::Double(Atom::from("React"), Atom::from(default_property_name)) } - fn default(default_property_name: &'static str) -> Self { - Self { ident_parts: vec![Atom::from("React"), Atom::from(default_property_name)] } + fn parse_impl(pragma: &str, ast: AstBuilder<'a>) -> Self { + let strs_to_atoms = |parts: &[&str]| parts.iter().map(|part| ast.atom(part)).collect(); + + let parts = pragma.split('.').collect::>(); + match &parts[..] { + [] => unreachable!(), + ["this", rest @ ..] => Self::This(strs_to_atoms(rest)), + ["import", "meta", rest @ ..] => Self::ImportMeta(strs_to_atoms(rest)), + [first, second] => Self::Double(ast.atom(first), ast.atom(second)), + [only] => Self::Single(ast.atom(only)), + parts => Self::Multiple(strs_to_atoms(parts)), + } } fn create_expression(&self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> { - let (mut expr, parts) = match &self.ident_parts[..] { - [] => unreachable!(), - [atom, rest @ ..] if atom == "this" => (ctx.ast.expression_this(SPAN), rest), - [first, second, rest @ ..] if first == "import" && second == "meta" => ( - ctx.ast.expression_meta_property( - SPAN, - ctx.ast.identifier_name(SPAN, first), - ctx.ast.identifier_name(SPAN, second), - ), - rest, - ), - [first, rest @ ..] => { + let (object, parts) = match self { + Self::Double(first, second) => { let object = get_read_identifier_reference(SPAN, first.clone(), ctx); - let expr = Expression::Identifier(ctx.alloc(object)); - (expr, rest) + return Expression::from(ctx.ast.member_expression_static( + SPAN, + object, + ctx.ast.identifier_name(SPAN, second.clone()), + false, + )); + } + Self::Single(single) => { + return get_read_identifier_reference(SPAN, single.clone(), ctx); + } + Self::Multiple(parts) => { + let mut parts = parts.iter(); + let first = parts.next().unwrap().clone(); + let object = get_read_identifier_reference(SPAN, first, ctx); + (object, parts) + } + Self::This(parts) => { + let object = ctx.ast.expression_this(SPAN); + (object, parts.iter()) + } + Self::ImportMeta(parts) => { + let object = ctx.ast.expression_meta_property( + SPAN, + ctx.ast.identifier_name(SPAN, Atom::from("import")), + ctx.ast.identifier_name(SPAN, Atom::from("meta")), + ); + (object, parts.iter()) } }; + + let mut expr = object; for item in parts { let name = ctx.ast.identifier_name(SPAN, item.clone()); expr = ctx.ast.member_expression_static(SPAN, expr, name, false).into(); @@ -386,8 +418,9 @@ impl<'a, 'ctx> JsxImpl<'a, 'ctx> { if options.import_source.is_some() { ctx.error(diagnostics::import_source_cannot_be_set()); } - let pragma = Pragma::parse(options.pragma.as_ref(), "createElement", ast, ctx); - let pragma_frag = Pragma::parse(options.pragma_frag.as_ref(), "Fragment", ast, ctx); + let pragma = Pragma::parse(options.pragma.as_deref(), "createElement", ast, ctx); + let pragma_frag = + Pragma::parse(options.pragma_frag.as_deref(), "Fragment", ast, ctx); Bindings::Classic(ClassicBindings { pragma, pragma_frag }) } JsxRuntime::Automatic => { @@ -1048,10 +1081,11 @@ fn get_read_identifier_reference<'a>( span: Span, name: Atom<'a>, ctx: &mut TraverseCtx<'a>, -) -> IdentifierReference<'a> { +) -> Expression<'a> { let reference_id = ctx.create_reference_in_current_scope(name.to_compact_str(), ReferenceFlags::Read); - ctx.ast.identifier_reference_with_reference_id(span, name, reference_id) + let ident = ctx.ast.alloc_identifier_reference_with_reference_id(span, name, reference_id); + Expression::Identifier(ident) } fn create_static_member_expression<'a>( @@ -1073,26 +1107,108 @@ mod test { use std::path::Path; use oxc_allocator::Allocator; - use oxc_ast::{ast::Expression, AstBuilder}; + use oxc_ast::ast::Expression; use oxc_semantic::{ScopeTree, SymbolTable}; + use oxc_syntax::{node::NodeId, scope::ScopeFlags}; use oxc_traverse::ReusableTraverseCtx; - use crate::{context::TransformCtx, TransformOptions}; + use crate::{TransformCtx, TransformOptions}; use super::Pragma; + macro_rules! setup { + ($traverse_ctx:ident, $transform_ctx:ident) => { + let allocator = Allocator::default(); + + let mut scopes = ScopeTree::default(); + scopes.add_scope(None, NodeId::DUMMY, ScopeFlags::Top); + + let traverse_ctx = ReusableTraverseCtx::new(scopes, SymbolTable::default(), &allocator); + // SAFETY: Macro user only gets a `&mut TraverseCtx`, which cannot be abused + let mut traverse_ctx = unsafe { traverse_ctx.unwrap() }; + let $traverse_ctx = &mut traverse_ctx; + + let $transform_ctx = + TransformCtx::new(Path::new("test.jsx"), &TransformOptions::default()); + }; + } + + #[test] + fn default_pragma() { + setup!(traverse_ctx, transform_ctx); + + let pragma = None; + let pragma = Pragma::parse(pragma, "createElement", traverse_ctx.ast, &transform_ctx); + let expr = pragma.create_expression(traverse_ctx); + + let Expression::StaticMemberExpression(member) = &expr else { panic!() }; + let Expression::Identifier(object) = &member.object else { panic!() }; + assert_eq!(object.name, "React"); + assert_eq!(member.property.name, "createElement"); + } + #[test] - fn this_expr_pragma() { - let alloc = Allocator::default(); - let ast = AstBuilder::new(&alloc); - let ctx = TransformCtx::new(Path::new("test.jsx"), &TransformOptions::default()); - // SAFETY: constructed to avoid unsoundness - let mut traverse_ctx = unsafe { - ReusableTraverseCtx::new(ScopeTree::default(), SymbolTable::default(), &alloc).unwrap() + fn single_part_pragma() { + setup!(traverse_ctx, transform_ctx); + + let pragma = Some("single"); + let pragma = Pragma::parse(pragma, "createElement", traverse_ctx.ast, &transform_ctx); + let expr = pragma.create_expression(traverse_ctx); + + let Expression::Identifier(ident) = &expr else { panic!() }; + assert_eq!(ident.name, "single"); + } + + #[test] + fn two_part_pragma() { + setup!(traverse_ctx, transform_ctx); + + let pragma = Some("first.second"); + let pragma = Pragma::parse(pragma, "createElement", traverse_ctx.ast, &transform_ctx); + let expr = pragma.create_expression(traverse_ctx); + + let Expression::StaticMemberExpression(member) = &expr else { panic!() }; + let Expression::Identifier(object) = &member.object else { panic!() }; + assert_eq!(object.name, "first"); + assert_eq!(member.property.name, "second"); + } + + #[test] + fn multi_part_pragma() { + setup!(traverse_ctx, transform_ctx); + + let pragma = Some("first.second.third"); + let pragma = Pragma::parse(pragma, "createElement", traverse_ctx.ast, &transform_ctx); + let expr = pragma.create_expression(traverse_ctx); + + let Expression::StaticMemberExpression(outer_member) = &expr else { panic!() }; + let Expression::StaticMemberExpression(inner_member) = &outer_member.object else { + panic!() }; + let Expression::Identifier(object) = &inner_member.object else { panic!() }; + assert_eq!(object.name, "first"); + assert_eq!(inner_member.property.name, "second"); + assert_eq!(outer_member.property.name, "third"); + } + + #[test] + fn this_pragma() { + setup!(traverse_ctx, transform_ctx); + + let pragma = Some("this"); + let pragma = Pragma::parse(pragma, "createElement", traverse_ctx.ast, &transform_ctx); + let expr = pragma.create_expression(traverse_ctx); + + assert!(matches!(&expr, Expression::ThisExpression(_))); + } + + #[test] + fn this_prop_pragma() { + setup!(traverse_ctx, transform_ctx); - let pragma = Pragma::parse(Some(&"this.a.b".to_string()), "createElement", ast, &ctx); - let expr = pragma.create_expression(&mut traverse_ctx); + let pragma = Some("this.a.b"); + let pragma = Pragma::parse(pragma, "createElement", traverse_ctx.ast, &transform_ctx); + let expr = pragma.create_expression(traverse_ctx); let Expression::StaticMemberExpression(outer_member) = &expr else { panic!() }; let Expression::StaticMemberExpression(inner_member) = &outer_member.object else { @@ -1105,17 +1221,24 @@ mod test { #[test] fn import_meta_pragma() { - let alloc = Allocator::default(); - let ast = AstBuilder::new(&alloc); - let ctx = TransformCtx::new(Path::new("test.jsx"), &TransformOptions::default()); - // SAFETY: constructed to avoid unsoundness - let mut traverse_ctx = unsafe { - ReusableTraverseCtx::new(ScopeTree::default(), SymbolTable::default(), &alloc).unwrap() - }; + setup!(traverse_ctx, transform_ctx); + + let pragma = Some("import.meta"); + let pragma = Pragma::parse(pragma, "createElement", traverse_ctx.ast, &transform_ctx); + let expr = pragma.create_expression(traverse_ctx); + + let Expression::MetaProperty(meta_prop) = &expr else { panic!() }; + assert_eq!(&meta_prop.meta.name, "import"); + assert_eq!(&meta_prop.property.name, "meta"); + } + + #[test] + fn import_meta_prop_pragma() { + setup!(traverse_ctx, transform_ctx); - let pragma = - Pragma::parse(Some(&"import.meta.prop".to_string()), "createElement", ast, &ctx); - let expr = pragma.create_expression(&mut traverse_ctx); + let pragma = Some("import.meta.prop"); + let pragma = Pragma::parse(pragma, "createElement", traverse_ctx.ast, &transform_ctx); + let expr = pragma.create_expression(traverse_ctx); let Expression::StaticMemberExpression(member) = &expr else { panic!() }; let Expression::MetaProperty(meta_prop) = &member.object else { panic!() }; diff --git a/crates/oxc_transformer/src/plugins/replace_global_defines.rs b/crates/oxc_transformer/src/plugins/replace_global_defines.rs index d647bbdc6645c4..6f3293f8acd4fc 100644 --- a/crates/oxc_transformer/src/plugins/replace_global_defines.rs +++ b/crates/oxc_transformer/src/plugins/replace_global_defines.rs @@ -538,7 +538,7 @@ impl<'a> ReplaceGlobalDefines<'a> { }; } - true + current_part_member_expression.is_none() } } diff --git a/crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs b/crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs index f57edd9678247c..f7ddd2c7830887 100644 --- a/crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs +++ b/crates/oxc_transformer/tests/integrations/plugins/replace_global_defines.rs @@ -71,6 +71,20 @@ fn dot_with_overlap() { test("import.meta.env.NODE_ENV", "import.meta.env.NODE_ENV", config.clone()); } +#[test] +fn dot_define_is_member_expr_postfix() { + let config = ReplaceGlobalDefinesConfig::new(&[ + ("__OBJ__", r#"{"process":{"env":{"SOMEVAR":"foo"}}}"#), + ("process.env.SOMEVAR", "\"SOMEVAR\""), + ]) + .unwrap(); + test( + "console.log(__OBJ__.process.env.SOMEVAR)", + "console.log({ 'process': { 'env': { 'SOMEVAR': 'foo' } } }.process.env.SOMEVAR);\n", + config.clone(), + ); +} + #[test] fn dot_nested() { let config = ReplaceGlobalDefinesConfig::new(&[("process", "production")]).unwrap(); diff --git a/crates/oxc_traverse/CHANGELOG.md b/crates/oxc_traverse/CHANGELOG.md index 97e8087e0e9c75..261080a66cf48f 100644 --- a/crates/oxc_traverse/CHANGELOG.md +++ b/crates/oxc_traverse/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +- f2f31a8 traverse: [**BREAKING**] Remove unsound APIs (#7514) (overlookmotel) + +### Features + +- 9c9deae traverse: Add `generate_uid_in_current_hoist_scope` method (#7423) (Dunqing) + +### Bug Fixes + + +### Documentation + +- 4d157c5 traverse: Document soundness hole (#7515) (overlookmotel) + ## [0.38.0] - 2024-11-26 ### Features diff --git a/crates/oxc_traverse/Cargo.toml b/crates/oxc_traverse/Cargo.toml index d191c9ab2833b5..a1dd33aceab506 100644 --- a/crates/oxc_traverse/Cargo.toml +++ b/crates/oxc_traverse/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_traverse" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true diff --git a/napi/minify/package.json b/napi/minify/package.json index 72cb6768a3c0e0..3693e036d8b4de 100644 --- a/napi/minify/package.json +++ b/napi/minify/package.json @@ -2,6 +2,7 @@ "name": "@oxc-minify/binding", "private": true, "scripts": { + "build-dev": "napi build --platform", "build": "napi build --platform --release", "test": "echo 'skip'" }, diff --git a/napi/parser/Cargo.toml b/napi/parser/Cargo.toml index 1a26303e4892f3..e5f8526815ab37 100644 --- a/napi/parser/Cargo.toml +++ b/napi/parser/Cargo.toml @@ -21,12 +21,13 @@ test = false doctest = false [dependencies] -oxc = { workspace = true, features = ["napi", "serialize"] } -oxc_napi = { workspace = true } +oxc = { workspace = true, features = ["serialize"] } + +rustc-hash = { workspace = true } +serde_json = { workspace = true } napi = { workspace = true, features = ["async"] } napi-derive = { workspace = true } -serde_json = { workspace = true } [package.metadata.cargo-shear] ignored = ["napi"] diff --git a/napi/parser/index.d.ts b/napi/parser/index.d.ts index ab2164ce2c59e5..252d1cc4e9b095 100644 --- a/napi/parser/index.d.ts +++ b/napi/parser/index.d.ts @@ -10,24 +10,20 @@ export interface Comment { } export interface EcmaScriptModule { + /** + * Has ESM syntax. + * + * i.e. `import` and `export` statements, and `import.meta`. + * + * Dynamic imports `import('foo')` are ignored since they can be used in non-ESM files. + */ + hasModuleSyntax: boolean /** Import Statements. */ staticImports: Array /** Export Statements. */ staticExports: Array } -export interface ExportEntry { - start: number - end: number - moduleRequest?: ValueSpan - /** The name under which the desired binding is exported by the module`. */ - importName: ExportImportName - /** The name used to export this binding by this module. */ - exportName: ExportExportName - /** The name that is used to locally access the exported value from within the importing module. */ - localName: ExportLocalName -} - export interface ExportExportName { kind: ExportExportNameKind name?: string @@ -81,40 +77,6 @@ export declare const enum ExportLocalNameKind { None = 'None' } -export interface ImportEntry { - /** - * The name under which the desired binding is exported by the module. - * - * ```js - * import { foo } from "mod"; - * // ^^^ - * import { foo as bar } from "mod"; - * // ^^^ - * ``` - */ - importName: ImportName - /** - * The name that is used to locally access the imported value from within the importing module. - * ```js - * import { foo } from "mod"; - * // ^^^ - * import { foo as bar } from "mod"; - * // ^^^ - * ``` - */ - localName: ValueSpan - /** - * Whether this binding is for a TypeScript type-only import. - * - * `true` for the following imports: - * ```ts - * import type { foo } from "mod"; - * import { type foo } from "mod"; - * ``` - */ - isType: boolean -} - export interface ImportName { kind: ImportNameKind name?: string @@ -174,7 +136,19 @@ export declare function parseWithoutReturn(filename: string, sourceText: string, export interface StaticExport { start: number end: number - entries: Array + entries: Array +} + +export interface StaticExportEntry { + start: number + end: number + moduleRequest?: ValueSpan + /** The name under which the desired binding is exported by the module`. */ + importName: ExportImportName + /** The name used to export this binding by this module. */ + exportName: ExportExportName + /** The name that is used to locally access the exported value from within the importing module. */ + localName: ExportLocalName } export interface StaticImport { @@ -196,7 +170,41 @@ export interface StaticImport { * * Empty for `import "mod"`. */ - entries: Array + entries: Array +} + +export interface StaticImportEntry { + /** + * The name under which the desired binding is exported by the module. + * + * ```js + * import { foo } from "mod"; + * // ^^^ + * import { foo as bar } from "mod"; + * // ^^^ + * ``` + */ + importName: ImportName + /** + * The name that is used to locally access the imported value from within the importing module. + * ```js + * import { foo } from "mod"; + * // ^^^ + * import { foo as bar } from "mod"; + * // ^^^ + * ``` + */ + localName: ValueSpan + /** + * Whether this binding is for a TypeScript type-only import. + * + * `true` for the following imports: + * ```ts + * import type { foo } from "mod"; + * import { type foo } from "mod"; + * ``` + */ + isType: boolean } export interface ValueSpan { diff --git a/napi/parser/index.js b/napi/parser/index.js index fcedfd9cf3a0fd..ff5b26bdbda4d5 100644 --- a/napi/parser/index.js +++ b/napi/parser/index.js @@ -1,7 +1,5 @@ const bindings = require('./bindings.js'); -module.exports.moduleLexerAsync = bindings.moduleLexerAsync; -module.exports.moduleLexerSync = bindings.moduleLexerSync; module.exports.parseWithoutReturn = bindings.parseWithoutReturn; module.exports.parseAsync = async function parseAsync(...args) { diff --git a/napi/parser/package.json b/napi/parser/package.json index 571cfc061572f3..085ff9202d5957 100644 --- a/napi/parser/package.json +++ b/napi/parser/package.json @@ -2,8 +2,8 @@ "name": "@oxc-parser/binding", "private": true, "scripts": { - "build": "napi build --platform --release --js bindings.js", "build-dev": "napi build --platform --js bindings.js", + "build": "napi build --platform --js bindings.js --release", "test": "vitest --typecheck run ./test" }, "napi": { diff --git a/crates/oxc_napi/src/parse.rs b/napi/parser/src/convert.rs similarity index 53% rename from crates/oxc_napi/src/parse.rs rename to napi/parser/src/convert.rs index 9fc35d8c3f07b6..5800166d74ec38 100644 --- a/crates/oxc_napi/src/parse.rs +++ b/napi/parser/src/convert.rs @@ -1,208 +1,15 @@ -use napi_derive::napi; - use rustc_hash::FxHashMap; -use oxc_span::Span; -use oxc_syntax::module_record::{self, ModuleRecord}; - -#[napi(object)] -#[derive(Default)] -pub struct ParserOptions { - #[napi(ts_type = "'script' | 'module' | 'unambiguous' | undefined")] - pub source_type: Option, - - /// Treat the source text as `js`, `jsx`, `ts`, or `tsx`. - #[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")] - pub lang: Option, - - /// Emit `ParenthesizedExpression` in AST. - /// - /// If this option is true, parenthesized expressions are represented by - /// (non-standard) `ParenthesizedExpression` nodes that have a single `expression` property - /// containing the expression inside parentheses. - /// - /// Default: true - pub preserve_parens: Option, -} - -#[napi(object)] -#[derive(Default)] -pub struct ParseResult { - #[napi(ts_type = "import(\"@oxc-project/types\").Program")] - pub program: String, - pub module: EcmaScriptModule, - pub comments: Vec, - pub errors: Vec, -} - -#[napi(object)] -pub struct Comment { - #[napi(ts_type = "'Line' | 'Block'")] - pub r#type: &'static str, - pub value: String, - pub start: u32, - pub end: u32, -} - -#[napi(object)] -#[derive(Default)] -pub struct EcmaScriptModule { - /// Import Statements. - pub static_imports: Vec, - /// Export Statements. - pub static_exports: Vec, -} - -#[napi(object)] -pub struct ValueSpan { - pub value: String, - pub start: u32, - pub end: u32, -} - -#[napi(object)] -pub struct StaticImport { - /// Start of import statement. - pub start: u32, - /// End of import statement. - pub end: u32, - /// Import source. - /// - /// ```js - /// import { foo } from "mod"; - /// // ^^^ - /// ``` - pub module_request: ValueSpan, - /// Import specifiers. - /// - /// Empty for `import "mod"`. - pub entries: Vec, -} - -#[napi(object)] -pub struct ImportEntry { - /// The name under which the desired binding is exported by the module. - /// - /// ```js - /// import { foo } from "mod"; - /// // ^^^ - /// import { foo as bar } from "mod"; - /// // ^^^ - /// ``` - pub import_name: ImportName, - /// The name that is used to locally access the imported value from within the importing module. - /// ```js - /// import { foo } from "mod"; - /// // ^^^ - /// import { foo as bar } from "mod"; - /// // ^^^ - /// ``` - pub local_name: ValueSpan, +use oxc::{ + span::Span, + syntax::module_record::{self, ModuleRecord}, +}; - /// Whether this binding is for a TypeScript type-only import. - /// - /// `true` for the following imports: - /// ```ts - /// import type { foo } from "mod"; - /// import { type foo } from "mod"; - /// ``` - pub is_type: bool, -} - -#[napi(string_enum)] -pub enum ImportNameKind { - /// `import { x } from "mod"` - Name, - /// `import * as ns from "mod"` - NamespaceObject, - /// `import defaultExport from "mod"` - Default, -} - -#[napi(object)] -pub struct ImportName { - pub kind: ImportNameKind, - pub name: Option, - pub start: Option, - pub end: Option, -} - -#[napi(object)] -pub struct StaticExport { - pub start: u32, - pub end: u32, - pub entries: Vec, -} - -#[napi(object)] -pub struct ExportEntry { - pub start: u32, - pub end: u32, - pub module_request: Option, - /// The name under which the desired binding is exported by the module`. - pub import_name: ExportImportName, - /// The name used to export this binding by this module. - pub export_name: ExportExportName, - /// The name that is used to locally access the exported value from within the importing module. - pub local_name: ExportLocalName, -} - -#[napi(string_enum)] -pub enum ExportImportNameKind { - /// `export { name } - Name, - /// `export * as ns from "mod"` - All, - /// `export * from "mod"` - AllButDefault, - /// Does not have a specifier. - None, -} - -#[napi(object)] -pub struct ExportImportName { - pub kind: ExportImportNameKind, - pub name: Option, - pub start: Option, - pub end: Option, -} - -#[napi(string_enum)] -pub enum ExportExportNameKind { - /// `export { name } - Name, - /// `export default expression` - Default, - /// `export * from "mod" - None, -} - -#[napi(object)] -pub struct ExportExportName { - pub kind: ExportExportNameKind, - pub name: Option, - pub start: Option, - pub end: Option, -} - -#[napi(object)] -pub struct ExportLocalName { - pub kind: ExportLocalNameKind, - pub name: Option, - pub start: Option, - pub end: Option, -} - -#[napi(string_enum)] -pub enum ExportLocalNameKind { - /// `export { name } - Name, - /// `export default expression` - Default, - /// If the exported value is not locally accessible from within the module. - /// `export default function () {}` - None, -} +use crate::types::{ + EcmaScriptModule, ExportExportName, ExportExportNameKind, ExportImportName, + ExportImportNameKind, ExportLocalName, ExportLocalNameKind, ImportName, ImportNameKind, + StaticExport, StaticExportEntry, StaticImport, StaticImportEntry, ValueSpan, +}; impl From<&ModuleRecord<'_>> for EcmaScriptModule { fn from(record: &ModuleRecord<'_>) -> Self { @@ -215,7 +22,7 @@ impl From<&ModuleRecord<'_>> for EcmaScriptModule { .import_entries .iter() .filter(|e| e.statement_span == m.statement_span) - .map(ImportEntry::from) + .map(StaticImportEntry::from) .collect::>(); { StaticImport { @@ -239,10 +46,10 @@ impl From<&ModuleRecord<'_>> for EcmaScriptModule { .iter() .chain(record.indirect_export_entries.iter()) .chain(record.star_export_entries.iter()) - .map(|e| (e.statement_span, ExportEntry::from(e))) + .map(|e| (e.statement_span, StaticExportEntry::from(e))) .collect::>() .into_iter() - .fold(FxHashMap::>::default(), |mut acc, (span, e)| { + .fold(FxHashMap::>::default(), |mut acc, (span, e)| { acc.entry(span).or_default().push(e); acc }) @@ -251,11 +58,24 @@ impl From<&ModuleRecord<'_>> for EcmaScriptModule { .collect::>(); static_exports.sort_unstable_by_key(|e| e.start); - Self { static_imports, static_exports } + Self { has_module_syntax: record.has_module_syntax, static_imports, static_exports } + } +} + +impl From<&module_record::ExportEntry<'_>> for StaticExportEntry { + fn from(e: &module_record::ExportEntry) -> Self { + Self { + start: e.span.start, + end: e.span.end, + module_request: e.module_request.as_ref().map(ValueSpan::from), + import_name: ExportImportName::from(&e.import_name), + export_name: ExportExportName::from(&e.export_name), + local_name: ExportLocalName::from(&e.local_name), + } } } -impl From<&module_record::ImportEntry<'_>> for ImportEntry { +impl From<&module_record::ImportEntry<'_>> for StaticImportEntry { fn from(e: &module_record::ImportEntry<'_>) -> Self { Self { import_name: ImportName::from(&e.import_name), @@ -295,19 +115,6 @@ impl From<&module_record::NameSpan<'_>> for ValueSpan { } } -impl From<&module_record::ExportEntry<'_>> for ExportEntry { - fn from(e: &module_record::ExportEntry) -> Self { - Self { - start: e.span.start, - end: e.span.end, - module_request: e.module_request.as_ref().map(ValueSpan::from), - import_name: ExportImportName::from(&e.import_name), - export_name: ExportExportName::from(&e.export_name), - local_name: ExportLocalName::from(&e.local_name), - } - } -} - impl From<&module_record::ExportImportName<'_>> for ExportImportName { fn from(e: &module_record::ExportImportName<'_>) -> Self { let (kind, name, start, end) = match e { diff --git a/napi/parser/src/lib.rs b/napi/parser/src/lib.rs index cdf78570f30a9e..459a9929720b1e 100644 --- a/napi/parser/src/lib.rs +++ b/napi/parser/src/lib.rs @@ -2,6 +2,9 @@ clippy::needless_pass_by_value // Napi value need to be passed as value )] +mod convert; +mod types; + use std::sync::Arc; use napi::{bindgen_prelude::AsyncTask, Task}; @@ -14,7 +17,8 @@ use oxc::{ parser::{ParseOptions, Parser, ParserReturn}, span::SourceType, }; -use oxc_napi::parse::{Comment, EcmaScriptModule, ParseResult, ParserOptions}; + +pub use crate::types::{Comment, EcmaScriptModule, ParseResult, ParserOptions}; fn get_source_type(filename: &str, options: &ParserOptions) -> SourceType { match options.lang.as_deref() { @@ -83,8 +87,8 @@ fn parse_with_return(filename: &str, source_text: &str, options: &ParserOptions) .iter() .map(|comment| Comment { r#type: match comment.kind { - CommentKind::Line => "Line", - CommentKind::Block => "Block", + CommentKind::Line => String::from("Line"), + CommentKind::Block => String::from("Block"), }, value: comment.content_span().source_text(source_text).to_string(), start: comment.span.start, diff --git a/napi/parser/src/types.rs b/napi/parser/src/types.rs new file mode 100644 index 00000000000000..ba379f9152786a --- /dev/null +++ b/napi/parser/src/types.rs @@ -0,0 +1,204 @@ +use napi_derive::napi; + +#[napi(object)] +#[derive(Default)] +pub struct ParserOptions { + #[napi(ts_type = "'script' | 'module' | 'unambiguous' | undefined")] + pub source_type: Option, + + /// Treat the source text as `js`, `jsx`, `ts`, or `tsx`. + #[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")] + pub lang: Option, + + /// Emit `ParenthesizedExpression` in AST. + /// + /// If this option is true, parenthesized expressions are represented by + /// (non-standard) `ParenthesizedExpression` nodes that have a single `expression` property + /// containing the expression inside parentheses. + /// + /// Default: true + pub preserve_parens: Option, +} + +#[napi(object)] +pub struct ParseResult { + #[napi(ts_type = "import(\"@oxc-project/types\").Program")] + pub program: String, + pub module: EcmaScriptModule, + pub comments: Vec, + pub errors: Vec, +} + +#[napi(object)] +pub struct Comment { + #[napi(ts_type = "'Line' | 'Block'")] + pub r#type: String, + pub value: String, + pub start: u32, + pub end: u32, +} + +#[napi(object)] +pub struct EcmaScriptModule { + /// Has ESM syntax. + /// + /// i.e. `import` and `export` statements, and `import.meta`. + /// + /// Dynamic imports `import('foo')` are ignored since they can be used in non-ESM files. + pub has_module_syntax: bool, + /// Import Statements. + pub static_imports: Vec, + /// Export Statements. + pub static_exports: Vec, +} + +#[napi(object)] +pub struct ValueSpan { + pub value: String, + pub start: u32, + pub end: u32, +} + +#[napi(object)] +pub struct StaticImport { + /// Start of import statement. + pub start: u32, + /// End of import statement. + pub end: u32, + /// Import source. + /// + /// ```js + /// import { foo } from "mod"; + /// // ^^^ + /// ``` + pub module_request: ValueSpan, + /// Import specifiers. + /// + /// Empty for `import "mod"`. + pub entries: Vec, +} + +#[napi(object)] +pub struct StaticImportEntry { + /// The name under which the desired binding is exported by the module. + /// + /// ```js + /// import { foo } from "mod"; + /// // ^^^ + /// import { foo as bar } from "mod"; + /// // ^^^ + /// ``` + pub import_name: ImportName, + /// The name that is used to locally access the imported value from within the importing module. + /// ```js + /// import { foo } from "mod"; + /// // ^^^ + /// import { foo as bar } from "mod"; + /// // ^^^ + /// ``` + pub local_name: ValueSpan, + + /// Whether this binding is for a TypeScript type-only import. + /// + /// `true` for the following imports: + /// ```ts + /// import type { foo } from "mod"; + /// import { type foo } from "mod"; + /// ``` + pub is_type: bool, +} + +#[napi(string_enum)] +pub enum ImportNameKind { + /// `import { x } from "mod"` + Name, + /// `import * as ns from "mod"` + NamespaceObject, + /// `import defaultExport from "mod"` + Default, +} + +#[napi(object)] +pub struct ImportName { + pub kind: ImportNameKind, + pub name: Option, + pub start: Option, + pub end: Option, +} + +#[napi(object)] +pub struct StaticExportEntry { + pub start: u32, + pub end: u32, + pub module_request: Option, + /// The name under which the desired binding is exported by the module`. + pub import_name: ExportImportName, + /// The name used to export this binding by this module. + pub export_name: ExportExportName, + /// The name that is used to locally access the exported value from within the importing module. + pub local_name: ExportLocalName, +} + +#[napi(object)] +pub struct StaticExport { + pub start: u32, + pub end: u32, + pub entries: Vec, +} + +#[napi(string_enum)] +pub enum ExportImportNameKind { + /// `export { name } + Name, + /// `export * as ns from "mod"` + All, + /// `export * from "mod"` + AllButDefault, + /// Does not have a specifier. + None, +} + +#[napi(object)] +pub struct ExportImportName { + pub kind: ExportImportNameKind, + pub name: Option, + pub start: Option, + pub end: Option, +} + +#[napi(string_enum)] +pub enum ExportExportNameKind { + /// `export { name } + Name, + /// `export default expression` + Default, + /// `export * from "mod" + None, +} + +#[napi(object)] +pub struct ExportExportName { + pub kind: ExportExportNameKind, + pub name: Option, + pub start: Option, + pub end: Option, +} + +#[napi(object)] +pub struct ExportLocalName { + pub kind: ExportLocalNameKind, + pub name: Option, + pub start: Option, + pub end: Option, +} + +#[napi(string_enum)] +pub enum ExportLocalNameKind { + /// `export { name } + Name, + /// `export default expression` + Default, + /// If the exported value is not locally accessible from within the module. + /// `export default function () {}` + None, +} diff --git a/napi/parser/test/__snapshots__/esm.test.ts.snap b/napi/parser/test/__snapshots__/esm.test.ts.snap index b91c93c2b166a6..a261d34cbd5778 100644 --- a/napi/parser/test/__snapshots__/esm.test.ts.snap +++ b/napi/parser/test/__snapshots__/esm.test.ts.snap @@ -2,6 +2,7 @@ exports[`esm > export * as name1 from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -37,6 +38,7 @@ exports[`esm > export * as name1 from "module-name"; 1`] = ` exports[`esm > export * from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -69,6 +71,7 @@ exports[`esm > export * from "module-name"; 1`] = ` exports[`esm > export { default as name1 } from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -107,6 +110,7 @@ exports[`esm > export { default as name1 } from "module-name"; 1`] = ` exports[`esm > export { default, /* …, */ } from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -145,6 +149,7 @@ exports[`esm > export { default, /* …, */ } from "module-name"; 1`] = ` exports[`esm > export { import1 as name1, import2 as name2, /* …, */ nameN } from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -231,6 +236,7 @@ exports[`esm > export { import1 as name1, import2 as name2, /* …, */ nameN } f exports[`esm > export { name1 as default /*, … */ }; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -264,6 +270,7 @@ exports[`esm > export { name1 as default /*, … */ }; 1`] = ` exports[`esm > export { name1, /* …, */ nameN } from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -326,6 +333,7 @@ exports[`esm > export { name1, /* …, */ nameN } from "module-name"; 1`] = ` exports[`esm > export { name1, /* …, */ nameN }; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -378,6 +386,7 @@ exports[`esm > export { name1, /* …, */ nameN }; 1`] = ` exports[`esm > export { variable1 as "string name" }; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -411,6 +420,7 @@ exports[`esm > export { variable1 as "string name" }; 1`] = ` exports[`esm > export { variable1 as name1, variable2 as name2, /* …, */ nameN }; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -482,6 +492,7 @@ exports[`esm > export { variable1 as name1, variable2 as name2, /* …, */ nameN exports[`esm > export class ClassName { /* … */ } 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -515,6 +526,7 @@ exports[`esm > export class ClassName { /* … */ } 1`] = ` exports[`esm > export const [ name1, name2 ] = array; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -567,6 +579,7 @@ exports[`esm > export const [ name1, name2 ] = array; 1`] = ` exports[`esm > export const { name1, name2: bar } = o; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -619,6 +632,7 @@ exports[`esm > export const { name1, name2: bar } = o; 1`] = ` exports[`esm > export const name1 = 1, name2 = 2/*, … */; // also var, let 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -671,6 +685,7 @@ exports[`esm > export const name1 = 1, name2 = 2/*, … */; // also var, let 1`] exports[`esm > export default class { /* … */ } 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -700,6 +715,7 @@ exports[`esm > export default class { /* … */ } 1`] = ` exports[`esm > export default class ClassName { /* … */ } 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -732,6 +748,7 @@ exports[`esm > export default class ClassName { /* … */ } 1`] = ` exports[`esm > export default expression; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -764,6 +781,7 @@ exports[`esm > export default expression; 1`] = ` exports[`esm > export default function () { /* … */ } 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -793,6 +811,7 @@ exports[`esm > export default function () { /* … */ } 1`] = ` exports[`esm > export default function functionName() { /* … */ } 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -825,6 +844,7 @@ exports[`esm > export default function functionName() { /* … */ } 1`] = ` exports[`esm > export default function* () { /* … */ } 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -854,6 +874,7 @@ exports[`esm > export default function* () { /* … */ } 1`] = ` exports[`esm > export default function* generatorFunctionName() { /* … */ } 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -886,6 +907,7 @@ exports[`esm > export default function* generatorFunctionName() { /* … */ } 1` exports[`esm > export function functionName() { /* … */ } 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -919,6 +941,7 @@ exports[`esm > export function functionName() { /* … */ } 1`] = ` exports[`esm > export function* generatorFunctionName() { /* … */ } 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -952,6 +975,7 @@ exports[`esm > export function* generatorFunctionName() { /* … */ } 1`] = ` exports[`esm > export let name1, name2/*, … */; // also var 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [], "staticExports": [ { @@ -1004,6 +1028,7 @@ exports[`esm > export let name1, name2/*, … */; // also var 1`] = ` exports[`esm > import "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [ { "start": 0, @@ -1022,6 +1047,7 @@ exports[`esm > import "module-name"; 1`] = ` exports[`esm > import * as name from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [ { "start": 0, @@ -1052,6 +1078,7 @@ exports[`esm > import * as name from "module-name"; 1`] = ` exports[`esm > import { "string name" as alias } from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [ { "start": 0, @@ -1085,6 +1112,7 @@ exports[`esm > import { "string name" as alias } from "module-name"; 1`] = ` exports[`esm > import { default as alias } from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [ { "start": 0, @@ -1118,6 +1146,7 @@ exports[`esm > import { default as alias } from "module-name"; 1`] = ` exports[`esm > import { export1 } from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [ { "start": 0, @@ -1151,6 +1180,7 @@ exports[`esm > import { export1 } from "module-name"; 1`] = ` exports[`esm > import { export1 as alias1 } from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [ { "start": 0, @@ -1184,6 +1214,7 @@ exports[`esm > import { export1 as alias1 } from "module-name"; 1`] = ` exports[`esm > import { export1, export2 } from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [ { "start": 0, @@ -1231,6 +1262,7 @@ exports[`esm > import { export1, export2 } from "module-name"; 1`] = ` exports[`esm > import { export1, export2 as alias2, /* … */ } from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [ { "start": 0, @@ -1278,6 +1310,7 @@ exports[`esm > import { export1, export2 as alias2, /* … */ } from "module-nam exports[`esm > import defaultExport from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [ { "start": 0, @@ -1310,6 +1343,7 @@ exports[`esm > import defaultExport from "module-name"; 1`] = ` exports[`esm > import defaultExport, * as name from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [ { "start": 0, @@ -1353,6 +1387,7 @@ exports[`esm > import defaultExport, * as name from "module-name"; 1`] = ` exports[`esm > import defaultExport, { export1, /* … */ } from "module-name"; 1`] = ` "{ + "hasModuleSyntax": true, "staticImports": [ { "start": 0, diff --git a/napi/parser/test/esm.test.ts b/napi/parser/test/esm.test.ts index 2537fe74dc072d..411926a1de95b7 100644 --- a/napi/parser/test/esm.test.ts +++ b/napi/parser/test/esm.test.ts @@ -52,6 +52,7 @@ export { default as name1 } from "module-name"; expect(ret.program.body.length).toBeGreaterThan(0); expect(ret.errors.length).toBe(0); expect(JSON.stringify(ret.module, null, 2)).toMatchSnapshot(); + expect(ret.module.hasModuleSyntax).toBe(true); if (s.startsWith('import')) { expect(ret.module.staticImports.length).toBe(1); expect(ret.module.staticExports.length).toBe(0); @@ -62,3 +63,20 @@ export { default as name1 } from "module-name"; } }); }); + +describe('hasModuleSyntax', () => { + test('import.meta', () => { + const ret = parseSync('test.js', 'import.meta.foo'); + expect(ret.module.hasModuleSyntax).toBe(true); + }); + + test('import expression', () => { + const ret = parseSync('test.js', "import('foo')"); + expect(ret.module.hasModuleSyntax).toBe(false); + }); + + test('script', () => { + const ret = parseSync('test.js', "require('foo')"); + expect(ret.module.hasModuleSyntax).toBe(false); + }); +}); diff --git a/napi/transform/CHANGELOG.md b/napi/transform/CHANGELOG.md index e23f04d7aa79fa..d7772718dff27d 100644 --- a/napi/transform/CHANGELOG.md +++ b/napi/transform/CHANGELOG.md @@ -4,6 +4,22 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +### Features + +- 5864352 napi/transform: Add `TransformerOptions::assumptions` (#7601) (翠 / green) +- 771c698 oxc: Remove `oxc_napi` crate (#7634) (Boshen) +- bd977cf oxc: Add `oxc_napi` crate (#7612) (Boshen) + +### Bug Fixes + +- be2293a napi/transform: Respect `options.sourcemap` for id (#7590) (Kevin Deng 三咲智子) + +### Refactor + +- b4f3812 oxc_napi: Remove `source_map` - moved to its crate (#7614) (Boshen) + ## [0.38.0] - 2024-11-26 - bb2c0c2 transformer: [**BREAKING**] Return `String` as error instead of OxcDiagnostic (#7424) (Boshen) diff --git a/napi/transform/Cargo.toml b/napi/transform/Cargo.toml index a525b17d61e4cc..6a7cb381814277 100644 --- a/napi/transform/Cargo.toml +++ b/napi/transform/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxc_transform_napi" -version = "0.38.0" +version = "0.39.0" authors.workspace = true categories.workspace = true edition.workspace = true @@ -21,9 +21,10 @@ test = false doctest = false [dependencies] -oxc = { workspace = true, features = ["full", "napi"] } -oxc_napi = { workspace = true } -oxc_sourcemap = { workspace = true, features = ["napi"] } +oxc = { workspace = true, features = ["full"] } +oxc_sourcemap = { workspace = true, features = ["napi", "rayon"] } + +rustc-hash = { workspace = true } napi = { workspace = true } napi-derive = { workspace = true } diff --git a/napi/transform/package.json b/napi/transform/package.json index c7efd9a2953c1a..08b414f6a54178 100644 --- a/napi/transform/package.json +++ b/napi/transform/package.json @@ -2,6 +2,7 @@ "name": "@oxc-transform/binding", "private": true, "scripts": { + "build-dev": "napi build --platform", "build": "napi build --platform --release", "test": "vitest --typecheck run ./test" }, diff --git a/napi/transform/src/isolated_declaration.rs b/napi/transform/src/isolated_declaration.rs index babd87f31703e3..49f33cd8b2437e 100644 --- a/napi/transform/src/isolated_declaration.rs +++ b/napi/transform/src/isolated_declaration.rs @@ -9,11 +9,38 @@ use oxc::{ parser::Parser, span::SourceType, }; -use oxc_napi::isolated_declarations::{IsolatedDeclarationsOptions, IsolatedDeclarationsResult}; -use oxc_sourcemap::napi::SourceMap; use crate::errors::wrap_diagnostics; +use oxc_sourcemap::napi::SourceMap; + +#[napi(object)] +pub struct IsolatedDeclarationsResult { + pub code: String, + pub map: Option, + pub errors: Vec, +} + +#[napi(object)] +#[derive(Debug, Default, Clone, Copy)] +pub struct IsolatedDeclarationsOptions { + /// Do not emit declarations for code that has an @internal annotation in its JSDoc comment. + /// This is an internal compiler option; use at your own risk, because the compiler does not check that the result is valid. + /// + /// Default: `false` + /// + /// See + pub strip_internal: Option, + + pub sourcemap: Option, +} + +impl From for oxc::isolated_declarations::IsolatedDeclarationsOptions { + fn from(options: IsolatedDeclarationsOptions) -> Self { + Self { strip_internal: options.strip_internal.unwrap_or_default() } + } +} + /// TypeScript Isolated Declarations for Standalone DTS Emit #[allow(clippy::needless_pass_by_value)] #[napi] diff --git a/napi/transform/src/lib.rs b/napi/transform/src/lib.rs index a4568decfefcd4..eff0ebcfd88937 100644 --- a/napi/transform/src/lib.rs +++ b/napi/transform/src/lib.rs @@ -1,7 +1,5 @@ mod errors; -pub use oxc_napi::{isolated_declarations, transform}; - mod isolated_declaration; pub use isolated_declaration::*; diff --git a/napi/transform/src/transformer.rs b/napi/transform/src/transformer.rs index ce5e5854f35668..30af9f97537837 100644 --- a/napi/transform/src/transformer.rs +++ b/napi/transform/src/transformer.rs @@ -1,20 +1,396 @@ -use std::path::Path; +// NOTE: Types must be aligned with [@types/babel__core](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b5dc32740d9b45d11cff9b025896dd333c795b39/types/babel__core/index.d.ts). +#![allow(rustdoc::bare_urls)] + +use std::path::{Path, PathBuf}; use napi::Either; use napi_derive::napi; +use rustc_hash::FxHashMap; use oxc::{ codegen::CodegenReturn, diagnostics::OxcDiagnostic, - isolated_declarations::IsolatedDeclarationsOptions, span::SourceType, - transformer::{InjectGlobalVariablesConfig, InjectImport, ReplaceGlobalDefinesConfig}, + transformer::{ + EnvOptions, InjectGlobalVariablesConfig, InjectImport, JsxRuntime, + ReplaceGlobalDefinesConfig, RewriteExtensionsMode, + }, CompilerInterface, }; -use oxc_napi::transform::{TransformOptions, TransformResult}; use oxc_sourcemap::napi::SourceMap; -use crate::errors::wrap_diagnostics; +use crate::{errors::wrap_diagnostics, IsolatedDeclarationsOptions}; + +#[derive(Default)] +#[napi(object)] +pub struct TransformResult { + /// The transformed code. + /// + /// If parsing failed, this will be an empty string. + pub code: String, + + /// The source map for the transformed code. + /// + /// This will be set if {@link TransformOptions#sourcemap} is `true`. + pub map: Option, + + /// The `.d.ts` declaration file for the transformed code. Declarations are + /// only generated if `declaration` is set to `true` and a TypeScript file + /// is provided. + /// + /// If parsing failed and `declaration` is set, this will be an empty string. + /// + /// @see {@link TypeScriptOptions#declaration} + /// @see [declaration tsconfig option](https://www.typescriptlang.org/tsconfig/#declaration) + pub declaration: Option, + + /// Declaration source map. Only generated if both + /// {@link TypeScriptOptions#declaration declaration} and + /// {@link TransformOptions#sourcemap sourcemap} are set to `true`. + pub declaration_map: Option, + + /// Parse and transformation errors. + /// + /// Oxc's parser recovers from common syntax errors, meaning that + /// transformed code may still be available even if there are errors in this + /// list. + pub errors: Vec, +} + +/// Options for transforming a JavaScript or TypeScript file. +/// +/// @see {@link transform} +#[napi(object)] +#[derive(Default)] +pub struct TransformOptions { + #[napi(ts_type = "'script' | 'module' | 'unambiguous' | undefined")] + pub source_type: Option, + + /// Treat the source text as `js`, `jsx`, `ts`, or `tsx`. + #[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")] + pub lang: Option, + + /// The current working directory. Used to resolve relative paths in other + /// options. + pub cwd: Option, + + /// Enable source map generation. + /// + /// When `true`, the `sourceMap` field of transform result objects will be populated. + /// + /// @default false + /// + /// @see {@link SourceMap} + pub sourcemap: Option, + + /// Set assumptions in order to produce smaller output. + pub assumptions: Option, + + /// Configure how TypeScript is transformed. + pub typescript: Option, + + /// Configure how TSX and JSX are transformed. + pub jsx: Option, + + /// Sets the target environment for the generated JavaScript. + /// + /// The lowest target is `es2015`. + /// + /// Example: + /// + /// * 'es2015' + /// * ['es2020', 'chrome58', 'edge16', 'firefox57', 'node12', 'safari11'] + /// + /// @default `esnext` (No transformation) + /// + /// @see [esbuild#target](https://esbuild.github.io/api/#target) + pub target: Option>>, + + /// Define Plugin + #[napi(ts_type = "Record")] + pub define: Option>, + + /// Inject Plugin + #[napi(ts_type = "Record")] + pub inject: Option>>>, +} + +impl TryFrom for oxc::transformer::TransformOptions { + type Error = String; + + fn try_from(options: TransformOptions) -> Result { + let env = match options.target { + Some(Either::A(s)) => EnvOptions::from_target(&s)?, + Some(Either::B(list)) => EnvOptions::from_target_list(&list)?, + _ => EnvOptions::default(), + }; + Ok(Self { + cwd: options.cwd.map(PathBuf::from).unwrap_or_default(), + assumptions: options.assumptions.map(Into::into).unwrap_or_default(), + typescript: options + .typescript + .map(oxc::transformer::TypeScriptOptions::from) + .unwrap_or_default(), + jsx: options.jsx.map(Into::into).unwrap_or_default(), + env, + ..Self::default() + }) + } +} + +#[napi(object)] +#[derive(Default, Debug)] +pub struct CompilerAssumptions { + pub ignore_function_length: Option, + pub no_document_all: Option, + pub object_rest_no_symbols: Option, + pub pure_getters: Option, + pub set_public_class_fields: Option, +} + +impl From for oxc::transformer::CompilerAssumptions { + fn from(value: CompilerAssumptions) -> Self { + let ops = oxc::transformer::CompilerAssumptions::default(); + Self { + ignore_function_length: value + .ignore_function_length + .unwrap_or(ops.ignore_function_length), + no_document_all: value.no_document_all.unwrap_or(ops.no_document_all), + object_rest_no_symbols: value + .object_rest_no_symbols + .unwrap_or(ops.object_rest_no_symbols), + pure_getters: value.pure_getters.unwrap_or(ops.pure_getters), + set_public_class_fields: value + .set_public_class_fields + .unwrap_or(ops.set_public_class_fields), + ..ops + } + } +} + +#[napi(object)] +#[derive(Default)] +pub struct TypeScriptOptions { + pub jsx_pragma: Option, + pub jsx_pragma_frag: Option, + pub only_remove_type_imports: Option, + pub allow_namespaces: Option, + pub allow_declare_fields: Option, + /// Also generate a `.d.ts` declaration file for TypeScript files. + /// + /// The source file must be compliant with all + /// [`isolatedDeclarations`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html#isolated-declarations) + /// requirements. + /// + /// @default false + pub declaration: Option, + /// Rewrite or remove TypeScript import/export declaration extensions. + /// + /// - When set to `rewrite`, it will change `.ts`, `.mts`, `.cts` extensions to `.js`, `.mjs`, `.cjs` respectively. + /// - When set to `remove`, it will remove `.ts`/`.mts`/`.cts`/`.tsx` extension entirely. + /// - When set to `true`, it's equivalent to `rewrite`. + /// - When set to `false` or omitted, no changes will be made to the extensions. + /// + /// @default false + #[napi(ts_type = "'rewrite' | 'remove' | boolean")] + pub rewrite_import_extensions: Option>, +} + +impl From for oxc::transformer::TypeScriptOptions { + fn from(options: TypeScriptOptions) -> Self { + let ops = oxc::transformer::TypeScriptOptions::default(); + oxc::transformer::TypeScriptOptions { + jsx_pragma: options.jsx_pragma.map(Into::into).unwrap_or(ops.jsx_pragma), + jsx_pragma_frag: options.jsx_pragma_frag.map(Into::into).unwrap_or(ops.jsx_pragma_frag), + only_remove_type_imports: options + .only_remove_type_imports + .unwrap_or(ops.only_remove_type_imports), + allow_namespaces: options.allow_namespaces.unwrap_or(ops.allow_namespaces), + allow_declare_fields: options.allow_declare_fields.unwrap_or(ops.allow_declare_fields), + optimize_const_enums: false, + rewrite_import_extensions: options.rewrite_import_extensions.and_then(|value| { + match value { + Either::A(v) => { + if v { + Some(RewriteExtensionsMode::Rewrite) + } else { + None + } + } + Either::B(v) => match v.as_str() { + "rewrite" => Some(RewriteExtensionsMode::Rewrite), + "remove" => Some(RewriteExtensionsMode::Remove), + _ => None, + }, + } + }), + } + } +} + +/// Configure how TSX and JSX are transformed. +/// +/// @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx#options} +#[napi(object)] +pub struct JsxOptions { + /// Decides which runtime to use. + /// + /// - 'automatic' - auto-import the correct JSX factories + /// - 'classic' - no auto-import + /// + /// @default 'automatic' + #[napi(ts_type = "'classic' | 'automatic'")] + pub runtime: Option, + + /// Emit development-specific information, such as `__source` and `__self`. + /// + /// @default false + /// + /// @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx-development} + pub development: Option, + + /// Toggles whether or not to throw an error if an XML namespaced tag name + /// is used. + /// + /// Though the JSX spec allows this, it is disabled by default since React's + /// JSX does not currently have support for it. + /// + /// @default true + pub throw_if_namespace: Option, + + /// Enables `@babel/plugin-transform-react-pure-annotations`. + /// + /// It will mark top-level React method calls as pure for tree shaking. + /// + /// @see {@link https://babeljs.io/docs/en/babel-plugin-transform-react-pure-annotations} + /// + /// @default true + pub pure: Option, + + /// Replaces the import source when importing functions. + /// + /// @default 'react' + pub import_source: Option, + + /// Replace the function used when compiling JSX expressions. It should be a + /// qualified name (e.g. `React.createElement`) or an identifier (e.g. + /// `createElement`). + /// + /// Only used for `classic` {@link runtime}. + /// + /// @default 'React.createElement' + pub pragma: Option, + + /// Replace the component used when compiling JSX fragments. It should be a + /// valid JSX tag name. + /// + /// Only used for `classic` {@link runtime}. + /// + /// @default 'React.Fragment' + pub pragma_frag: Option, + + /// When spreading props, use `Object.assign` directly instead of an extend helper. + /// + /// Only used for `classic` {@link runtime}. + /// + /// @default false + pub use_built_ins: Option, + + /// When spreading props, use inline object with spread elements directly + /// instead of an extend helper or Object.assign. + /// + /// Only used for `classic` {@link runtime}. + /// + /// @default false + pub use_spread: Option, + + /// Enable React Fast Refresh . + /// + /// Conforms to the implementation in {@link https://github.com/facebook/react/tree/v18.3.1/packages/react-refresh} + /// + /// @default false + pub refresh: Option>, +} + +impl From for oxc::transformer::JsxOptions { + fn from(options: JsxOptions) -> Self { + let ops = oxc::transformer::JsxOptions::default(); + oxc::transformer::JsxOptions { + runtime: match options.runtime.as_deref() { + Some("classic") => JsxRuntime::Classic, + /* "automatic" */ _ => JsxRuntime::Automatic, + }, + development: options.development.unwrap_or(ops.development), + throw_if_namespace: options.throw_if_namespace.unwrap_or(ops.throw_if_namespace), + pure: options.pure.unwrap_or(ops.pure), + import_source: options.import_source, + pragma: options.pragma, + pragma_frag: options.pragma_frag, + use_built_ins: options.use_built_ins, + use_spread: options.use_spread, + refresh: options.refresh.and_then(|value| match value { + Either::A(b) => b.then(oxc::transformer::ReactRefreshOptions::default), + Either::B(options) => Some(oxc::transformer::ReactRefreshOptions::from(options)), + }), + ..Default::default() + } + } +} + +#[napi(object)] +pub struct ReactRefreshOptions { + /// Specify the identifier of the refresh registration variable. + /// + /// @default `$RefreshReg$`. + pub refresh_reg: Option, + + /// Specify the identifier of the refresh signature variable. + /// + /// @default `$RefreshSig$`. + pub refresh_sig: Option, + + pub emit_full_signatures: Option, +} + +impl From for oxc::transformer::ReactRefreshOptions { + fn from(options: ReactRefreshOptions) -> Self { + let ops = oxc::transformer::ReactRefreshOptions::default(); + oxc::transformer::ReactRefreshOptions { + refresh_reg: options.refresh_reg.unwrap_or(ops.refresh_reg), + refresh_sig: options.refresh_sig.unwrap_or(ops.refresh_sig), + emit_full_signatures: options.emit_full_signatures.unwrap_or(ops.emit_full_signatures), + } + } +} + +#[napi(object)] +pub struct ArrowFunctionsOptions { + /// This option enables the following: + /// * Wrap the generated function in .bind(this) and keeps uses of this inside the function as-is, instead of using a renamed this. + /// * Add a runtime check to ensure the functions are not instantiated. + /// * Add names to arrow functions. + /// + /// @default false + pub spec: Option, +} + +impl From for oxc::transformer::ArrowFunctionsOptions { + fn from(options: ArrowFunctionsOptions) -> Self { + oxc::transformer::ArrowFunctionsOptions { spec: options.spec.unwrap_or_default() } + } +} + +#[napi(object)] +pub struct Es2015Options { + /// Transform arrow functions into function expressions. + pub arrow_function: Option, +} + +impl From for oxc::transformer::ES2015Options { + fn from(options: Es2015Options) -> Self { + oxc::transformer::ES2015Options { arrow_function: options.arrow_function.map(Into::into) } + } +} #[derive(Default)] struct Compiler { @@ -116,7 +492,9 @@ impl CompilerInterface for Compiler { Some(&self.transform_options) } - fn isolated_declaration_options(&self) -> Option { + fn isolated_declaration_options( + &self, + ) -> Option { self.isolated_declaration_options } diff --git a/npm/oxc-parser/CHANGELOG.md b/npm/oxc-parser/CHANGELOG.md index f5491c12fa332b..0ca27fb69c22e3 100644 --- a/npm/oxc-parser/CHANGELOG.md +++ b/npm/oxc-parser/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +### Refactor + +- b4f3812 oxc_napi: Remove `source_map` - moved to its crate (#7614) (Boshen) + ## [0.35.0] - 2024-11-04 ### Bug Fixes diff --git a/npm/oxc-parser/package.json b/npm/oxc-parser/package.json index 2b605015f19efd..44dfdcd941ab26 100644 --- a/npm/oxc-parser/package.json +++ b/npm/oxc-parser/package.json @@ -1,6 +1,6 @@ { "name": "oxc-parser", - "version": "0.38.0", + "version": "0.39.0", "description": "Oxc Parser Node API", "keywords": [ "Parser" diff --git a/npm/oxc-transform/package.json b/npm/oxc-transform/package.json index 04d019473672a2..767741c1e47c75 100644 --- a/npm/oxc-transform/package.json +++ b/npm/oxc-transform/package.json @@ -1,6 +1,6 @@ { "name": "oxc-transform", - "version": "0.38.0", + "version": "0.39.0", "description": "Oxc transform Node API", "keywords": [ "transform" diff --git a/npm/oxc-types/CHANGELOG.md b/npm/oxc-types/CHANGELOG.md index 52e5bc30718b62..a8327203126218 100644 --- a/npm/oxc-types/CHANGELOG.md +++ b/npm/oxc-types/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) until v1.0.0. +## [0.39.0] - 2024-12-04 + +- b0e1c03 ast: [**BREAKING**] Add `StringLiteral::raw` field (#7393) (Boshen) + +### Features + + ## [0.37.0] - 2024-11-21 - f059b0e ast: [**BREAKING**] Add missing `ChainExpression` from `TSNonNullExpression` (#7377) (Boshen) diff --git a/npm/oxc-types/package.json b/npm/oxc-types/package.json index 66045a4a74cc7c..05fc7a85eafac4 100644 --- a/npm/oxc-types/package.json +++ b/npm/oxc-types/package.json @@ -1,6 +1,6 @@ { "name": "@oxc-project/types", - "version": "0.38.0", + "version": "0.39.0", "description": "Types for Oxc AST nodes", "keywords": [ "AST", diff --git a/package.json b/package.json index 006deb61eeccf8..2b896e0ea9d63e 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ }, "scripts": { "build": "pnpm --workspace-concurrency=1 --filter './napi/*' build", + "build-dev": "pnpm --workspace-concurrency=1 --filter './napi/*' build-dev", "test": "pnpm --workspace-concurrency=1 --filter './napi/*' test" }, "devDependencies": { diff --git a/tasks/coverage/misc/fail/oxc-7582.js b/tasks/coverage/misc/fail/oxc-7582.js new file mode 100644 index 00000000000000..86960dbcce2353 --- /dev/null +++ b/tasks/coverage/misc/fail/oxc-7582.js @@ -0,0 +1,6 @@ +class C { + x = 1; + method() { + obj.#x; + } +} diff --git a/tasks/coverage/snapshots/parser_babel.snap b/tasks/coverage/snapshots/parser_babel.snap index a528515941365c..5ec4f1590b5b06 100644 --- a/tasks/coverage/snapshots/parser_babel.snap +++ b/tasks/coverage/snapshots/parser_babel.snap @@ -3,7 +3,7 @@ commit: 54a8389f parser_babel Summary: AST Parsed : 2205/2218 (99.41%) Positive Passed: 2190/2218 (98.74%) -Negative Passed: 1510/1634 (92.41%) +Negative Passed: 1511/1634 (92.47%) Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/annex-b/enabled/3.1-sloppy-labeled-functions-if-body/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-fn-decl-labeled-inside-if/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-fn-decl-labeled-inside-loop/input.js @@ -28,7 +28,6 @@ Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-nested/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2018/object-rest-spread/no-pattern-in-rest/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2018/object-rest-spread/no-pattern-in-rest-with-ts/input.js -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-delete-optional-private-property/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2022/class-properties/super-inside-function/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2022/class-properties/yield-in-class-property-in-generator/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2022/class-static-block/invalid-decorators/input.js @@ -943,11 +942,10 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc · ────── ╰──── - × Expected `(` but found `await` + × `await` is only allowed within async functions and at the top levels of modules ╭─[babel/packages/babel-parser/test/fixtures/core/opts/allowAwaitOutsideFunction-false/input.js:1:5] 1 │ for await (const i of imports) {} - · ──┬── - · ╰── `(` expected + · ───── ╰──── × Unexpected new.target expression @@ -5371,12 +5369,11 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc · ╰── `)` expected ╰──── - × Expected `(` but found `await` + × `await` is only allowed within async functions and at the top levels of modules ╭─[babel/packages/babel-parser/test/fixtures/es2018/async-generators/for-await-async-context/input.js:2:7] 1 │ function f() { 2 │ for await (let x of y); - · ──┬── - · ╰── `(` expected + · ───── 3 │ } ╰──── @@ -7869,7 +7866,15 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc 4 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. + ╭─[babel/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-delete-optional-private-property/input.js:4:18] + 3 │ constructor() { + 4 │ delete this?.#x; + · ── + 5 │ } + ╰──── + + × The operand of a 'delete' operator cannot be a private identifier. ╭─[babel/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-delete-private-property/input.js:4:12] 3 │ constructor() { 4 │ delete this.#x; @@ -8310,11 +8315,10 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc 3 │ } ╰──── - × Expected `(` but found `await` + × `await` is only allowed within async functions and at the top levels of modules ╭─[babel/packages/babel-parser/test/fixtures/es2022/top-level-await-script/for-await/input.js:1:5] 1 │ for await (const a of b); - · ──┬── - · ╰── `(` expected + · ───── ╰──── × `await` is only allowed within async functions and at the top levels of modules diff --git a/tasks/coverage/snapshots/parser_misc.snap b/tasks/coverage/snapshots/parser_misc.snap index a6e8189eebb460..75662028a5ff23 100644 --- a/tasks/coverage/snapshots/parser_misc.snap +++ b/tasks/coverage/snapshots/parser_misc.snap @@ -1,7 +1,7 @@ parser_misc Summary: AST Parsed : 30/30 (100.00%) Positive Passed: 30/30 (100.00%) -Negative Passed: 25/25 (100.00%) +Negative Passed: 26/26 (100.00%) × Unexpected token ╭─[misc/fail/oxc-169.js:2:1] @@ -264,6 +264,14 @@ Negative Passed: 25/25 (100.00%) 3 │ } ╰──── + × Private field 'x' must be declared in an enclosing class + ╭─[misc/fail/oxc-7582.js:4:9] + 3 │ method() { + 4 │ obj.#x; + · ── + 5 │ } + ╰──── + × The keyword 'let' is reserved ╭─[misc/fail/oxc.js:3:1] 2 │ diff --git a/tasks/coverage/snapshots/parser_test262.snap b/tasks/coverage/snapshots/parser_test262.snap index 9eef882775167c..675bb81f26b6b1 100644 --- a/tasks/coverage/snapshots/parser_test262.snap +++ b/tasks/coverage/snapshots/parser_test262.snap @@ -11039,7 +11039,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 25 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-method-accessor-get.js:41:15] 40 │ g = this.f; 41 │ x = delete (g().#m); @@ -11055,7 +11055,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-method-accessor-set.js:41:15] 40 │ g = this.f; 41 │ x = delete (g().#m); @@ -11071,7 +11071,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-method-async-gen.js:41:15] 40 │ g = this.f; 41 │ x = delete (g().#m); @@ -11087,7 +11087,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-method-async.js:41:15] 40 │ g = this.f; 41 │ x = delete (g().#m); @@ -11103,7 +11103,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-method-gen.js:41:15] 40 │ g = this.f; 41 │ x = delete (g().#m); @@ -11119,7 +11119,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-method.js:41:15] 40 │ g = this.f; 41 │ x = delete (g().#m); @@ -11135,7 +11135,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-no-reference.js:41:15] 40 │ g = this.f; 41 │ x = delete (g().#m); @@ -11151,7 +11151,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-privatename.js:41:15] 40 │ g = this.f; 41 │ x = delete (g().#x); @@ -11159,7 +11159,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-method-accessor-get.js:41:15] 40 │ 41 │ x = delete (this.#m @@ -11175,7 +11175,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-method-accessor-set.js:41:15] 40 │ 41 │ x = delete (this.#m @@ -11191,7 +11191,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-method-async-gen.js:41:15] 40 │ 41 │ x = delete (this.#m @@ -11199,7 +11199,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-method-async.js:41:15] 40 │ 41 │ x = delete (this.#m @@ -11207,7 +11207,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-method-gen.js:41:15] 40 │ 41 │ x = delete (this.#m @@ -11215,7 +11215,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-method.js:41:15] 40 │ 41 │ x = delete (this.#m @@ -11231,7 +11231,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-no-reference.js:41:15] 40 │ 41 │ x = delete (this.#m); @@ -11247,7 +11247,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-privatename.js:41:15] 40 │ 41 │ x = delete (this.#x); @@ -11255,7 +11255,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-method-accessor-get.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -11271,7 +11271,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-method-accessor-set.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -11287,7 +11287,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-method-async-gen.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -11303,7 +11303,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-method-async.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -11319,7 +11319,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-method-gen.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -11335,7 +11335,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-method.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -11351,7 +11351,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-no-reference.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -11367,7 +11367,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-privatename.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#x; @@ -11375,7 +11375,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-method-accessor-get.js:35:14] 34 │ 35 │ x = delete this.#m @@ -11391,7 +11391,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-method-accessor-set.js:35:14] 34 │ 35 │ x = delete this.#m @@ -11407,7 +11407,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-method-async-gen.js:35:14] 34 │ 35 │ x = delete this.#m @@ -11415,7 +11415,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-method-async.js:35:14] 34 │ 35 │ x = delete this.#m @@ -11423,7 +11423,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-method-gen.js:35:14] 34 │ 35 │ x = delete this.#m @@ -11431,7 +11431,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-method.js:35:14] 34 │ 35 │ x = delete this.#m @@ -11447,7 +11447,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-no-reference.js:35:14] 34 │ 35 │ x = delete this.#m; @@ -11463,7 +11463,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-privatename.js:35:14] 34 │ 35 │ x = delete this.#x; @@ -11471,7 +11471,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-method-accessor-get.js:41:16] 40 │ g = this.f; 41 │ x = delete ((g().#m)); @@ -11487,7 +11487,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-method-accessor-set.js:41:16] 40 │ g = this.f; 41 │ x = delete ((g().#m)); @@ -11503,7 +11503,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-method-async-gen.js:41:16] 40 │ g = this.f; 41 │ x = delete ((g().#m)); @@ -11519,7 +11519,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-method-async.js:41:16] 40 │ g = this.f; 41 │ x = delete ((g().#m)); @@ -11535,7 +11535,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-method-gen.js:41:16] 40 │ g = this.f; 41 │ x = delete ((g().#m)); @@ -11551,7 +11551,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-method.js:41:16] 40 │ g = this.f; 41 │ x = delete ((g().#m)); @@ -11567,7 +11567,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-no-reference.js:41:16] 40 │ g = this.f; 41 │ x = delete ((g().#m)); @@ -11583,7 +11583,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-privatename.js:41:16] 40 │ g = this.f; 41 │ x = delete ((g().#x)); @@ -11591,7 +11591,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-method-accessor-get.js:41:16] 40 │ 41 │ x = delete ((this.#m @@ -11607,7 +11607,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-method-accessor-set.js:41:16] 40 │ 41 │ x = delete ((this.#m @@ -11623,7 +11623,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-method-async-gen.js:41:16] 40 │ 41 │ x = delete ((this.#m @@ -11631,7 +11631,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-method-async.js:41:16] 40 │ 41 │ x = delete ((this.#m @@ -11639,7 +11639,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-method-gen.js:41:16] 40 │ 41 │ x = delete ((this.#m @@ -11647,7 +11647,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-method.js:41:16] 40 │ 41 │ x = delete ((this.#m @@ -11663,7 +11663,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-no-reference.js:41:16] 40 │ 41 │ x = delete ((this.#m)); @@ -11679,7 +11679,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-privatename.js:41:16] 40 │ 41 │ x = delete ((this.#x)); @@ -11687,7 +11687,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 42 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-method-accessor-get.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -11703,7 +11703,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-method-accessor-set.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -11719,7 +11719,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-method-async-gen.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -11735,7 +11735,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-method-async.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -11751,7 +11751,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-method-gen.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -11767,7 +11767,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-method.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -11783,7 +11783,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-no-reference.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -11799,7 +11799,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-privatename.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#x); @@ -11807,7 +11807,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-method-accessor-get.js:43:13] 42 │ 43 │ delete (this.#m @@ -11823,7 +11823,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-method-accessor-set.js:43:13] 42 │ 43 │ delete (this.#m @@ -11839,7 +11839,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-method-async-gen.js:43:13] 42 │ 43 │ delete (this.#m @@ -11847,7 +11847,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-method-async.js:43:13] 42 │ 43 │ delete (this.#m @@ -11855,7 +11855,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-method-gen.js:43:13] 42 │ 43 │ delete (this.#m @@ -11863,7 +11863,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-method.js:43:13] 42 │ 43 │ delete (this.#m @@ -11879,7 +11879,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-no-reference.js:43:13] 42 │ 43 │ delete (this.#m); @@ -11895,7 +11895,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-privatename.js:43:13] 42 │ 43 │ delete (this.#x); @@ -11903,7 +11903,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-method-accessor-get.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -11919,7 +11919,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-method-accessor-set.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -11935,7 +11935,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-method-async-gen.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -11951,7 +11951,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-method-async.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -11967,7 +11967,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-method-gen.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -11983,7 +11983,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-method.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -11999,7 +11999,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-no-reference.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -12015,7 +12015,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-privatename.js:37:12] 36 │ var g = this.f; 37 │ delete g().#x; @@ -12023,7 +12023,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-method-accessor-get.js:37:12] 36 │ 37 │ delete this.#m @@ -12039,7 +12039,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-method-accessor-set.js:37:12] 36 │ 37 │ delete this.#m @@ -12055,7 +12055,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-method-async-gen.js:37:12] 36 │ 37 │ delete this.#m @@ -12063,7 +12063,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-method-async.js:37:12] 36 │ 37 │ delete this.#m @@ -12071,7 +12071,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-method-gen.js:37:12] 36 │ 37 │ delete this.#m @@ -12079,7 +12079,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-method.js:37:12] 36 │ 37 │ delete this.#m @@ -12095,7 +12095,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-no-reference.js:37:12] 36 │ 37 │ delete this.#m; @@ -12111,7 +12111,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-privatename.js:37:12] 36 │ 37 │ delete this.#x; @@ -12119,7 +12119,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-method-accessor-get.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -12135,7 +12135,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-method-accessor-set.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -12151,7 +12151,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-method-async-gen.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -12167,7 +12167,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-method-async.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -12183,7 +12183,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-method-gen.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -12199,7 +12199,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-method.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -12215,7 +12215,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-no-reference.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -12231,7 +12231,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-privatename.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#x)); @@ -12239,7 +12239,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-method-accessor-get.js:43:14] 42 │ 43 │ delete ((this.#m @@ -12255,7 +12255,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-method-accessor-set.js:43:14] 42 │ 43 │ delete ((this.#m @@ -12271,7 +12271,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-method-async-gen.js:43:14] 42 │ 43 │ delete ((this.#m @@ -12279,7 +12279,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-method-async.js:43:14] 42 │ 43 │ delete ((this.#m @@ -12287,7 +12287,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-method-gen.js:43:14] 42 │ 43 │ delete ((this.#m @@ -12295,7 +12295,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-method.js:43:14] 42 │ 43 │ delete ((this.#m @@ -12311,7 +12311,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-no-reference.js:43:14] 42 │ 43 │ delete ((this.#m)); @@ -12327,7 +12327,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-privatename.js:43:14] 42 │ 43 │ delete ((this.#x)); @@ -27885,7 +27885,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 25 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-method-accessor-get.js:38:15] 37 │ g = this.f; 38 │ x = delete (g().#m); @@ -27901,7 +27901,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-method-accessor-set.js:38:15] 37 │ g = this.f; 38 │ x = delete (g().#m); @@ -27917,7 +27917,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-method-async-gen.js:38:15] 37 │ g = this.f; 38 │ x = delete (g().#m); @@ -27933,7 +27933,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-method-async.js:38:15] 37 │ g = this.f; 38 │ x = delete (g().#m); @@ -27949,7 +27949,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-method-gen.js:38:15] 37 │ g = this.f; 38 │ x = delete (g().#m); @@ -27965,7 +27965,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-method.js:38:15] 37 │ g = this.f; 38 │ x = delete (g().#m); @@ -27981,7 +27981,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-private-no-reference.js:38:15] 37 │ g = this.f; 38 │ x = delete (g().#m); @@ -27997,7 +27997,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-call-expression-privatename.js:38:15] 37 │ g = this.f; 38 │ x = delete (g().#x); @@ -28005,7 +28005,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-method-accessor-get.js:38:15] 37 │ 38 │ x = delete (this.#m @@ -28021,7 +28021,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-method-accessor-set.js:38:15] 37 │ 38 │ x = delete (this.#m @@ -28037,7 +28037,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-method-async-gen.js:38:15] 37 │ 38 │ x = delete (this.#m @@ -28045,7 +28045,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-method-async.js:38:15] 37 │ 38 │ x = delete (this.#m @@ -28053,7 +28053,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-method-gen.js:38:15] 37 │ 38 │ x = delete (this.#m @@ -28061,7 +28061,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-method.js:38:15] 37 │ 38 │ x = delete (this.#m @@ -28077,7 +28077,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-private-no-reference.js:38:15] 37 │ 38 │ x = delete (this.#m); @@ -28093,7 +28093,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-covered-err-delete-member-expression-privatename.js:38:15] 37 │ 38 │ x = delete (this.#x); @@ -28101,7 +28101,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-method-accessor-get.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -28117,7 +28117,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-method-accessor-set.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -28133,7 +28133,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-method-async-gen.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -28149,7 +28149,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-method-async.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -28165,7 +28165,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-method-gen.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -28181,7 +28181,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-method.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -28197,7 +28197,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-private-no-reference.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#m; @@ -28213,7 +28213,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-call-expression-privatename.js:35:14] 34 │ g = this.f; 35 │ x = delete g().#x; @@ -28221,7 +28221,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ f() { ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-method-accessor-get.js:35:14] 34 │ 35 │ x = delete this.#m @@ -28237,7 +28237,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-method-accessor-set.js:35:14] 34 │ 35 │ x = delete this.#m @@ -28253,7 +28253,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-method-async-gen.js:35:14] 34 │ 35 │ x = delete this.#m @@ -28261,7 +28261,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-method-async.js:35:14] 34 │ 35 │ x = delete this.#m @@ -28269,7 +28269,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-method-gen.js:35:14] 34 │ 35 │ x = delete this.#m @@ -28277,7 +28277,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-method.js:35:14] 34 │ 35 │ x = delete this.#m @@ -28293,7 +28293,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-private-no-reference.js:35:14] 34 │ 35 │ x = delete this.#m; @@ -28309,7 +28309,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-err-delete-member-expression-privatename.js:35:14] 34 │ 35 │ x = delete this.#x; @@ -28317,7 +28317,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 36 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-method-accessor-get.js:38:16] 37 │ g = this.f; 38 │ x = delete ((g().#m)); @@ -28333,7 +28333,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-method-accessor-set.js:38:16] 37 │ g = this.f; 38 │ x = delete ((g().#m)); @@ -28349,7 +28349,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-method-async-gen.js:38:16] 37 │ g = this.f; 38 │ x = delete ((g().#m)); @@ -28365,7 +28365,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-method-async.js:38:16] 37 │ g = this.f; 38 │ x = delete ((g().#m)); @@ -28381,7 +28381,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-method-gen.js:38:16] 37 │ g = this.f; 38 │ x = delete ((g().#m)); @@ -28397,7 +28397,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-method.js:38:16] 37 │ g = this.f; 38 │ x = delete ((g().#m)); @@ -28413,7 +28413,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-private-no-reference.js:38:16] 37 │ g = this.f; 38 │ x = delete ((g().#m)); @@ -28429,7 +28429,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-call-expression-privatename.js:38:16] 37 │ g = this.f; 38 │ x = delete ((g().#x)); @@ -28437,7 +28437,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-method-accessor-get.js:38:16] 37 │ 38 │ x = delete ((this.#m @@ -28453,7 +28453,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-method-accessor-set.js:38:16] 37 │ 38 │ x = delete ((this.#m @@ -28469,7 +28469,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-method-async-gen.js:38:16] 37 │ 38 │ x = delete ((this.#m @@ -28477,7 +28477,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-method-async.js:38:16] 37 │ 38 │ x = delete ((this.#m @@ -28485,7 +28485,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-method-gen.js:38:16] 37 │ 38 │ x = delete ((this.#m @@ -28493,7 +28493,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-method.js:38:16] 37 │ 38 │ x = delete ((this.#m @@ -28509,7 +28509,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-private-no-reference.js:38:16] 37 │ 38 │ x = delete ((this.#m)); @@ -28525,7 +28525,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/field-delete-twice-covered-err-delete-member-expression-privatename.js:38:16] 37 │ 38 │ x = delete ((this.#x)); @@ -28533,7 +28533,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 39 │ ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-method-accessor-get.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -28549,7 +28549,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-method-accessor-set.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -28565,7 +28565,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-method-async-gen.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -28581,7 +28581,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-method-async.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -28597,7 +28597,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-method-gen.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -28613,7 +28613,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-method.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -28629,7 +28629,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-private-no-reference.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#m); @@ -28645,7 +28645,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-call-expression-privatename.js:43:13] 42 │ var g = this.f; 43 │ delete (g().#x); @@ -28653,7 +28653,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-method-accessor-get.js:43:13] 42 │ 43 │ delete (this.#m @@ -28669,7 +28669,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-method-accessor-set.js:43:13] 42 │ 43 │ delete (this.#m @@ -28685,7 +28685,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-method-async-gen.js:43:13] 42 │ 43 │ delete (this.#m @@ -28693,7 +28693,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-method-async.js:43:13] 42 │ 43 │ delete (this.#m @@ -28701,7 +28701,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-method-gen.js:43:13] 42 │ 43 │ delete (this.#m @@ -28709,7 +28709,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-method.js:43:13] 42 │ 43 │ delete (this.#m @@ -28725,7 +28725,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ ); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-private-no-reference.js:43:13] 42 │ 43 │ delete (this.#m); @@ -28741,7 +28741,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-covered-err-delete-member-expression-privatename.js:43:13] 42 │ 43 │ delete (this.#x); @@ -28749,7 +28749,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-method-accessor-get.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -28765,7 +28765,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-method-accessor-set.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -28781,7 +28781,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-method-async-gen.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -28797,7 +28797,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-method-async.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -28813,7 +28813,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-method-gen.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -28829,7 +28829,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-method.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -28845,7 +28845,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-private-no-reference.js:37:12] 36 │ var g = this.f; 37 │ delete g().#m; @@ -28861,7 +28861,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-call-expression-privatename.js:37:12] 36 │ var g = this.f; 37 │ delete g().#x; @@ -28869,7 +28869,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-method-accessor-get.js:37:12] 36 │ 37 │ delete this.#m @@ -28885,7 +28885,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-method-accessor-set.js:37:12] 36 │ 37 │ delete this.#m @@ -28901,7 +28901,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-method-async-gen.js:37:12] 36 │ 37 │ delete this.#m @@ -28909,7 +28909,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-method-async.js:37:12] 36 │ 37 │ delete this.#m @@ -28917,7 +28917,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-method-gen.js:37:12] 36 │ 37 │ delete this.#m @@ -28925,7 +28925,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-method.js:37:12] 36 │ 37 │ delete this.#m @@ -28941,7 +28941,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ ; ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-private-no-reference.js:37:12] 36 │ 37 │ delete this.#m; @@ -28957,7 +28957,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-err-delete-member-expression-privatename.js:37:12] 36 │ 37 │ delete this.#x; @@ -28965,7 +28965,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 38 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-method-accessor-get.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -28981,7 +28981,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-method-accessor-set.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -28997,7 +28997,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-method-async-gen.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -29013,7 +29013,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-method-async.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -29029,7 +29029,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-method-gen.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -29045,7 +29045,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-method.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -29061,7 +29061,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-private-no-reference.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#m)); @@ -29077,7 +29077,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-call-expression-privatename.js:43:14] 42 │ var g = this.f; 43 │ delete ((g().#x)); @@ -29085,7 +29085,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-method-accessor-get.js:43:14] 42 │ 43 │ delete ((this.#m @@ -29101,7 +29101,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-method-accessor-set.js:43:14] 42 │ 43 │ delete ((this.#m @@ -29117,7 +29117,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-method-async-gen.js:43:14] 42 │ 43 │ delete ((this.#m @@ -29125,7 +29125,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-method-async.js:43:14] 42 │ 43 │ delete ((this.#m @@ -29133,7 +29133,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-method-gen.js:43:14] 42 │ 43 │ delete ((this.#m @@ -29141,7 +29141,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-method.js:43:14] 42 │ 43 │ delete ((this.#m @@ -29157,7 +29157,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ )); ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-private-no-reference.js:43:14] 42 │ 43 │ delete ((this.#m)); @@ -29173,7 +29173,7 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut 44 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[test262/test/language/statements/class/elements/syntax/early-errors/delete/method-delete-twice-covered-err-delete-member-expression-privatename.js:43:14] 42 │ 43 │ delete ((this.#x)); diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index 42d7ab15706ac2..130019b60c928d 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -1,7 +1,7 @@ commit: d85767ab parser_typescript Summary: -AST Parsed : 6494/6503 (99.86%) +AST Parsed : 6495/6503 (99.88%) Positive Passed: 6483/6503 (99.69%) Negative Passed: 1239/5747 (21.56%) Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration10.ts @@ -4514,14 +4514,92 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/typings/t Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/typings/typingsSuggestionBun2.ts Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/arrayFromAsync.ts - × Expected `(` but found `await` + × `await` is only allowed within async functions and at the top levels of modules ╭─[typescript/tests/cases/compiler/arrayFromAsync.ts:22:5] 21 │ const arr : number[] = []; 22 │ for await (const v of asyncGen(4)) { - · ──┬── - · ╰── `(` expected + · ───── 23 │ arr.push(v); ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/arrayFromAsync.ts:26:18] + 25 │ + 26 │ const sameArr1 = await Array.fromAsync(arrLike); + · ───── + 27 │ const sameArr2 = await Array.fromAsync([Promise.resolve(0), Promise.resolve(2), Promise.resolve(4), Promise.resolve(6)]); + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/arrayFromAsync.ts:27:18] + 26 │ const sameArr1 = await Array.fromAsync(arrLike); + 27 │ const sameArr2 = await Array.fromAsync([Promise.resolve(0), Promise.resolve(2), Promise.resolve(4), Promise.resolve(6)]); + · ───── + 28 │ const sameArr3 = await Array.fromAsync(genPromises(4)); + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/arrayFromAsync.ts:28:18] + 27 │ const sameArr2 = await Array.fromAsync([Promise.resolve(0), Promise.resolve(2), Promise.resolve(4), Promise.resolve(6)]); + 28 │ const sameArr3 = await Array.fromAsync(genPromises(4)); + · ───── + 29 │ const sameArr4 = await Array.fromAsync(asyncGen(4)); + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/arrayFromAsync.ts:29:18] + 28 │ const sameArr3 = await Array.fromAsync(genPromises(4)); + 29 │ const sameArr4 = await Array.fromAsync(asyncGen(4)); + · ───── + 30 │ + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/arrayFromAsync.ts:33:18] + 32 │ Data.fromAsync = Array.fromAsync; + 33 │ const sameArr5 = await Data.fromAsync(asyncGen(4)); + · ───── + 34 │ + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/arrayFromAsync.ts:35:17] + 34 │ + 35 │ const mapArr1 = await Array.fromAsync(asyncGen(4), v => v ** 2); + · ───── + 36 │ const mapArr2 = await Array.fromAsync([0,2,4,6], v => Promise.resolve(v ** 2)); + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/arrayFromAsync.ts:36:17] + 35 │ const mapArr1 = await Array.fromAsync(asyncGen(4), v => v ** 2); + 36 │ const mapArr2 = await Array.fromAsync([0,2,4,6], v => Promise.resolve(v ** 2)); + · ───── + 37 │ const mapArr3 = await Array.fromAsync([0,2,4,6], v => v ** 2); + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/arrayFromAsync.ts:37:17] + 36 │ const mapArr2 = await Array.fromAsync([0,2,4,6], v => Promise.resolve(v ** 2)); + 37 │ const mapArr3 = await Array.fromAsync([0,2,4,6], v => v ** 2); + · ───── + 38 │ + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/arrayFromAsync.ts:42:18] + 41 │ // This returns a promise that will reject with `err`. + 42 │ const badArray = await Array.fromAsync(badIterable); + · ───── + 43 │ + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/arrayFromAsync.ts:44:25] + 43 │ + 44 │ const withIndexResult = await Array.fromAsync(["a", "b"], (str, index) => ({ index, str })); + · ───── + ╰──── Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/bom-utf16be.ts × Invalid Character `￾` @@ -5514,15 +5592,133 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private ╰──── help: Try insert a semicolon here - × Expected `(` but found `await` + × `await` is only allowed within async functions and at the top levels of modules ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:4:7] 3 │ function normalFunc(p: Promise) { 4 │ for await (const _ of []); - · ──┬── - · ╰── `(` expected + · ───── 5 │ return await p; ╰──── + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:5:10] + 4 │ for await (const _ of []); + 5 │ return await p; + · ───── + 6 │ } + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:9:7] + 8 │ export function exportedFunc(p: Promise) { + 9 │ for await (const _ of []); + · ───── + 10 │ return await p; + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:10:10] + 9 │ for await (const _ of []); + 10 │ return await p; + · ───── + 11 │ } + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:14:7] + 13 │ const functionExpression = function(p: Promise) { + 14 │ for await (const _ of []); + · ───── + 15 │ await p; + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:15:3] + 14 │ for await (const _ of []); + 15 │ await p; + · ───── + 16 │ } + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:19:7] + 18 │ const arrowFunc = (p: Promise) => { + 19 │ for await (const _ of []); + · ───── + 20 │ return await p; + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:20:10] + 19 │ for await (const _ of []); + 20 │ return await p; + · ───── + 21 │ }; + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:24:7] + 23 │ function* generatorFunc(p: Promise) { + 24 │ for await (const _ of []); + · ───── + 25 │ yield await p; + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:25:9] + 24 │ for await (const _ of []); + 25 │ yield await p; + · ───── + 26 │ } + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:30:9] + 29 │ constructor(p: Promise) { + 30 │ for await (const _ of []); + · ───── + 31 │ await p; + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:31:5] + 30 │ for await (const _ of []); + 31 │ await p; + · ───── + 32 │ } + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:34:7] + 33 │ method(p: Promise) { + 34 │ for await (const _ of []); + · ───── + 35 │ await p; + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:35:5] + 34 │ for await (const _ of []); + 35 │ await p; + · ───── + 36 │ } + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:39:5] + 38 │ + 39 │ for await (const _ of []); + · ───── + 40 │ await null; + ╰──── + + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/awaitInNonAsyncFunction.ts:40:1] + 39 │ for await (const _ of []); + 40 │ await null; + · ───── + ╰──── + × `await` is only allowed within async functions and at the top levels of modules ╭─[typescript/tests/cases/compiler/awaitLiteralValues.ts:2:5] 1 │ function awaitString() { @@ -9387,14 +9583,21 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private ╰──── help: Try insert a semicolon here - × Expected `(` but found `await` + × `await` is only allowed within async functions and at the top levels of modules ╭─[typescript/tests/cases/compiler/modulePreserveTopLevelAwait1.ts:1:5] 1 │ for await (const x of []) {} - · ──┬── - · ╰── `(` expected + · ───── 2 │ await Promise.resolve(); ╰──── + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/compiler/modulePreserveTopLevelAwait1.ts:2:1] + 1 │ for await (const x of []) {} + 2 │ await Promise.resolve(); + · ───── + 3 │ + ╰──── + × Expected a semicolon or an implicit semicolon after a statement, but found none ╭─[typescript/tests/cases/compiler/moduleProperty1.ts:9:12] 8 │ var x = 10; // variable local to this module body @@ -14904,7 +15107,7 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 11 │ } ╰──── - × Private fields can not be deleted + × The operand of a 'delete' operator cannot be a private identifier. ╭─[typescript/tests/cases/conformance/classes/members/privateNames/privateNamesNoDelete.ts:4:16] 3 │ constructor() { 4 │ delete this.#v; // Error: The operand of a delete operator cannot be a private name. @@ -19129,12 +19332,11 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 2 │ ╰──── - × Expected `(` but found `await` + × `await` is only allowed within async functions and at the top levels of modules ╭─[typescript/tests/cases/conformance/externalModules/topLevelAwaitNonModule.ts:5:5] 4 │ 5 │ for await (const item of arr) { - · ──┬── - · ╰── `(` expected + · ───── 6 │ item; ╰──── @@ -22808,12 +23010,20 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 3 │ } ╰──── - × Expected `(` but found `await` + × `await` is only allowed within async functions and at the top levels of modules ╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarationsInForAwaitOf.3.ts:5:5] 4 │ 5 │ for await (await using of x); - · ──┬── - · ╰── `(` expected + · ───── + 6 │ + ╰──── + + × Expected `;` but found `Identifier` + ╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarationsInForAwaitOf.3.ts:5:27] + 4 │ + 5 │ for await (await using of x); + · ┬ + · ╰── `;` expected 6 │ ╰──── diff --git a/tasks/coverage/snapshots/semantic_typescript.snap b/tasks/coverage/snapshots/semantic_typescript.snap index a92c966d0f7d62..c5a21e11ed0677 100644 --- a/tasks/coverage/snapshots/semantic_typescript.snap +++ b/tasks/coverage/snapshots/semantic_typescript.snap @@ -987,7 +987,17 @@ after transform: ScopeId(1): ["T", "arr", "depth"] rebuilt : ScopeId(1): ["arr", "depth"] tasks/coverage/typescript/tests/cases/compiler/arrayFromAsync.ts -semantic error: Expected `(` but found `await` +semantic error: `await` is only allowed within async functions and at the top levels of modules +`await` is only allowed within async functions and at the top levels of modules +`await` is only allowed within async functions and at the top levels of modules +`await` is only allowed within async functions and at the top levels of modules +`await` is only allowed within async functions and at the top levels of modules +`await` is only allowed within async functions and at the top levels of modules +`await` is only allowed within async functions and at the top levels of modules +`await` is only allowed within async functions and at the top levels of modules +`await` is only allowed within async functions and at the top levels of modules +`await` is only allowed within async functions and at the top levels of modules +`await` is only allowed within async functions and at the top levels of modules tasks/coverage/typescript/tests/cases/compiler/arrayLiteralContextualType.ts semantic error: Bindings mismatch: diff --git a/tasks/transform_conformance/snapshots/babel.snap.md b/tasks/transform_conformance/snapshots/babel.snap.md index 9b9ba0420090d7..b181f62632ac9a 100644 --- a/tasks/transform_conformance/snapshots/babel.snap.md +++ b/tasks/transform_conformance/snapshots/babel.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 415/846 +Passed: 416/846 # All Passed: * babel-plugin-transform-class-static-block @@ -276,7 +276,7 @@ x Output mismatch x Output mismatch -# babel-plugin-transform-class-properties (88/264) +# babel-plugin-transform-class-properties (89/264) * assumption-constantSuper/complex-super-class/input.js x Output mismatch @@ -503,9 +503,6 @@ x Output mismatch * private/optional-chain-cast-to-boolean/input.js x Output mismatch -* private/optional-chain-delete-property/input.js -x Output mismatch - * private/optional-chain-delete-property-with-transform/input.js x Output mismatch @@ -537,7 +534,18 @@ x Output mismatch x Output mismatch * private/parenthesized-optional-member-call/input.js -x Output mismatch +Scope children mismatch: +after transform: ScopeId(0): [ScopeId(1)] +rebuilt : ScopeId(0): [ScopeId(1), ScopeId(5)] +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2), ScopeId(3), ScopeId(4)] +rebuilt : ScopeId(1): [ScopeId(2), ScopeId(3)] +Scope flags mismatch: +after transform: ScopeId(2): ScopeFlags(StrictMode | Function) +rebuilt : ScopeId(5): ScopeFlags(Function) +Scope parent mismatch: +after transform: ScopeId(2): Some(ScopeId(1)) +rebuilt : ScopeId(5): Some(ScopeId(0)) * private/parenthesized-optional-member-call-with-transform/input.js x Output mismatch diff --git a/tasks/transform_conformance/snapshots/babel_exec.snap.md b/tasks/transform_conformance/snapshots/babel_exec.snap.md index 2b0708d87ec742..b989551d489299 100644 --- a/tasks/transform_conformance/snapshots/babel_exec.snap.md +++ b/tasks/transform_conformance/snapshots/babel_exec.snap.md @@ -1,7 +1,7 @@ commit: 54a8389f node: v22.11.0 -⎯⎯⎯⎯⎯⎯ Failed Suites 38 ⎯⎯⎯⎯⎯⎯ +⎯⎯⎯⎯⎯⎯ Failed Suites 32 ⎯⎯⎯⎯⎯⎯ FAIL fixtures/babel/babel-plugin-transform-arrow-functions-test-fixtures-arrow-functions-implicit-var-arguments-exec.test.js [ fixtures/babel/babel-plugin-transform-arrow-functions-test-fixtures-arrow-functions-implicit-var-arguments-exec.test.js ] Error: 'eval' and 'arguments' cannot be used as a binding identifier in strict mode @@ -11,11 +11,11 @@ Error: 'eval' and 'arguments' cannot be used as a binding identifier in strict m ❯ ssrTransformScript ../../node_modules/.pnpm/vite@5.4.11_@types+node@22.9.1/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:52381:11 ❯ loadAndTransform ../../node_modules/.pnpm/vite@5.4.11_@types+node@22.9.1/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:51979:72 -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-noUninitializedPrivateFieldAccess-static-private-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-noUninitializedPrivateFieldAccess-static-private-exec.test.js ] SyntaxError: Private field '#x' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-super-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-super-exec.test.js ] FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-delete-super-property-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-delete-super-property-exec.test.js ] @@ -28,7 +28,7 @@ Error: Invalid access to super ❯ ssrTransformScript ../../node_modules/.pnpm/vite@5.4.11_@types+node@22.9.1/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:52381:11 ❯ loadAndTransform ../../node_modules/.pnpm/vite@5.4.11_@types+node@22.9.1/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:51979:72 -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-nested-class-super-property-in-accessor-key-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-nested-class-super-property-in-accessor-key-exec.test.js ] Error: Unexpected token `[`. Expected * for generator, private key, identifier or async @@ -38,163 +38,109 @@ Error: Unexpected token `[`. Expected * for generator, private key, identifier o ❯ ssrTransformScript ../../node_modules/.pnpm/vite@5.4.11_@types+node@22.9.1/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:52381:11 ❯ loadAndTransform ../../node_modules/.pnpm/vite@5.4.11_@types+node@22.9.1/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:51979:72 -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-access-before-declaration-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-access-before-declaration-exec.test.js ] SyntaxError: Private field '#p' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-1-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-1-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-2-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-2-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-3-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-3-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[8/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[8/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[9/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[9/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-static-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-static-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[10/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[10/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-1-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-1-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[11/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[11/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-2-exec-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-2-exec-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[12/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[12/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-3-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-3-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[13/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[13/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[14/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[14/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-static-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-static-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[15/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[15/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-access-before-declaration-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-access-before-declaration-exec.test.js ] SyntaxError: Private field '#p' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[16/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[16/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-1-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-1-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[17/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[17/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-2-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-2-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[18/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[18/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-3-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-3-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[19/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[19/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[20/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[20/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-static-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-static-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[21/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[21/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-1-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-1-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[22/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[22/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-2-exec-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-2-exec-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[23/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[23/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-3-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-3-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[24/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[24/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[25/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[25/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-static-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-static-exec.test.js ] SyntaxError: Private field '#client' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[26/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[26/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-nested-class-computed-redeclared-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-nested-class-computed-redeclared-exec.test.js ] SyntaxError: Private field '#foo' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[27/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-property-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-property-exec.test.js ] -SyntaxError: Private field '#x' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[28/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-property-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-property-exec.test.js ] -SyntaxError: Private field '#x' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[29/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-parenthesized-optional-member-call-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-parenthesized-optional-member-call-exec.test.js ] -SyntaxError: Private field '#m' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[30/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[27/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-nested-class-computed-redeclared-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-nested-class-computed-redeclared-exec.test.js ] SyntaxError: Private field '#foo' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[31/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-property-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-property-exec.test.js ] -SyntaxError: Private field '#x' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[32/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-property-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-property-exec.test.js ] -SyntaxError: Private field '#x' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[33/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-parenthesized-optional-member-call-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-parenthesized-optional-member-call-exec.test.js ] -SyntaxError: Private field '#m' must be declared in an enclosing class -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[34/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[28/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-regression-7371-exec.test.js [ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-regression-7371-exec.test.js ] SyntaxError: 'super' keyword unexpected here -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[35/84]⎯ - -⎯⎯⎯⎯⎯⎯ Failed Tests 46 ⎯⎯⎯⎯⎯⎯⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-before-member-call-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Object._ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-before-member-call-exec.test.js:112:10 - 110| var _x = { _: 1 }; - 111| var _m = { _: function() { - 112| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 113| } }; - 114| var _self = { _: _Foo }; - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-before-member-call-exec.test.js:21:46 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-before-member-call-exec.test.js:116:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[36/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-cast-to-boolean-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.testIf fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-cast-to-boolean-exec.test.js:7:8 - 5| class C { - 6| static testIf(o) { - 7| if (_assertClassBrand(C, o, _a)._.b.c.d) { - | ^ - 8| return true; - 9| } - ❯ Function.testNullish fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-cast-to-boolean-exec.test.js:89:14 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-cast-to-boolean-exec.test.js:105:4 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[37/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[29/50]⎯ + +⎯⎯⎯⎯⎯⎯ Failed Tests 18 ⎯⎯⎯⎯⎯⎯⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-infer-name-exec.test.js > exec AssertionError: expected '_Class' to be 'Foo' // Object.is equality @@ -209,7 +155,7 @@ Received: "_Class" | ^ 9| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[38/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[30/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-nested-class-super-call-in-decorator-exec.test.js > exec AssertionError: expected undefined to be 'hello' // Object.is equality @@ -227,7 +173,7 @@ undefined | ^ 22| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[39/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[31/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-nested-class-super-property-in-decorator-exec.test.js > exec AssertionError: expected undefined to be 'hello' // Object.is equality @@ -245,7 +191,7 @@ undefined | ^ 23| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[40/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[32/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-constructor-collision-exec.test.js > exec AssertionError: expected undefined to be 'bar' // Object.is equality @@ -264,7 +210,7 @@ undefined 19| expect("bar" in f).toBe(false); 20| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[41/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[33/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-constructor-collision-exec.test.js > exec AssertionError: expected undefined to be 'bar' // Object.is equality @@ -283,7 +229,7 @@ undefined 19| expect("bar" in f).toBe(false); 20| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[42/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[34/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-nested-class-extends-computed-exec.test.js > exec AssertionError: expected [Function] to not throw an error but 'TypeError: Private element is not pre…' was thrown @@ -301,173 +247,7 @@ undefined | ^ 31| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[43/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-member-call-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Object._ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-member-call-exec.test.js:111:10 - 109| var _x = { _: 1 }; - 110| var _m = { _: function() { - 111| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 112| } }; - 113| var _self = { _: _Foo }; - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-member-call-exec.test.js:20:46 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-member-call-exec.test.js:115:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[44/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-member-call-with-transform-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-member-call-with-transform-exec.test.js:32:168 - - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-member-call-with-transform-exec.test.js:115:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[45/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-property-with-transform-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-property-with-transform-exec.test.js:32:166 - - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-property-with-transform-exec.test.js:112:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[46/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-cast-to-boolean-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.testIf fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-cast-to-boolean-exec.test.js:7:8 - 5| class C { - 6| static testIf(o) { - 7| if (_assertClassBrand(C, o, _a)._.b.c.d) { - | ^ - 8| return true; - 9| } - ❯ Function.testNullish fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-cast-to-boolean-exec.test.js:89:14 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-cast-to-boolean-exec.test.js:105:4 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[47/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-delete-property-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.testNull fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-delete-property-exec.test.js:57:18 - 55| return deep; - 56| } - 57| expect(delete _assertClassBrand(Foo, deep?.very.o?.Foo, _self)._.un… - | ^ - 58| expect(delete _assertClassBrand(Foo, o?.Foo, _self)._.unicorn).toBe… - 59| expect(delete _assertClassBrand(Foo, o?.Foo, _self)._.self.unicorn)… - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-delete-property-exec.test.js:94:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[48/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-delete-property-with-transform-exec.test.js > exec -AssertionError: expected function to throw an error, but it didn't - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-delete-property-with-transform-exec.test.js:42:7 - - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-delete-property-with-transform-exec.test.js:160:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[49/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Object._ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-exec.test.js:44:10 - 42| var _x = { _: 1 }; - 43| var _m = { _: function() { - 44| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 45| } }; - 46| var _self = { _: _Foo }; - ❯ f fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-exec.test.js:19:57 - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-exec.test.js:34:11 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-exec.test.js:48:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[50/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ _ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js:59:10 - 57| var _x = { _: 1 }; - 58| var _m = { _: function() { - 59| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 60| } }; - 61| var _self = { _: _Foo }; - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js:45:181 - ❯ j fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js:46:6 - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js:53:11 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js:63:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[51/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Object._ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-exec.test.js:114:10 - 112| var _x = { _: 1 }; - 113| var _m = { _: function() { - 114| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 115| } }; - 116| var _self = { _: _Foo }; - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-exec.test.js:20:17 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-exec.test.js:118:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[52/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-with-transform-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Object._ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-with-transform-exec.test.js:114:10 - 112| var _x = { _: 1 }; - 113| var _m = { _: function() { - 114| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 115| } }; - 116| var _self = { _: _Foo }; - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-with-transform-exec.test.js:23:142 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-with-transform-exec.test.js:118:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[53/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-member-call-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Object._ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-member-call-exec.test.js:114:10 - 112| var _x = { _: 1 }; - 113| var _m = { _: function() { - 114| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 115| } }; - 116| var _self = { _: _Foo }; - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-member-call-exec.test.js:20:14 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-member-call-exec.test.js:118:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[54/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-member-call-with-transform-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-member-call-with-transform-exec.test.js:35:269 - - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-member-call-with-transform-exec.test.js:118:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[55/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-property-with-transform-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-property-with-transform-exec.test.js:35:269 - - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-property-with-transform-exec.test.js:115:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[56/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[35/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-shadow-exec.test.js > exec TypeError: e.has is not a function @@ -482,7 +262,7 @@ TypeError: e.has is not a function ❯ Function.method fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-shadow-exec.test.js:12:11 ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-shadow-exec.test.js:16:14 -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[57/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[36/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-nested-class-extends-computed-exec.test.js > exec AssertionError: expected [Function] to not throw an error but 'TypeError: Private element is not pre…' was thrown @@ -500,173 +280,7 @@ undefined | ^ 32| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[58/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-member-call-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Object._ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-member-call-exec.test.js:112:10 - 110| var _x = { _: 1 }; - 111| var _m = { _: function() { - 112| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 113| } }; - 114| var _self = { _: _Foo }; - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-member-call-exec.test.js:21:46 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-member-call-exec.test.js:116:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[59/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-member-call-with-transform-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-member-call-with-transform-exec.test.js:33:168 - - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-member-call-with-transform-exec.test.js:116:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[60/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-property-with-transform-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-property-with-transform-exec.test.js:33:166 - - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-property-with-transform-exec.test.js:113:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[61/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-cast-to-boolean-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.testIf fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-cast-to-boolean-exec.test.js:7:8 - 5| class C { - 6| static testIf(o) { - 7| if (_assertClassBrand(C, o, _a)._.b.c.d) { - | ^ - 8| return true; - 9| } - ❯ Function.testNullish fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-cast-to-boolean-exec.test.js:89:14 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-cast-to-boolean-exec.test.js:105:4 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[62/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-delete-property-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.testNull fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-delete-property-exec.test.js:58:18 - 56| return deep; - 57| } - 58| expect(delete _assertClassBrand(Foo, deep?.very.o?.Foo, _self)._.un… - | ^ - 59| expect(delete _assertClassBrand(Foo, o?.Foo, _self)._.unicorn).toBe… - 60| expect(delete _assertClassBrand(Foo, o?.Foo, _self)._.self.unicorn)… - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-delete-property-exec.test.js:95:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[63/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-delete-property-with-transform-exec.test.js > exec -AssertionError: expected function to throw an error, but it didn't - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-delete-property-with-transform-exec.test.js:42:7 - - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-delete-property-with-transform-exec.test.js:160:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[64/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Object._ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-exec.test.js:45:10 - 43| var _x = { _: 1 }; - 44| var _m = { _: function() { - 45| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 46| } }; - 47| var _self = { _: _Foo }; - ❯ f fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-exec.test.js:20:57 - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-exec.test.js:35:11 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-exec.test.js:49:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[65/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-with-transform-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ _ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-with-transform-exec.test.js:60:10 - 58| var _x = { _: 1 }; - 59| var _m = { _: function() { - 60| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 61| } }; - 62| var _self = { _: _Foo }; - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-with-transform-exec.test.js:46:181 - ❯ j fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-with-transform-exec.test.js:47:6 - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-with-transform-exec.test.js:54:11 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-with-transform-exec.test.js:64:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[66/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-member-optional-call-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Object._ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-member-optional-call-exec.test.js:115:10 - 113| var _x = { _: 1 }; - 114| var _m = { _: function() { - 115| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 116| } }; - 117| var _self = { _: _Foo }; - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-member-optional-call-exec.test.js:21:17 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-member-optional-call-exec.test.js:119:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[67/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-member-optional-call-with-transform-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Object._ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-member-optional-call-with-transform-exec.test.js:115:10 - 113| var _x = { _: 1 }; - 114| var _m = { _: function() { - 115| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 116| } }; - 117| var _self = { _: _Foo }; - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-member-optional-call-with-transform-exec.test.js:24:142 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-member-optional-call-with-transform-exec.test.js:119:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[68/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-member-call-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Object._ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-member-call-exec.test.js:115:10 - 113| var _x = { _: 1 }; - 114| var _m = { _: function() { - 115| return _assertClassBrand(_Foo, this, _x)._; - | ^ - 116| } }; - 117| var _self = { _: _Foo }; - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-member-call-exec.test.js:21:14 - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-member-call-exec.test.js:119:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[69/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-member-call-with-transform-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-member-call-with-transform-exec.test.js:36:269 - - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-member-call-with-transform-exec.test.js:119:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[70/84]⎯ - - FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-property-with-transform-exec.test.js > exec -TypeError: Private element is not present on this object - ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 - ❯ Function.test fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-property-with-transform-exec.test.js:36:269 - - ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-property-with-transform-exec.test.js:116:6 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[71/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[37/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-static-shadow-exec.test.js > exec TypeError: e.has is not a function @@ -681,7 +295,7 @@ TypeError: e.has is not a function ❯ Function.method fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-static-shadow-exec.test.js:12:11 ❯ fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-static-shadow-exec.test.js:16:14 -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[72/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[38/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-computed-toPrimitive-exec.test.js > exec AssertionError: expected [Function] to throw error including '@@toPrimitive must return a primitive…' but got 'Cannot convert object to primitive va…' @@ -697,7 +311,7 @@ Received: "Cannot convert object to primitive value" 38| expect(() => class { 39| static get [arrayLike]() { -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[73/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[39/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-loose-static-infer-name-exec.test.js > exec AssertionError: expected '_Class' to be 'Foo' // Object.is equality @@ -712,7 +326,7 @@ Received: "_Class" | ^ 9| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[74/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[40/50]⎯ FAIL fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-static-infer-name-exec.test.js > exec AssertionError: expected '_Class' to be 'Foo' // Object.is equality @@ -727,7 +341,7 @@ Received: "_Class" | ^ 10| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[75/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[41/50]⎯ FAIL fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-assumption-noDocumentAll-parenthesized-expression-member-call-exec.test.js > exec TypeError: Cannot read properties of undefined (reading 'x') @@ -741,7 +355,7 @@ TypeError: Cannot read properties of undefined (reading 'x') ❯ Foo.test fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-assumption-noDocumentAll-parenthesized-expression-member-call-exec.test.js:25:63 ❯ fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-assumption-noDocumentAll-parenthesized-expression-member-call-exec.test.js:68:12 -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[76/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[42/50]⎯ FAIL fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-exec.test.js > exec TypeError: Cannot read properties of undefined (reading 'x') @@ -755,7 +369,7 @@ TypeError: Cannot read properties of undefined (reading 'x') ❯ Foo.test fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-exec.test.js:25:63 ❯ fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-exec.test.js:68:12 -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[77/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[43/50]⎯ FAIL fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-loose-exec.test.js > exec TypeError: Cannot read properties of undefined (reading 'x') @@ -769,7 +383,7 @@ TypeError: Cannot read properties of undefined (reading 'x') ❯ Foo.test fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-loose-exec.test.js:25:63 ❯ fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-loose-exec.test.js:68:12 -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[78/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[44/50]⎯ FAIL fixtures/babel/babel-preset-env-test-fixtures-plugins-integration-issue-15170-exec.test.js > exec AssertionError: expected [Function] to not throw an error but 'ReferenceError: x is not defined' was thrown @@ -787,7 +401,7 @@ undefined | ^ 7| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[79/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[45/50]⎯ FAIL fixtures/babel/babel-preset-env-test-fixtures-sanity-check-es2015-constants-exec.test.js > exec TypeError: Assignment to constant variable. @@ -798,7 +412,7 @@ TypeError: Assignment to constant variable. | ^ 6| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[80/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[46/50]⎯ FAIL fixtures/babel/babel-preset-env-test-fixtures-sanity-regex-dot-all-exec.test.js > exec AssertionError: expected false to be true // Object.is equality @@ -817,5 +431,5 @@ AssertionError: expected false to be true // Object.is equality 11| expect(/hello.world/su.test(input)).toBe(true); 12| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[81/84]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[47/50]⎯ diff --git a/wasm/parser/package.json b/wasm/parser/package.json index 95c32b676e817d..2f5607c6eb7417 100644 --- a/wasm/parser/package.json +++ b/wasm/parser/package.json @@ -1,6 +1,6 @@ { "name": "@oxc-parser/wasm", - "version": "0.38.0", + "version": "0.39.0", "description": "Wasm target for the oxc parser.", "keywords": [ "JavaScript",