diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a2dc1612f..76580adae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,9 +14,20 @@ concurrency: jobs: test: - name: Test - runs-on: ubuntu-latest + name: Test (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest steps: + - name: Enable Git long paths (Windows) + if: ${{ matrix.os == 'windows-latest' }} + run: git config --global core.longpaths true + - uses: actions/checkout@v6 with: submodules: recursive @@ -34,16 +45,64 @@ jobs: run: cargo test - name: Format + if: ${{ matrix.os == 'ubuntu-latest' }} run: cargo fmt --all -- --check - name: Conformance + if: ${{ matrix.os == 'ubuntu-latest' }} run: | cargo run -p oxc_angular_conformance git diff --exit-code + napi-smoke: + name: NAPI Smoke (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + steps: + - name: Enable Git long paths (Windows) + if: ${{ matrix.os == 'windows-latest' }} + run: git config --global core.longpaths true + + - uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + + - name: Install pnpm + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 + + - name: Install Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: pnpm + + - name: Install dependencies + run: pnpm install + + - name: Build + run: | + pnpm build-dev + pnpm --filter ./napi/angular-compiler build:ts + + - name: Test + run: pnpm test + napi: - name: NAPI Build + name: NAPI Build (Ubuntu E2E) runs-on: ubuntu-latest + needs: napi-smoke steps: - uses: actions/checkout@v6 with: @@ -56,7 +115,7 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 - name: Install Node.js uses: actions/setup-node@v6 @@ -77,7 +136,6 @@ jobs: - name: Test run: | - pnpm test pnpm --filter ./napi/angular-compiler exec playwright install --with-deps pnpm test:e2e - name: Compare tests diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 772d58020..4223d7639 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -80,7 +80,7 @@ jobs: tool: cargo-zigbuild - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 - name: Install Node.js uses: actions/setup-node@v6 @@ -109,7 +109,7 @@ jobs: run: pnpm build-dev --release --target ${{ matrix.target }} -x - name: Upload artifact - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: bindings-${{ matrix.target }} path: napi/angular-compiler/*.node @@ -127,7 +127,7 @@ jobs: - uses: actions/checkout@v6 - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 - name: Install Node.js uses: actions/setup-node@v6 @@ -139,7 +139,7 @@ jobs: run: pnpm install - name: Download artifacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: napi/angular-compiler/artifacts diff --git a/.gitignore b/.gitignore index 71d3ee596..95ddb197a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,5 +23,7 @@ node_modules # Claude Code .claude +.DS_Store + # NOTE: For non-project files such as `.vscode` or `.idea`, please add them to your `.gitignore_global`. # In `.gitconfig`, add `[core] excludesfile = ~/.gitignore_global` diff --git a/Cargo.lock b/Cargo.lock index 0edff759a..441ba32b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,36 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -17,12 +47,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - [[package]] name = "arrayvec" version = "0.7.6" @@ -35,13 +59,28 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-simd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" +dependencies = [ + "simd-abstraction", +] + [[package]] name = "base64-simd" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" dependencies = [ - "outref", + "outref 0.5.2", "vsimd", ] @@ -54,6 +93,27 @@ dependencies = [ "serde_core", ] +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + [[package]] name = "bstr" version = "1.12.1" @@ -64,6 +124,40 @@ dependencies = [ "serde", ] +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + [[package]] name = "castaway" version = "0.2.4" @@ -75,9 +169,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.56" +version = "1.2.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", "shlex", @@ -98,6 +192,15 @@ dependencies = [ "cc", ] +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + [[package]] name = "compact_str" version = "0.9.0" @@ -115,16 +218,50 @@ dependencies = [ [[package]] name = "console" -version = "0.15.11" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" dependencies = [ "encode_unicode", "libc", - "once_cell", "windows-sys", ] +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "const-str" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21077772762a1002bb421c3af42ac1725fa56066bfc53d9a55bb79905df2aaf3" +dependencies = [ + "const-str-proc-macro", +] + +[[package]] +name = "const-str-proc-macro" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1e0fdd2e5d3041e530e1b21158aeeef8b5d0e306bc5c1e3d6cf0930d10e25a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "convert_case" version = "0.11.0" @@ -140,11 +277,95 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79" +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "cssparser" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be934d936a0fbed5bcdc01042b770de1398bf79d0e192f49fa7faea0e99281e" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.11.3", + "smallvec", +] + +[[package]] +name = "cssparser-color" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556c099a61d85989d7af52b692e35a8d68a57e7df8c6d07563dc0778b3960c9f" +dependencies = [ + "cssparser", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "ctor" -version = "0.6.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "424e0138278faeb2b401f174ad17e715c829512d74f3d1e81eb43365c2e0590e" +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" dependencies = [ "ctor-proc-macro", "dtor", @@ -156,6 +377,45 @@ version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + +[[package]] +name = "data-url" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193" +dependencies = [ + "matches", +] + +[[package]] +name = "digest" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -164,7 +424,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -173,11 +433,26 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd8e701084c37e7ef62d3f9e453b618130cbc0ef3573847785952a3ac3f746bf" +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + [[package]] name = "dtor" -version = "0.1.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" dependencies = [ "dtor-proc-macro", ] @@ -194,6 +469,18 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "encode_unicode" version = "1.0.0" @@ -237,6 +524,16 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "float-cmp" version = "0.10.0" @@ -246,12 +543,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "foldhash" version = "0.2.0" @@ -267,6 +558,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.32" @@ -323,7 +620,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -357,15 +654,25 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", "wasip2", - "wasip3", ] [[package]] @@ -392,13 +699,19 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.5" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "foldhash 0.1.5", + "ahash 0.7.8", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.16.1" @@ -407,14 +720,17 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash 0.2.0", + "foldhash", ] [[package]] -name = "heck" -version = "0.5.0" +name = "hybrid-array" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +checksum = "1a79f2aff40c18ab8615ddc5caa9eb5b96314aef18fe5823090f204ad988e813" +dependencies = [ + "typenum", +] [[package]] name = "icu_collections" @@ -497,12 +813,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "idna" version = "1.1.0" @@ -538,9 +848,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.46.3" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82db8c87c7f1ccecb34ce0c24399b8a73081427f3c7c50a5d597925356115e4" +checksum = "99322078b2c076829a1db959d49da554fabc4342257fc0ba5a070a1eb3a01cd8" dependencies = [ "console", "globset", @@ -550,6 +860,15 @@ dependencies = [ "walkdir", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -561,9 +880,19 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] [[package]] name = "json-escape-simd" @@ -581,16 +910,16 @@ dependencies = [ ] [[package]] -name = "leb128fmt" -version = "0.1.0" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.182" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libloading" @@ -613,6 +942,46 @@ dependencies = [ "libc", ] +[[package]] +name = "lightningcss" +version = "1.0.0-alpha.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6314c2f0590ac93c86099b98bb7ba8abcf759bfd89604ffca906472bb54937" +dependencies = [ + "ahash 0.8.12", + "bitflags", + "const-str", + "cssparser", + "cssparser-color", + "dashmap", + "data-encoding", + "getrandom 0.3.4", + "indexmap", + "itertools 0.10.5", + "lazy_static", + "lightningcss-derive", + "parcel_selectors", + "parcel_sourcemap", + "pastey", + "pathdiff", + "rayon", + "serde", + "serde-content", + "smallvec", +] + +[[package]] +name = "lightningcss-derive" +version = "1.0.0-alpha.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12744d1279367caed41739ef094c325d53fb0ffcd4f9b84a368796f870252" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -625,12 +994,27 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + [[package]] name = "memchr" version = "2.8.0" @@ -646,11 +1030,21 @@ dependencies = [ "libmimalloc-sys2", ] +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "napi" -version = "3.8.3" +version = "3.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6944d0bf100571cd6e1a98a316cdca262deb6fccf8d93f5ae1502ca3fc88bd3" +checksum = "fb7848c221fb7bb789e02f01875287ebb1e078b92a6566a34de01ef8806e7c2b" dependencies = [ "bitflags", "ctor", @@ -670,16 +1064,16 @@ checksum = "d376940fd5b723c6893cd1ee3f33abbfd86acb1cd1ec079f3ab04a2a3bc4d3b1" [[package]] name = "napi-derive" -version = "3.5.2" +version = "3.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c914b5e420182bfb73504e0607592cdb8e2e21437d450883077669fb72a114d" +checksum = "60867ff9a6f76e82350e0c3420cb0736f5866091b61d7d8a024baa54b0ec17dd" dependencies = [ - "convert_case", + "convert_case 0.11.0", "ctor", "napi-derive-backend", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -688,11 +1082,11 @@ version = "5.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0864cf6a82e2cfb69067374b64c9253d7e910e5b34db833ed7495dda56ccb18" dependencies = [ - "convert_case", + "convert_case 0.11.0", "proc-macro2", "quote", "semver", - "syn", + "syn 2.0.117", ] [[package]] @@ -752,9 +1146,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "outref" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" [[package]] name = "outref" @@ -768,11 +1168,24 @@ version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" +[[package]] +name = "oxc-browserslist" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86f358f5705f4da4bce0af2792b6d24d64be9c465bdc70a752b2f168c5ff721" +dependencies = [ + "flate2", + "postcard", + "rustc-hash", + "serde", + "thiserror", +] + [[package]] name = "oxc-miette" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a7ba54c704edefead1f44e9ef09c43e5cfae666bdc33516b066011f0e6ebf7" +checksum = "4356a61f2ed4c9b3610245215fbf48970eb277126919f87db9d0efa93a74245c" dependencies = [ "cfg-if", "owo-colors", @@ -785,20 +1198,20 @@ dependencies = [ [[package]] name = "oxc-miette-derive" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4faecb54d0971f948fbc1918df69b26007e6f279a204793669542e1e8b75eb3" +checksum = "b237422b014f8f8fff75bb9379e697d13f8d57551a22c88bebb39f073c1bf696" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "oxc_allocator" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "918416b9d4065146aa5877d14dea39708a46bd5322e670d0ad9a6bc89274c610" +checksum = "5e6fc6ce99f6a28fd477c6df500bbc9bf1c39db166952e15bea218459cc0db0c" dependencies = [ "allocator-api2", "hashbrown 0.16.1", @@ -814,15 +1227,18 @@ version = "0.1.0" dependencies = [ "indexmap", "insta", + "lightningcss", "oxc-miette", "oxc_allocator", "oxc_ast", + "oxc_codegen", "oxc_diagnostics", "oxc_parser", "oxc_resolver", "oxc_semantic", "oxc_sourcemap", "oxc_span", + "oxc_transformer", "pathdiff", "rustc-hash", "semver", @@ -864,9 +1280,9 @@ dependencies = [ [[package]] name = "oxc_ast" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fd5e48a97444fa25ddd6375c19f78eb98978e0f4beab86385372f69626f13d" +checksum = "49fa0813bf9fcff5a4e48fc186ee15a0d276b30b0b575389a34a530864567819" dependencies = [ "bitflags", "oxc_allocator", @@ -881,39 +1297,76 @@ dependencies = [ [[package]] name = "oxc_ast_macros" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034e52d9978573a0c8673934c82ab7707b32dc7e6a30f97052c2281e6aa4c024" +checksum = "3a2b2a2e09ff0dd4790a5ceb4a93349e0ea769d4d98d778946de48decb763b18" dependencies = [ - "phf", + "phf 0.13.1", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "oxc_ast_visit" -version = "0.120.0" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6d2304cb25dbbd028440591bf289ef16e3df98517930e79dcc304be64b3045" +dependencies = [ + "oxc_allocator", + "oxc_ast", + "oxc_span", + "oxc_syntax", +] + +[[package]] +name = "oxc_codegen" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b11389ae97684eb864ebdaf507ce8a83253a1e5598905ed0b9abec69dd26d210" +checksum = "ce92b24319ee9fbfa14a5cc488a5ba91bb04bac070c4bad0ba18c772060d19c0" dependencies = [ + "bitflags", + "cow-utils", + "dragonbox_ecma", + "itoa", "oxc_allocator", "oxc_ast", + "oxc_data_structures", + "oxc_index", + "oxc_semantic", + "oxc_sourcemap", "oxc_span", "oxc_syntax", + "rustc-hash", +] + +[[package]] +name = "oxc_compat" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5a3de8c67c960a20bc0177d54498d1a96275c38eb78a0975b4ffdc5a1fb13a" +dependencies = [ + "cow-utils", + "oxc-browserslist", + "oxc_syntax", + "rustc-hash", + "serde", ] [[package]] name = "oxc_data_structures" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a91d5880ed635c42a5cadee08bfd8dd36e9cf927003eb900384a4b2ee1b741f" +checksum = "c8e8f59bed9522098da177d894dc8635fb3eae218ff97d9c695900cb11fd10a2" +dependencies = [ + "ropey", +] [[package]] name = "oxc_diagnostics" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8fb2f947b6824b47d2ad7dda3f11ec2f9472b8520c196e3f77e9563c820e1df" +checksum = "e0476859d4319f2b063f7c4a3120ee5b7e3e48032865ca501f8545ff44badcff" dependencies = [ "cow-utils", "oxc-miette", @@ -922,9 +1375,9 @@ dependencies = [ [[package]] name = "oxc_ecmascript" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3bf0d28f5f5b72c920d5498bbd4bfee913b219d24f7e0ad1e249699b206bb3" +checksum = "1bcf46e5b1a6f8ea3797e887a9db4c79ed15894ca8685eb628da462d4c4e913f" dependencies = [ "cow-utils", "num-bigint", @@ -938,9 +1391,9 @@ dependencies = [ [[package]] name = "oxc_estree" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02992b00536af40ff377834c0fcfab85217bf3cea60aabb5f707ec11a3963c5" +checksum = "2251e6b61eab7b96f0e9d140b68b0f0d8a851c7d260725433e18b1babdcb9430" dependencies = [ "dragonbox_ecma", "itoa", @@ -959,9 +1412,9 @@ dependencies = [ [[package]] name = "oxc_napi" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb8ba3dc927295b63f16b2f7576c46f47273c790958079c140ab3f1ceb0bf253" +checksum = "ff9776efeaa305817485c69e219bd952ec9b1012ff2cc582e271f3cfd4b10b06" dependencies = [ "napi", "napi-build", @@ -975,9 +1428,9 @@ dependencies = [ [[package]] name = "oxc_parser" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d30b87449d18c4920f3da507ebc781e0a7027c1997004706ee8a721ed8c2437" +checksum = "439d2580047b77faf6e60d358b48e5292e0e026b9cfc158d46ddd0175244bb26" dependencies = [ "bitflags", "cow-utils", @@ -998,16 +1451,16 @@ dependencies = [ [[package]] name = "oxc_regular_expression" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5884f4eaf18d18061037586844374c85045da18bd88de9d4ba84d71cb6498b77" +checksum = "0fb5669d3298a92d440afec516943745794cb4cf977911728cd73e3438db87b9" dependencies = [ "bitflags", "oxc_allocator", "oxc_ast_macros", "oxc_diagnostics", "oxc_span", - "phf", + "phf 0.13.1", "rustc-hash", "unicode-id-start", ] @@ -1041,11 +1494,11 @@ dependencies = [ [[package]] name = "oxc_semantic" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18691a1d381cdbd8b07adb62a057de4dcb9cf4d9a68573eddaafdb64c4dc7647" +checksum = "487e9ef54375b23b159eef73746a02b505c3ae70b9c302610680d3c68a3bb62c" dependencies = [ - "itertools", + "itertools 0.14.0", "memchr", "oxc_allocator", "oxc_ast", @@ -1061,11 +1514,11 @@ dependencies = [ [[package]] name = "oxc_sourcemap" -version = "6.0.2" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f89482522f3cd820817d48ee4ade5b10822060d6e5e4d419f05f6d8bd29d70" +checksum = "6d378eb8bad20e89d66276aebab51f6a5408571092cac94abdd3eabb773713d6" dependencies = [ - "base64-simd", + "base64-simd 0.8.0", "json-escape-simd", "rustc-hash", "serde", @@ -1074,9 +1527,9 @@ dependencies = [ [[package]] name = "oxc_span" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ec4aa8f1b4d29473614e318a24ee48463643cb8c67b5784ce68c053da2f9d5" +checksum = "b1d452f6a664627bdd0f1f1586f9258f81cd7edc5c83e9ef50019f701ef1722d" dependencies = [ "compact_str", "oxc-miette", @@ -1089,9 +1542,9 @@ dependencies = [ [[package]] name = "oxc_str" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74cebd97df3543beeadb40b520b56d99b285910216842c5558648526c85d9697" +checksum = "5c7a27c4371f69387f3d6f8fa56f70e4c6fa6aedc399285de6ec02bb9fd148d7" dependencies = [ "compact_str", "hashbrown 0.16.1", @@ -1102,9 +1555,9 @@ dependencies = [ [[package]] name = "oxc_syntax" -version = "0.120.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a9b599badbe69c0516760af7ebe69d5406aa130e51e5fba3ee60665490edc8" +checksum = "0d60d91023aafc256ab99c3dbf6181473e495695029c0152d2093e87df18ffe2" dependencies = [ "bitflags", "cow-utils", @@ -1115,11 +1568,59 @@ dependencies = [ "oxc_estree", "oxc_index", "oxc_span", - "phf", + "phf 0.13.1", "serde", "unicode-id-start", ] +[[package]] +name = "oxc_transformer" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226d77c70778860c4b4888e4aa52f1b4799bfc67093aa5070f367848ff8df09b" +dependencies = [ + "base64", + "compact_str", + "indexmap", + "itoa", + "memchr", + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_compat", + "oxc_data_structures", + "oxc_diagnostics", + "oxc_ecmascript", + "oxc_regular_expression", + "oxc_semantic", + "oxc_span", + "oxc_syntax", + "oxc_traverse", + "rustc-hash", + "serde", + "serde_json", + "sha1", +] + +[[package]] +name = "oxc_traverse" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31aba1910999e2f9a1cc9c47a490caaed828bb119351abe20a2a7851d554963" +dependencies = [ + "itoa", + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_data_structures", + "oxc_ecmascript", + "oxc_semantic", + "oxc_span", + "oxc_str", + "oxc_syntax", + "rustc-hash", +] + [[package]] name = "papaya" version = "0.2.3" @@ -1130,6 +1631,55 @@ dependencies = [ "seize", ] +[[package]] +name = "parcel_selectors" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54fd03f1ad26cb6b3ec1b7414fa78a3bd639e7dbb421b1a60513c96ce886a196" +dependencies = [ + "bitflags", + "cssparser", + "log", + "phf 0.11.3", + "phf_codegen", + "precomputed-hash", + "rustc-hash", + "smallvec", +] + +[[package]] +name = "parcel_sourcemap" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "485b74d7218068b2b7c0e3ff12fbc61ae11d57cb5d8224f525bd304c6be05bbb" +dependencies = [ + "base64-simd 0.7.0", + "data-url", + "rkyv", + "serde", + "serde_json", + "vlq", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + [[package]] name = "pathdiff" version = "0.2.3" @@ -1142,17 +1692,47 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + [[package]] name = "phf" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ - "phf_macros", - "phf_shared", + "phf_macros 0.13.1", + "phf_shared 0.13.1", "serde", ] +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand", +] + [[package]] name = "phf_generator" version = "0.13.1" @@ -1160,7 +1740,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ "fastrand", - "phf_shared", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -1169,11 +1762,20 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn", + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", ] [[package]] @@ -1197,6 +1799,18 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -1207,14 +1821,10 @@ dependencies = [ ] [[package]] -name = "prettyplease" -version = "0.2.37" +name = "precomputed-hash" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "proc-macro2" @@ -1231,6 +1841,26 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bccbff07d5ed689c4087d20d7307a52ab6141edeedf487c3876a55b86cf63df" +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quote" version = "1.0.45" @@ -1242,9 +1872,59 @@ dependencies = [ [[package]] name = "r-efi" -version = "6.0.0" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] [[package]] name = "ref-cast" @@ -1263,7 +1943,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1283,11 +1963,59 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ropey" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" +dependencies = [ + "smallvec", + "str_indices", +] + [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustix" @@ -1323,6 +2051,18 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "seize" version = "0.5.1" @@ -1361,6 +2101,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-content" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3753ca04f350fa92d00b6146a3555e63c55388c9ef2e11e09bce2ff1c0b509c6" +dependencies = [ + "serde", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -1378,7 +2127,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1395,12 +2144,38 @@ dependencies = [ "zmij", ] +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simd-abstraction" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" +dependencies = [ + "outref 0.1.0", +] + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + [[package]] name = "simd-json" version = "0.17.0" @@ -1461,6 +2236,23 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "str_indices" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.117" @@ -1480,9 +2272,15 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.27.0" @@ -1490,7 +2288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom", + "getrandom 0.3.4", "once_cell", "rustix", "windows-sys", @@ -1524,7 +2322,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1537,6 +2335,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.50.0" @@ -1565,7 +2378,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1577,6 +2390,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + [[package]] name = "unicode-id-start" version = "1.4.0" @@ -1607,12 +2426,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "url" version = "2.5.8" @@ -1631,6 +2444,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "uuid" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "value-trait" version = "0.12.1" @@ -1643,6 +2466,18 @@ dependencies = [ "ryu", ] +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vlq" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65dd7eed29412da847b0f78bcec0ac98588165988a8cfe41d4ea1d429f8ccfff" + [[package]] name = "vsimd" version = "0.8.0" @@ -1659,6 +2494,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasip2" version = "1.0.2+wasi-0.2.9" @@ -1669,46 +2510,48 @@ dependencies = [ ] [[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +name = "wasm-bindgen" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ - "wit-bindgen", + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] -name = "wasm-encoder" -version = "0.244.0" +name = "wasm-bindgen-macro" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ - "leb128fmt", - "wasmparser", + "quote", + "wasm-bindgen-macro-support", ] [[package]] -name = "wasm-metadata" -version = "0.244.0" +name = "wasm-bindgen-macro-support" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", ] [[package]] -name = "wasmparser" -version = "0.244.0" +name = "wasm-bindgen-shared" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", + "unicode-ident", ] [[package]] @@ -1773,7 +2616,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1784,7 +2627,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1823,27 +2666,11 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows-link", ] [[package]] @@ -1855,148 +2682,27 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] [[package]] -name = "wit-component" -version = "0.244.0" +name = "writeable" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] -name = "wit-parser" -version = "0.244.0" +name = "wyz" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", + "tap", ] -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - [[package]] name = "yoke" version = "0.8.1" @@ -2016,10 +2722,30 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "zerofrom" version = "0.1.6" @@ -2037,7 +2763,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -2071,7 +2797,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4b1fb143e..79462d38c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,15 +87,17 @@ multiple_crate_versions = "allow" [workspace.dependencies] # External oxc crates from crates.io -oxc_allocator = "0.120" -oxc_ast = "0.120" -oxc_ast_visit = "0.120" -oxc_diagnostics = "0.120" -oxc_napi = "0.120" -oxc_parser = "0.120" -oxc_semantic = "0.120" -oxc_span = "0.120" +oxc_allocator = "0.123" +oxc_ast = "0.123" +oxc_ast_visit = "0.123" +oxc_diagnostics = "0.123" +oxc_napi = "0.123" +oxc_parser = "0.123" +oxc_semantic = "0.123" +oxc_span = "0.123" oxc_sourcemap = "6.0.1" +oxc_transformer = "0.123" +oxc_codegen = "0.123" # Internal oxc_angular_compiler = { path = "crates/oxc_angular_compiler" } diff --git a/README.md b/README.md index 538701ece..b10b1d32b 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,8 @@ export default defineConfig({ plugins: [ angular({ // Optional configuration - angularVersion: 21, - enableHmr: true, + angularVersion: { major: 21, minor: 0, patch: 0 }, + liveReload: true, }), ], }) @@ -57,8 +57,8 @@ const result = await compileTemplate( 'AppComponent', 'app.component.ts', { - angularVersion: 21, - enableHmr: false, + angularVersion: { major: 21, minor: 0, patch: 0 }, + hmr: false, }, ) @@ -115,10 +115,14 @@ Returns: `Promise` ```typescript interface TransformOptions { // Angular version (19, 20, 21) - angularVersion?: number + angularVersion?: { + major: number + minor: number + patch: number + } // Enable Hot Module Replacement - enableHmr?: boolean + hmr?: boolean // Enable cross-file type elision enableCrossFileElision?: boolean diff --git a/crates/.DS_Store b/crates/.DS_Store new file mode 100644 index 000000000..75368568e Binary files /dev/null and b/crates/.DS_Store differ diff --git a/crates/angular_conformance/src/extractor/util.rs b/crates/angular_conformance/src/extractor/util.rs index 2f493b290..358a5b19c 100644 --- a/crates/angular_conformance/src/extractor/util.rs +++ b/crates/angular_conformance/src/extractor/util.rs @@ -54,7 +54,7 @@ impl SpecExtractor { let parts: Vec<&str> = lit .quasis .iter() - .filter_map(|q| q.value.cooked.as_ref().map(oxc_span::Atom::as_str)) + .filter_map(|q| q.value.cooked.as_ref().map(oxc_span::Str::as_str)) .collect(); Some(parts.join("")) } @@ -138,10 +138,10 @@ impl SpecExtractor { Some(serde_json::Value::String(lit.value.to_string())) } Expression::NumericLiteral(lit) => { - serde_json::Number::from_f64(lit.value).map(serde_json::Value::Number) + serde_json::Number::from_f64(lit.value.into()).map(serde_json::Value::Number) } Expression::NullLiteral(_) => Some(serde_json::Value::Null), - Expression::BooleanLiteral(lit) => Some(serde_json::Value::Bool(lit.value)), + Expression::BooleanLiteral(lit) => Some(serde_json::Value::Bool(lit.value.into())), Expression::ArrayExpression(arr) => { let mut values = Vec::new(); for element in &arr.elements { diff --git a/crates/angular_conformance/src/subsystems/html_whitespace.rs b/crates/angular_conformance/src/subsystems/html_whitespace.rs index 1f18f4fde..8e7447bec 100644 --- a/crates/angular_conformance/src/subsystems/html_whitespace.rs +++ b/crates/angular_conformance/src/subsystems/html_whitespace.rs @@ -384,7 +384,7 @@ impl WhitespaceRemovingHumanizer { for token in tokens { match token.token_type { InterpolatedTokenType::Text => { - let text = token.parts.first().map_or("", oxc_span::Atom::as_str); + let text = token.parts.first().map_or("", oxc_span::Ident::as_str); let processed = process_whitespace(text); result.push(vec![processed]); } diff --git a/crates/oxc_angular_compiler/Cargo.toml b/crates/oxc_angular_compiler/Cargo.toml index fbecb13a2..d2db6c6c6 100644 --- a/crates/oxc_angular_compiler/Cargo.toml +++ b/crates/oxc_angular_compiler/Cargo.toml @@ -23,9 +23,12 @@ oxc_parser = { workspace = true } oxc_semantic = { workspace = true } oxc_span = { workspace = true } oxc_sourcemap = { workspace = true } +oxc_transformer = { workspace = true } +oxc_codegen = { workspace = true } miette = { workspace = true } rustc-hash = { workspace = true } indexmap = { workspace = true } +lightningcss = "1.0.0-alpha.71" oxc_resolver = { version = "11", optional = true } pathdiff = { version = "0.2", optional = true } semver = "1.0.27" diff --git a/crates/oxc_angular_compiler/src/ast/expression.rs b/crates/oxc_angular_compiler/src/ast/expression.rs index 593efc470..e75bd26eb 100644 --- a/crates/oxc_angular_compiler/src/ast/expression.rs +++ b/crates/oxc_angular_compiler/src/ast/expression.rs @@ -11,7 +11,7 @@ //! - No bitwise operators use oxc_allocator::{Allocator, Box, Vec}; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; /// A span within the expression source. #[derive(Debug, Clone, Copy)] @@ -594,7 +594,7 @@ pub struct PropertyRead<'a> { /// The receiver expression. pub receiver: AngularExpression<'a>, /// The property name. - pub name: Atom<'a>, + pub name: Ident<'a>, } /// A safe property read expression: `receiver?.property`. @@ -609,7 +609,7 @@ pub struct SafePropertyRead<'a> { /// The receiver expression. pub receiver: AngularExpression<'a>, /// The property name. - pub name: Atom<'a>, + pub name: Ident<'a>, } /// A keyed read expression: `receiver[key]`. @@ -659,7 +659,7 @@ pub struct BindingPipe<'a> { /// The expression being piped. pub exp: AngularExpression<'a>, /// The pipe name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// The pipe arguments. pub args: Vec<'a, AngularExpression<'a>>, /// The type of pipe reference. @@ -670,7 +670,7 @@ pub struct BindingPipe<'a> { #[derive(Debug, Clone)] pub enum LiteralValue<'a> { /// A string literal. - String(Atom<'a>), + String(Ident<'a>), /// A number literal. Number(f64), /// A boolean literal. @@ -718,7 +718,7 @@ pub struct SpreadElement<'a> { #[derive(Debug)] pub struct ArrowFunctionParameter<'a> { /// The parameter name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// The span of this parameter. pub span: ParseSpan, /// The absolute source span. @@ -751,7 +751,7 @@ pub enum LiteralMapKey<'a> { #[derive(Debug)] pub struct LiteralMapPropertyKey<'a> { /// The key string. - pub key: Atom<'a>, + pub key: Ident<'a>, /// Whether the key is quoted. pub quoted: bool, /// Whether this is a shorthand initialization. @@ -788,7 +788,7 @@ pub struct Interpolation<'a> { /// The absolute source span. pub source_span: AbsoluteSourceSpan, /// The static string parts. - pub strings: Vec<'a, Atom<'a>>, + pub strings: Vec<'a, Ident<'a>>, /// The dynamic expression parts. pub expressions: Vec<'a, AngularExpression<'a>>, } @@ -1062,7 +1062,7 @@ pub struct TemplateLiteralElement<'a> { /// The absolute source span. pub source_span: AbsoluteSourceSpan, /// The text content. - pub text: Atom<'a>, + pub text: Ident<'a>, } /// A parenthesized expression. @@ -1084,9 +1084,9 @@ pub struct RegularExpressionLiteral<'a> { /// The absolute source span. pub source_span: AbsoluteSourceSpan, /// The regex pattern. - pub body: Atom<'a>, + pub body: Ident<'a>, /// The regex flags. - pub flags: Option>, + pub flags: Option>, } // ============================================================================ @@ -1128,7 +1128,7 @@ pub struct ExpressionBinding<'a> { #[derive(Debug, Clone)] pub struct TemplateBindingIdentifier<'a> { /// The source text. - pub source: Atom<'a>, + pub source: Ident<'a>, /// The span. pub span: AbsoluteSourceSpan, } @@ -1139,9 +1139,9 @@ pub struct ASTWithSource<'a> { /// The AST. pub ast: AngularExpression<'a>, /// The original source. - pub source: Option>, + pub source: Option>, /// The source location. - pub location: Atom<'a>, + pub location: Ident<'a>, /// The absolute offset in the template. pub absolute_offset: u32, // Note: Errors are collected separately in the parser/transformer context @@ -1210,7 +1210,7 @@ pub enum BindingType { #[derive(Debug)] pub struct ParsedProperty<'a> { /// The property name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// The binding expression. pub expression: ASTWithSource<'a>, /// The type of property binding. @@ -1247,11 +1247,11 @@ impl<'a> ParsedProperty<'a> { #[derive(Debug)] pub struct ParsedEvent<'a> { /// The event name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// The event target or animation phase. /// For regular events: "window", "document", "body", or None. /// For legacy animation events: the animation phase. - pub target_or_phase: Option>, + pub target_or_phase: Option>, /// The type of event binding. pub event_type: ParsedEventType, /// The handler expression. diff --git a/crates/oxc_angular_compiler/src/ast/html.rs b/crates/oxc_angular_compiler/src/ast/html.rs index 41a5e3f6f..751e52038 100644 --- a/crates/oxc_angular_compiler/src/ast/html.rs +++ b/crates/oxc_angular_compiler/src/ast/html.rs @@ -4,7 +4,7 @@ //! ported from Angular's `ml_parser/ast.ts`. use oxc_allocator::{Box, Vec}; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use super::expression::AngularExpression; @@ -31,7 +31,7 @@ pub struct InterpolatedToken<'a> { /// The token type. pub token_type: InterpolatedTokenType, /// The token parts (structure depends on token type). - pub parts: Vec<'a, Atom<'a>>, + pub parts: Vec<'a, Ident<'a>>, /// The source span. pub span: Span, } @@ -83,7 +83,7 @@ impl<'a> HtmlNode<'a> { #[derive(Debug)] pub struct HtmlText<'a> { /// The decoded text value (entities resolved, interpolations joined). - pub value: Atom<'a>, + pub value: Ident<'a>, /// The source span (after stripping leading trivia). pub span: Span, /// The full start offset before stripping leading trivia (for source maps). @@ -100,13 +100,13 @@ pub struct HtmlElement<'a> { /// The element tag name. /// For regular elements: the tag name (e.g., "div", ":svg:rect"). /// For selectorless components: the component name (e.g., "MyComp"). - pub name: Atom<'a>, + pub name: Ident<'a>, /// For selectorless components: the namespace prefix (e.g., "svg" in ``). /// None for regular elements or selectorless components without namespace. - pub component_prefix: Option>, + pub component_prefix: Option>, /// For selectorless components: the HTML tag name (e.g., "rect" in ``). /// None for regular elements or selectorless components without tag name. - pub component_tag_name: Option>, + pub component_tag_name: Option>, /// The element attributes. pub attrs: Vec<'a, HtmlAttribute<'a>>, /// Selectorless directives (e.g., @Dir, @Dir(attr="value")). @@ -133,12 +133,12 @@ pub struct HtmlElement<'a> { #[derive(Debug)] pub struct HtmlComponent<'a> { /// The component class name (e.g., "MyComp"). - pub component_name: Atom<'a>, + pub component_name: Ident<'a>, /// The HTML tag name (e.g., "button" in ``). /// None for component-only syntax like ``. - pub tag_name: Option>, + pub tag_name: Option>, /// The full qualified name (e.g., "MyComp:svg:rect"). - pub full_name: Atom<'a>, + pub full_name: Ident<'a>, /// The element attributes. pub attrs: Vec<'a, HtmlAttribute<'a>>, /// Selectorless directives (e.g., @Dir). @@ -159,7 +159,7 @@ pub struct HtmlComponent<'a> { #[derive(Debug)] pub struct HtmlDirective<'a> { /// The directive name (without the @ prefix). - pub name: Atom<'a>, + pub name: Ident<'a>, /// The directive attributes (inside parentheses, if any). pub attrs: Vec<'a, HtmlAttribute<'a>>, /// The source span for the entire directive. @@ -176,9 +176,9 @@ pub struct HtmlDirective<'a> { #[derive(Debug)] pub struct HtmlAttribute<'a> { /// The attribute name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// The decoded attribute value (entities resolved, interpolations joined). - pub value: Atom<'a>, + pub value: Ident<'a>, /// The source span. pub span: Span, /// The name span. @@ -194,7 +194,7 @@ pub struct HtmlAttribute<'a> { #[derive(Debug)] pub struct HtmlComment<'a> { /// The comment value. - pub value: Atom<'a>, + pub value: Ident<'a>, /// The source span. pub span: Span, } @@ -203,9 +203,9 @@ pub struct HtmlComment<'a> { #[derive(Debug)] pub struct HtmlExpansion<'a> { /// The switch value. - pub switch_value: Atom<'a>, + pub switch_value: Ident<'a>, /// The expansion type (e.g., "plural", "select"). - pub expansion_type: Atom<'a>, + pub expansion_type: Ident<'a>, /// The expansion cases. pub cases: Vec<'a, HtmlExpansionCase<'a>>, /// The source span. @@ -222,7 +222,7 @@ pub struct HtmlExpansion<'a> { #[derive(Debug)] pub struct HtmlExpansionCase<'a> { /// The case value (e.g., "one", "other"). - pub value: Atom<'a>, + pub value: Ident<'a>, /// The expansion nodes. pub expansion: Vec<'a, HtmlNode<'a>>, /// The source span. @@ -268,7 +268,7 @@ pub struct HtmlBlock<'a> { /// The block type. pub block_type: BlockType, /// The block name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// The block parameters. pub parameters: Vec<'a, HtmlBlockParameter<'a>>, /// The child nodes. @@ -291,7 +291,7 @@ pub struct HtmlBlock<'a> { #[derive(Debug)] pub struct HtmlBlockParameter<'a> { /// The raw expression text (e.g., "minimum 500ms", "track item.id"). - pub expression: Atom<'a>, + pub expression: Ident<'a>, /// The source span. pub span: Span, } @@ -300,7 +300,7 @@ pub struct HtmlBlockParameter<'a> { #[derive(Debug)] pub struct HtmlLetDeclaration<'a> { /// The variable name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// The value expression. pub value: AngularExpression<'a>, /// The source span. @@ -506,7 +506,7 @@ mod tests { // Create a simple tree: root element with two child elements let child1 = HtmlElement { - name: Atom::from("span"), + name: Ident::from("span"), component_prefix: None, component_tag_name: None, attrs: Vec::new_in(&allocator), @@ -520,7 +520,7 @@ mod tests { }; let child2 = HtmlElement { - name: Atom::from("p"), + name: Ident::from("p"), component_prefix: None, component_tag_name: None, attrs: Vec::new_in(&allocator), @@ -538,7 +538,7 @@ mod tests { children.push(HtmlNode::Element(Box::new_in(child2, &allocator))); let root = HtmlElement { - name: Atom::from("div"), + name: Ident::from("div"), component_prefix: None, component_tag_name: None, attrs: Vec::new_in(&allocator), @@ -565,7 +565,7 @@ mod tests { let allocator = Allocator::default(); let text = HtmlText { - value: Atom::from("Hello"), + value: Ident::from("Hello"), span: Span::default(), full_start: None, tokens: Vec::new_in(&allocator), diff --git a/crates/oxc_angular_compiler/src/ast/r3.rs b/crates/oxc_angular_compiler/src/ast/r3.rs index 9e4f8ea4e..772fbbad8 100644 --- a/crates/oxc_angular_compiler/src/ast/r3.rs +++ b/crates/oxc_angular_compiler/src/ast/r3.rs @@ -6,7 +6,7 @@ //! Ported from Angular's `render3/r3_ast.ts`. use oxc_allocator::{Allocator, Box, HashMap, Vec}; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use crate::ast::expression::{ASTWithSource, AngularExpression, BindingType, ParsedEventType}; use crate::i18n::serializer::format_i18n_placeholder_name; @@ -43,18 +43,18 @@ pub struct I18nMessage<'a> { /// Message AST nodes. pub nodes: Vec<'a, I18nNode<'a>>, /// The meaning of the message (for disambiguation). - pub meaning: Atom<'a>, + pub meaning: Ident<'a>, /// Description of the message for translators. - pub description: Atom<'a>, + pub description: Ident<'a>, /// Custom ID specified by the developer. - pub custom_id: Atom<'a>, + pub custom_id: Ident<'a>, /// The computed message ID. - pub id: Atom<'a>, + pub id: Ident<'a>, /// Legacy IDs for backwards compatibility. - pub legacy_ids: Vec<'a, Atom<'a>>, + pub legacy_ids: Vec<'a, Ident<'a>>, /// The serialized message string for goog.getMsg and $localize. /// Contains the message text with placeholder markers like "{$interpolation}". - pub message_string: Atom<'a>, + pub message_string: Ident<'a>, } /// i18n AST node. @@ -80,7 +80,7 @@ pub enum I18nNode<'a> { #[derive(Debug)] pub struct I18nText<'a> { /// The text value. - pub value: Atom<'a>, + pub value: Ident<'a>, /// Source span. pub source_span: Span, } @@ -98,28 +98,28 @@ pub struct I18nContainer<'a> { #[derive(Debug)] pub struct I18nIcu<'a> { /// The expression being evaluated. - pub expression: Atom<'a>, + pub expression: Ident<'a>, /// ICU type string (plural, select, selectordinal, or custom). - pub icu_type: Atom<'a>, + pub icu_type: Ident<'a>, /// Case branches. - pub cases: HashMap<'a, Atom<'a>, I18nNode<'a>>, + pub cases: HashMap<'a, Ident<'a>, I18nNode<'a>>, /// Source span. pub source_span: Span, /// Expression placeholder name (for message serialization). - pub expression_placeholder: Option>, + pub expression_placeholder: Option>, } /// HTML tag placeholder. #[derive(Debug)] pub struct I18nTagPlaceholder<'a> { /// Tag name. - pub tag: Atom<'a>, + pub tag: Ident<'a>, /// Tag attributes. - pub attrs: HashMap<'a, Atom<'a>, Atom<'a>>, + pub attrs: HashMap<'a, Ident<'a>, Ident<'a>>, /// Start tag placeholder name. - pub start_name: Atom<'a>, + pub start_name: Ident<'a>, /// Close tag placeholder name. - pub close_name: Atom<'a>, + pub close_name: Ident<'a>, /// Child nodes. pub children: Vec<'a, I18nNode<'a>>, /// Whether this is a void element. @@ -136,9 +136,9 @@ pub struct I18nTagPlaceholder<'a> { #[derive(Debug)] pub struct I18nPlaceholder<'a> { /// The expression value. - pub value: Atom<'a>, + pub value: Ident<'a>, /// Placeholder name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Source span. pub source_span: Span, } @@ -149,7 +149,7 @@ pub struct I18nIcuPlaceholder<'a> { /// The ICU expression. pub value: Box<'a, I18nIcu<'a>>, /// Placeholder name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Source span. pub source_span: Span, } @@ -158,13 +158,13 @@ pub struct I18nIcuPlaceholder<'a> { #[derive(Debug)] pub struct I18nBlockPlaceholder<'a> { /// Block name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Block parameters. - pub parameters: Vec<'a, Atom<'a>>, + pub parameters: Vec<'a, Ident<'a>>, /// Start block placeholder name. - pub start_name: Atom<'a>, + pub start_name: Ident<'a>, /// End block placeholder name. - pub close_name: Atom<'a>, + pub close_name: Ident<'a>, /// Child nodes. pub children: Vec<'a, I18nNode<'a>>, /// Source span (overall). @@ -559,7 +559,7 @@ impl<'a> R3Node<'a> { #[derive(Debug, Clone)] pub struct R3Comment<'a> { /// The comment text. - pub value: Atom<'a>, + pub value: Ident<'a>, /// Source span. pub source_span: Span, } @@ -568,7 +568,7 @@ pub struct R3Comment<'a> { #[derive(Debug)] pub struct R3Text<'a> { /// The text content. - pub value: Atom<'a>, + pub value: Ident<'a>, /// Source span. pub source_span: Span, } @@ -592,9 +592,9 @@ pub struct R3BoundText<'a> { #[derive(Debug)] pub struct R3TextAttribute<'a> { /// Attribute name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Attribute value. - pub value: Atom<'a>, + pub value: Ident<'a>, /// Source span. pub source_span: Span, /// Key span (the attribute name). @@ -647,7 +647,7 @@ impl Default for SecurityContext { #[derive(Debug)] pub struct R3BoundAttribute<'a> { /// Attribute name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Binding type (Property, Attribute, Class, Style, etc.). pub binding_type: BindingType, /// Security context for sanitization. @@ -655,7 +655,7 @@ pub struct R3BoundAttribute<'a> { /// The binding expression. pub value: AngularExpression<'a>, /// Unit for style bindings (e.g., "px"). - pub unit: Option>, + pub unit: Option>, /// Source span. pub source_span: Span, /// Key span. @@ -670,15 +670,15 @@ pub struct R3BoundAttribute<'a> { #[derive(Debug)] pub struct R3BoundEvent<'a> { /// Event name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Event type. pub event_type: ParsedEventType, /// Handler expression. pub handler: AngularExpression<'a>, /// Target element (for `window:` or `document:` events). - pub target: Option>, + pub target: Option>, /// Animation phase. - pub phase: Option>, + pub phase: Option>, /// Source span. pub source_span: Span, /// Handler span. @@ -695,7 +695,7 @@ pub struct R3BoundEvent<'a> { #[derive(Debug)] pub struct R3Element<'a> { /// Element tag name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Static attributes. pub attributes: Vec<'a, R3TextAttribute<'a>>, /// Bound input properties. @@ -726,7 +726,7 @@ pub struct R3Element<'a> { #[derive(Debug)] pub struct R3Template<'a> { /// Tag name (None for structural directives on `ng-template`). - pub tag_name: Option>, + pub tag_name: Option>, /// Static attributes. pub attributes: Vec<'a, R3TextAttribute<'a>>, /// Bound inputs. @@ -768,7 +768,7 @@ pub enum R3TemplateAttr<'a> { #[derive(Debug)] pub struct R3Content<'a> { /// The selector for content projection. - pub selector: Atom<'a>, + pub selector: Ident<'a>, /// Static attributes. pub attributes: Vec<'a, R3TextAttribute<'a>>, /// Child nodes (usually empty). @@ -789,9 +789,9 @@ pub struct R3Content<'a> { #[derive(Debug)] pub struct R3Variable<'a> { /// Variable name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Variable value (for `let x = value`). - pub value: Atom<'a>, + pub value: Ident<'a>, /// Source span. pub source_span: Span, /// Key span. @@ -804,9 +804,9 @@ pub struct R3Variable<'a> { #[derive(Debug)] pub struct R3Reference<'a> { /// Reference name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Reference value (directive name or empty). - pub value: Atom<'a>, + pub value: Ident<'a>, /// Source span. pub source_span: Span, /// Key span. @@ -1068,7 +1068,7 @@ pub struct R3ImmediateDeferredTrigger { #[derive(Debug)] pub struct R3HoverDeferredTrigger<'a> { /// Reference to the element to hover. - pub reference: Option>, + pub reference: Option>, /// Source span. pub source_span: Span, /// Name span. @@ -1102,7 +1102,7 @@ pub struct R3TimerDeferredTrigger { #[derive(Debug)] pub struct R3InteractionDeferredTrigger<'a> { /// Reference to the element to interact with. - pub reference: Option>, + pub reference: Option>, /// Source span. pub source_span: Span, /// Name span. @@ -1119,7 +1119,7 @@ pub struct R3InteractionDeferredTrigger<'a> { #[derive(Debug)] pub struct R3ViewportDeferredTrigger<'a> { /// Reference to the element to observe. - pub reference: Option>, + pub reference: Option>, /// Viewport options (margin, etc.). pub options: Option>, /// Source span. @@ -1265,7 +1265,7 @@ pub struct R3DeferredBlockError<'a> { #[derive(Debug)] pub struct R3UnknownBlock<'a> { /// Block name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Source span. pub source_span: Span, /// Name span. @@ -1276,7 +1276,7 @@ pub struct R3UnknownBlock<'a> { #[derive(Debug)] pub struct R3LetDeclaration<'a> { /// Variable name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Value expression. pub value: AngularExpression<'a>, /// Source span. @@ -1291,11 +1291,11 @@ pub struct R3LetDeclaration<'a> { #[derive(Debug)] pub struct R3Component<'a> { /// Component class name. - pub component_name: Atom<'a>, + pub component_name: Ident<'a>, /// Tag name in template. - pub tag_name: Option>, + pub tag_name: Option>, /// Full component name. - pub full_name: Atom<'a>, + pub full_name: Ident<'a>, /// Static attributes. pub attributes: Vec<'a, R3TextAttribute<'a>>, /// Bound inputs. @@ -1324,7 +1324,7 @@ pub struct R3Component<'a> { #[derive(Debug)] pub struct R3Directive<'a> { /// Directive class name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Static attributes. pub attributes: Vec<'a, R3TextAttribute<'a>>, /// Bound inputs. @@ -1349,7 +1349,7 @@ pub struct R3Directive<'a> { #[derive(Debug)] pub struct R3HostElement<'a> { /// Possible tag names for the host element. Must have at least one. - pub tag_names: Vec<'a, Atom<'a>>, + pub tag_names: Vec<'a, Ident<'a>>, /// Attribute and property bindings. pub bindings: Vec<'a, R3BoundAttribute<'a>>, /// Event listeners. @@ -1362,9 +1362,9 @@ pub struct R3HostElement<'a> { #[derive(Debug)] pub struct R3Icu<'a> { /// Variable expressions (ordered: must preserve insertion order like JS objects). - pub vars: Vec<'a, (Atom<'a>, R3BoundText<'a>)>, + pub vars: Vec<'a, (Ident<'a>, R3BoundText<'a>)>, /// Placeholder expressions (ordered: must preserve insertion order like JS objects). - pub placeholders: Vec<'a, (Atom<'a>, R3IcuPlaceholder<'a>)>, + pub placeholders: Vec<'a, (Ident<'a>, R3IcuPlaceholder<'a>)>, /// Source span. pub source_span: Span, /// i18n metadata. @@ -1395,13 +1395,31 @@ pub trait R3Visitor<'a> { /// Visit a bound text node. fn visit_bound_text(&mut self, _text: &R3BoundText<'a>) {} + /// Visit a static text attribute. + fn visit_text_attribute(&mut self, _attr: &R3TextAttribute<'a>) {} + + /// Visit a bound attribute (input property). + fn visit_bound_attribute(&mut self, _attr: &R3BoundAttribute<'a>) {} + + /// Visit a bound event (output). + fn visit_bound_event(&mut self, _event: &R3BoundEvent<'a>) {} + /// Visit an element. fn visit_element(&mut self, element: &R3Element<'a>) { self.visit_element_children(element); } - /// Visit element children. + /// Visit element children, attributes, inputs, and outputs. fn visit_element_children(&mut self, element: &R3Element<'a>) { + for attr in &element.attributes { + self.visit_text_attribute(attr); + } + for input in &element.inputs { + self.visit_bound_attribute(input); + } + for output in &element.outputs { + self.visit_bound_event(output); + } for child in &element.children { child.visit(self); } @@ -1412,8 +1430,17 @@ pub trait R3Visitor<'a> { self.visit_template_children(template); } - /// Visit template children. + /// Visit template children, attributes, inputs, and outputs. fn visit_template_children(&mut self, template: &R3Template<'a>) { + for attr in &template.attributes { + self.visit_text_attribute(attr); + } + for input in &template.inputs { + self.visit_bound_attribute(input); + } + for output in &template.outputs { + self.visit_bound_event(output); + } for child in &template.children { child.visit(self); } @@ -1421,6 +1448,9 @@ pub trait R3Visitor<'a> { /// Visit a content projection slot. fn visit_content(&mut self, content: &R3Content<'a>) { + for attr in &content.attributes { + self.visit_text_attribute(attr); + } for child in &content.children { child.visit(self); } @@ -1531,6 +1561,15 @@ pub trait R3Visitor<'a> { /// Visit a component. fn visit_component(&mut self, component: &R3Component<'a>) { + for attr in &component.attributes { + self.visit_text_attribute(attr); + } + for input in &component.inputs { + self.visit_bound_attribute(input); + } + for output in &component.outputs { + self.visit_bound_event(output); + } for child in &component.children { child.visit(self); } @@ -1560,11 +1599,133 @@ pub struct R3ParseResult<'a> { /// Uses std::vec::Vec since ParseError contains Drop types (Arc, String). pub errors: std::vec::Vec, /// Extracted styles. - pub styles: Vec<'a, Atom<'a>>, + pub styles: Vec<'a, Ident<'a>>, /// Extracted style URLs. - pub style_urls: Vec<'a, Atom<'a>>, + pub style_urls: Vec<'a, Ident<'a>>, /// Content projection selectors. - pub ng_content_selectors: Vec<'a, Atom<'a>>, + pub ng_content_selectors: Vec<'a, Ident<'a>>, /// Comment nodes (if collected). pub comment_nodes: Option>>, } + +#[cfg(test)] +mod tests { + use oxc_allocator::Allocator; + + use crate::ast::r3::{R3Visitor, visit_all}; + use crate::parser::html::HtmlParser; + use crate::transform::html_to_r3::{TransformOptions, html_ast_to_r3_ast}; + + /// A visitor that collects names of visited attributes, inputs, and outputs. + struct AttributeCollector { + text_attributes: Vec, + bound_attributes: Vec, + bound_events: Vec, + elements: Vec, + } + + impl AttributeCollector { + fn new() -> Self { + Self { + text_attributes: Vec::new(), + bound_attributes: Vec::new(), + bound_events: Vec::new(), + elements: Vec::new(), + } + } + } + + impl<'a> R3Visitor<'a> for AttributeCollector { + fn visit_element(&mut self, element: &super::R3Element<'a>) { + self.elements.push(element.name.to_string()); + self.visit_element_children(element); + } + + fn visit_text_attribute(&mut self, attr: &super::R3TextAttribute<'a>) { + self.text_attributes.push(attr.name.to_string()); + } + + fn visit_bound_attribute(&mut self, attr: &super::R3BoundAttribute<'a>) { + self.bound_attributes.push(attr.name.to_string()); + } + + fn visit_bound_event(&mut self, event: &super::R3BoundEvent<'a>) { + self.bound_events.push(event.name.to_string()); + } + } + + #[test] + fn test_r3_visitor_visits_attributes_inputs_outputs() { + let allocator = Allocator::default(); + let template = + r#""#; + + let html_result = HtmlParser::new(&allocator, template, "test.html").parse(); + assert!(html_result.errors.is_empty()); + + let r3_result = html_ast_to_r3_ast( + &allocator, + template, + &html_result.nodes, + TransformOptions::default(), + ); + assert!(r3_result.errors.is_empty()); + + let mut collector = AttributeCollector::new(); + visit_all(&mut collector, &r3_result.nodes); + + assert_eq!(collector.elements, vec!["button"]); + assert_eq!(collector.text_attributes, vec!["type"]); + assert_eq!(collector.bound_attributes, vec!["disabled"]); + assert_eq!(collector.bound_events, vec!["click"]); + } + + #[test] + fn test_r3_visitor_visits_nested_elements() { + let allocator = Allocator::default(); + let template = r#"
text
"#; + + let html_result = HtmlParser::new(&allocator, template, "test.html").parse(); + assert!(html_result.errors.is_empty()); + + let r3_result = html_ast_to_r3_ast( + &allocator, + template, + &html_result.nodes, + TransformOptions::default(), + ); + assert!(r3_result.errors.is_empty()); + + let mut collector = AttributeCollector::new(); + visit_all(&mut collector, &r3_result.nodes); + + assert_eq!(collector.elements, vec!["div", "span"]); + assert_eq!(collector.text_attributes, vec!["id", "class"]); + assert_eq!(collector.bound_attributes, vec!["title"]); + assert_eq!(collector.bound_events, vec!["mouseenter"]); + } + + #[test] + fn test_r3_visitor_default_noop_does_not_break() { + let allocator = Allocator::default(); + let template = r#""#; + + let html_result = HtmlParser::new(&allocator, template, "test.html").parse(); + assert!(html_result.errors.is_empty()); + + let r3_result = html_ast_to_r3_ast( + &allocator, + template, + &html_result.nodes, + TransformOptions::default(), + ); + assert!(r3_result.errors.is_empty()); + + // A visitor with all default no-op methods should traverse without panic + struct NoopVisitor; + impl<'a> R3Visitor<'a> for NoopVisitor {} + + let mut visitor = NoopVisitor; + visit_all(&mut visitor, &r3_result.nodes); + } +} diff --git a/crates/oxc_angular_compiler/src/class_debug_info/compiler.rs b/crates/oxc_angular_compiler/src/class_debug_info/compiler.rs index 947701555..df4c34e91 100644 --- a/crates/oxc_angular_compiler/src/class_debug_info/compiler.rs +++ b/crates/oxc_angular_compiler/src/class_debug_info/compiler.rs @@ -6,7 +6,7 @@ //! - `setClassDebugInfo(type, { className, filePath?, lineNumber?, forbidOrphanRendering? })` use oxc_allocator::{Allocator, Box, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use super::metadata::R3ClassDebugInfo; use crate::output::ast::{ @@ -54,7 +54,7 @@ fn internal_compile_class_debug_info<'a>( // className entries.push(LiteralMapEntry { - key: Atom::from("className"), + key: Ident::from("className"), value: literal_string_atom(allocator, debug_info.class_name.clone()), quoted: false, }); @@ -64,13 +64,13 @@ fn internal_compile_class_debug_info<'a>( // will typically ignore lineNumber as well) if let Some(file_path) = &debug_info.file_path { entries.push(LiteralMapEntry { - key: Atom::from("filePath"), + key: Ident::from("filePath"), value: literal_string_atom(allocator, file_path.clone()), quoted: false, }); entries.push(LiteralMapEntry { - key: Atom::from("lineNumber"), + key: Ident::from("lineNumber"), value: literal_number(allocator, debug_info.line_number), quoted: false, }); @@ -79,7 +79,7 @@ fn internal_compile_class_debug_info<'a>( // Include forbidOrphanRendering only if it's true (to reduce generated code) if debug_info.forbid_orphan_rendering { entries.push(LiteralMapEntry { - key: Atom::from("forbidOrphanRendering"), + key: Ident::from("forbidOrphanRendering"), value: literal_bool(allocator, true), quoted: false, }); @@ -114,7 +114,7 @@ fn dev_only_guarded_expression<'a>( expr: OutputExpression<'a>, ) -> OutputExpression<'a> { let guard_var = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ngDevMode"), source_span: None }, + ReadVarExpr { name: Ident::from("ngDevMode"), source_span: None }, allocator, )); @@ -167,12 +167,12 @@ fn import_expr<'a>(allocator: &'a Allocator, identifier: &'static str) -> Output ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(identifier), + name: Ident::from(identifier), optional: false, source_span: None, }, @@ -183,13 +183,13 @@ fn import_expr<'a>(allocator: &'a Allocator, identifier: &'static str) -> Output /// Creates a string literal from a static str. fn literal_string<'a>(allocator: &'a Allocator, value: &'static str) -> OutputExpression<'a> { OutputExpression::Literal(Box::new_in( - LiteralExpr { value: LiteralValue::String(Atom::from(value)), source_span: None }, + LiteralExpr { value: LiteralValue::String(Ident::from(value)), source_span: None }, allocator, )) } /// Creates a string literal from an Atom. -fn literal_string_atom<'a>(allocator: &'a Allocator, value: Atom<'a>) -> OutputExpression<'a> { +fn literal_string_atom<'a>(allocator: &'a Allocator, value: Ident<'a>) -> OutputExpression<'a> { OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::String(value), source_span: None }, allocator, diff --git a/crates/oxc_angular_compiler/src/class_debug_info/metadata.rs b/crates/oxc_angular_compiler/src/class_debug_info/metadata.rs index b86d5cb07..f6c61003c 100644 --- a/crates/oxc_angular_compiler/src/class_debug_info/metadata.rs +++ b/crates/oxc_angular_compiler/src/class_debug_info/metadata.rs @@ -2,7 +2,7 @@ //! //! Ported from Angular's `render3/r3_class_debug_info_compiler.ts`. -use oxc_span::Atom; +use oxc_span::Ident; use crate::output::ast::OutputExpression; @@ -16,7 +16,7 @@ pub struct R3ClassDebugInfo<'a> { pub r#type: OutputExpression<'a>, /// The original class name as it appears in its definition. - pub class_name: Atom<'a>, + pub class_name: Ident<'a>, /// The relative path of the file in which the class is defined. /// @@ -24,7 +24,7 @@ pub struct R3ClassDebugInfo<'a> { /// absolute file paths are never shown. If the relative path cannot /// be computed, this should be `None`, and downstream consumers will /// typically ignore the `line_number` field as well. - pub file_path: Option>, + pub file_path: Option>, /// The line number in which this class is defined (1-indexed). pub line_number: u32, @@ -40,12 +40,12 @@ impl<'a> R3ClassDebugInfo<'a> { /// /// File path and line number default to `None`/`0`, and /// `forbid_orphan_rendering` defaults to `false`. - pub fn new(r#type: OutputExpression<'a>, class_name: Atom<'a>) -> Self { + pub fn new(r#type: OutputExpression<'a>, class_name: Ident<'a>) -> Self { Self { r#type, class_name, file_path: None, line_number: 0, forbid_orphan_rendering: false } } /// Sets the file path for this debug info. - pub fn with_file_path(mut self, file_path: Atom<'a>) -> Self { + pub fn with_file_path(mut self, file_path: Ident<'a>) -> Self { self.file_path = Some(file_path); self } diff --git a/crates/oxc_angular_compiler/src/class_metadata/builders.rs b/crates/oxc_angular_compiler/src/class_metadata/builders.rs index c0e34a0ef..2b8566e21 100644 --- a/crates/oxc_angular_compiler/src/class_metadata/builders.rs +++ b/crates/oxc_angular_compiler/src/class_metadata/builders.rs @@ -8,7 +8,7 @@ use oxc_ast::ast::{ Class, ClassElement, Decorator, Expression, FormalParameter, MethodDefinitionKind, PropertyKey, TSType, TSTypeName, }; -use oxc_span::Atom; +use oxc_span::Ident; use crate::component::{ImportMap, NamespaceRegistry, R3DependencyMetadata}; use crate::output::ast::{ @@ -65,7 +65,7 @@ pub fn build_decorator_metadata_array<'a>( // Add "type" entry map_entries.push(LiteralMapEntry { - key: Atom::from("type"), + key: Ident::from("type"), value: type_expr, quoted: false, }); @@ -84,7 +84,7 @@ pub fn build_decorator_metadata_array<'a>( if !args.is_empty() { map_entries.push(LiteralMapEntry { - key: Atom::from("args"), + key: Ident::from("args"), value: OutputExpression::LiteralArray(Box::new_in( LiteralArrayExpr { entries: args, source_span: None }, allocator, @@ -155,7 +155,7 @@ pub fn build_ctor_params_metadata<'a>( }); map_entries.push(LiteralMapEntry { - key: Atom::from("type"), + key: Ident::from("type"), value: type_expr, quoted: false, }); @@ -165,7 +165,7 @@ pub fn build_ctor_params_metadata<'a>( if !param_decorators.is_empty() { let decorators_array = build_decorator_metadata_array(allocator, ¶m_decorators); map_entries.push(LiteralMapEntry { - key: Atom::from("decorators"), + key: Ident::from("decorators"), value: decorators_array, quoted: false, }); @@ -366,7 +366,7 @@ fn build_param_type_expression<'a>( /// /// Returns the simple type name from the annotation, if present. /// Used to get the type name for namespace-prefixed references in metadata. -fn extract_param_type_name<'a>(param: &FormalParameter<'a>) -> Option> { +fn extract_param_type_name<'a>(param: &FormalParameter<'a>) -> Option> { let type_annotation = param.type_annotation.as_ref()?; match &type_annotation.type_annotation { TSType::TSTypeReference(type_ref) => match &type_ref.type_name { @@ -444,10 +444,10 @@ fn get_decorator_name<'a>(decorator: &Decorator<'a>) -> Option<&'a str> { } /// Get property key name as an Atom. -fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { +fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { match key { PropertyKey::StaticIdentifier(id) => Some(id.name.into()), - PropertyKey::StringLiteral(lit) => Some(lit.value), + PropertyKey::StringLiteral(lit) => Some(lit.value.into()), _ => None, } } diff --git a/crates/oxc_angular_compiler/src/class_metadata/compiler.rs b/crates/oxc_angular_compiler/src/class_metadata/compiler.rs index da66f16d5..f0e679424 100644 --- a/crates/oxc_angular_compiler/src/class_metadata/compiler.rs +++ b/crates/oxc_angular_compiler/src/class_metadata/compiler.rs @@ -7,7 +7,7 @@ //! - `setClassMetadataAsync(type, resolver, callback)` for deferred dependencies use oxc_allocator::{Allocator, Box, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use super::metadata::{R3ClassMetadata, R3DeferPerComponentDependency}; use crate::output::ast::{ @@ -77,7 +77,7 @@ pub fn compile_opaque_async_class_metadata<'a>( allocator: &'a Allocator, metadata: &R3ClassMetadata<'a>, defer_resolver: OutputExpression<'a>, - deferred_dependency_names: &[Atom<'a>], + deferred_dependency_names: &[Ident<'a>], ) -> OutputExpression<'a> { let mut params = Vec::new_in(allocator); for name in deferred_dependency_names { @@ -106,16 +106,16 @@ pub fn compile_component_metadata_async_resolver<'a>( for dep in dependencies { // Create: (m) => m.CmpA (or m.default for default imports) let mut inner_params = Vec::new_in(allocator); - inner_params.push(FnParam { name: Atom::from("m") }); + inner_params.push(FnParam { name: Ident::from("m") }); let prop_name = - if dep.is_default_import { Atom::from("default") } else { dep.symbol_name.clone() }; + if dep.is_default_import { Ident::from("default") } else { dep.symbol_name.clone() }; let inner_body = OutputExpression::ReadProp(Box::new_in( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("m"), source_span: None }, + ReadVarExpr { name: Ident::from("m"), source_span: None }, allocator, )), allocator, @@ -149,7 +149,7 @@ pub fn compile_component_metadata_async_resolver<'a>( let then_prop = OutputExpression::ReadProp(Box::new_in( ReadPropExpr { receiver: Box::new_in(dynamic_import, allocator), - name: Atom::from("then"), + name: Ident::from("then"), optional: false, source_span: None, }, @@ -296,7 +296,7 @@ fn guarded_expression<'a>( expr: OutputExpression<'a>, ) -> OutputExpression<'a> { let guard_var = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(guard), source_span: None }, + ReadVarExpr { name: Ident::from(guard), source_span: None }, allocator, )); @@ -349,12 +349,12 @@ fn import_expr<'a>(allocator: &'a Allocator, identifier: &'static str) -> Output ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(identifier), + name: Ident::from(identifier), optional: false, source_span: None, }, @@ -373,7 +373,7 @@ fn literal_null<'a>(allocator: &'a Allocator) -> OutputExpression<'a> { /// Creates a string literal. fn literal_string<'a>(allocator: &'a Allocator, value: &'static str) -> OutputExpression<'a> { OutputExpression::Literal(Box::new_in( - LiteralExpr { value: LiteralValue::String(Atom::from(value)), source_span: None }, + LiteralExpr { value: LiteralValue::String(Ident::from(value)), source_span: None }, allocator, )) } diff --git a/crates/oxc_angular_compiler/src/class_metadata/metadata.rs b/crates/oxc_angular_compiler/src/class_metadata/metadata.rs index 6ae215b87..46535ea0f 100644 --- a/crates/oxc_angular_compiler/src/class_metadata/metadata.rs +++ b/crates/oxc_angular_compiler/src/class_metadata/metadata.rs @@ -3,7 +3,7 @@ //! Ported from Angular's `render3/r3_class_metadata_compiler.ts` and `view/api.ts`. use crate::output::ast::OutputExpression; -use oxc_span::Atom; +use oxc_span::Ident; /// Metadata of a class which captures the original Angular decorators. /// @@ -35,10 +35,10 @@ pub struct R3ClassMetadata<'a> { #[derive(Debug)] pub struct R3DeferPerComponentDependency<'a> { /// The symbol name of the dependency. - pub symbol_name: Atom<'a>, + pub symbol_name: Ident<'a>, /// The import path for the dependency. - pub import_path: Atom<'a>, + pub import_path: Ident<'a>, /// Whether this is a default import. pub is_default_import: bool, diff --git a/crates/oxc_angular_compiler/src/component/decorator.rs b/crates/oxc_angular_compiler/src/component/decorator.rs index 9287f0fd4..a7ede7ccf 100644 --- a/crates/oxc_angular_compiler/src/component/decorator.rs +++ b/crates/oxc_angular_compiler/src/component/decorator.rs @@ -8,7 +8,7 @@ use oxc_ast::ast::{ Argument, ArrayExpressionElement, Class, ClassElement, Decorator, Expression, MethodDefinitionKind, ObjectPropertyKind, PropertyKey, }; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use super::dependency::R3DependencyMetadata; use super::metadata::{ @@ -52,7 +52,7 @@ pub fn extract_component_metadata<'a>( import_map: &ImportMap<'a>, ) -> Option> { // Get the class name - let class_name: Atom<'a> = class.id.as_ref()?.name.clone().into(); + let class_name: Ident<'a> = class.id.as_ref()?.name.clone().into(); let class_span = class.span; // Find the @Component decorator @@ -138,7 +138,7 @@ pub fn extract_component_metadata<'a>( for part in export_as.as_str().split(',') { let trimmed = part.trim(); if !trimmed.is_empty() { - metadata.export_as.push(Atom::from(allocator.alloc_str(trimmed))); + metadata.export_as.push(Ident::from(allocator.alloc_str(trimmed))); } } } @@ -196,7 +196,8 @@ pub fn extract_component_metadata<'a>( // Add @HostBinding properties // Wrap with brackets: "class.active" -> "[class.active]" for (host_prop, class_prop) in host_bindings { - let wrapped_key = Atom::from(allocator.alloc_str(&format!("[{}]", host_prop.as_str()))); + let wrapped_key = + Ident::from(allocator.alloc_str(&format!("[{}]", host_prop.as_str()))); host.properties.push((wrapped_key, class_prop)); } @@ -206,15 +207,15 @@ pub fn extract_component_metadata<'a>( for (event_name, method_name, args) in host_listeners { // Wrap event name: "click" -> "(click)" let wrapped_key = - Atom::from(allocator.alloc_str(&format!("({})", event_name.as_str()))); + Ident::from(allocator.alloc_str(&format!("({})", event_name.as_str()))); // Build method expression with args: "handleClick" + ["$event"] -> "handleClick($event)" let method_expr = if args.is_empty() { - Atom::from(allocator.alloc_str(&format!("{}()", method_name.as_str()))) + Ident::from(allocator.alloc_str(&format!("{}()", method_name.as_str()))) } else { let args_str: String = args.iter().map(|a| a.as_str()).collect::>().join(","); - Atom::from(allocator.alloc_str(&format!("{}({})", method_name.as_str(), args_str))) + Ident::from(allocator.alloc_str(&format!("{}({})", method_name.as_str(), args_str))) }; host.listeners.push((wrapped_key, method_expr)); @@ -291,7 +292,7 @@ fn populate_declarations_from_imports<'a>( allocator, import_name.clone(), // Use a placeholder selector - the actual selector isn't used for dependencies array - Atom::from("*"), + Ident::from("*"), false, // is_component - unknown at this point ); @@ -334,23 +335,23 @@ fn is_component_call(callee: &Expression<'_>) -> bool { } /// Get the name of a property key as a string. -fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { +fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { match key { PropertyKey::StaticIdentifier(id) => Some(id.name.clone().into()), - PropertyKey::StringLiteral(lit) => Some(lit.value.clone()), + PropertyKey::StringLiteral(lit) => Some(lit.value.clone().into()), _ => None, } } /// Extract a string value from an expression. -fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { +fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { match expr { - Expression::StringLiteral(lit) => Some(lit.value.clone()), + Expression::StringLiteral(lit) => Some(lit.value.clone().into()), Expression::TemplateLiteral(tpl) if tpl.expressions.is_empty() => { // Simple template literal with no expressions: `template string` // Use cooked value to properly interpret escape sequences (\n -> newline) // Angular evaluates template literals, so we need cooked, not raw - tpl.quasis.first().and_then(|q| q.value.cooked.clone()) + tpl.quasis.first().and_then(|q| q.value.cooked.clone().map(Into::into)) } _ => None, } @@ -359,7 +360,7 @@ fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { /// Extract a boolean value from an expression. fn extract_boolean_value(expr: &Expression<'_>) -> Option { match expr { - Expression::BooleanLiteral(lit) => Some(lit.value), + Expression::BooleanLiteral(lit) => Some(lit.value.into()), _ => None, } } @@ -367,7 +368,7 @@ fn extract_boolean_value(expr: &Expression<'_>) -> Option { fn extract_string_array<'a>( allocator: &'a Allocator, expr: &Expression<'a>, -) -> Option>> { +) -> Option>> { let Expression::ArrayExpression(arr) = expr else { return None; }; @@ -375,13 +376,13 @@ fn extract_string_array<'a>( let mut result = Vec::new_in(allocator); for element in &arr.elements { if let ArrayExpressionElement::StringLiteral(lit) = element { - result.push(lit.value.clone()); + result.push(lit.value.clone().into()); } else if let ArrayExpressionElement::TemplateLiteral(tpl) = element { if tpl.expressions.is_empty() { // Use cooked value to properly interpret escape sequences if let Some(quasi) = tpl.quasis.first() { if let Some(cooked) = &quasi.value.cooked { - result.push(cooked.clone()); + result.push(cooked.clone().into()); } } } @@ -395,7 +396,7 @@ fn extract_string_array<'a>( fn extract_identifier_array<'a>( allocator: &'a Allocator, expr: &Expression<'a>, -) -> Vec<'a, Atom<'a>> { +) -> Vec<'a, Ident<'a>> { let mut result = Vec::new_in(allocator); let Expression::ArrayExpression(arr) = expr else { @@ -562,7 +563,7 @@ fn extract_single_host_directive<'a>( match element { // Simple identifier: TooltipDirective ArrayExpressionElement::Identifier(id) => { - let name: Atom<'a> = id.name.clone().into(); + let name: Ident<'a> = id.name.clone().into(); let mut meta = HostDirectiveMetadata::new(allocator, name.clone()); // Look up the source module from the import map if let Some(import_info) = import_map.get(&name) { @@ -573,7 +574,7 @@ fn extract_single_host_directive<'a>( // Object expression: { directive: ColorDirective, inputs: [...], outputs: [...] } ArrayExpressionElement::ObjectExpression(obj) => { - let mut directive_name: Option> = None; + let mut directive_name: Option> = None; let mut inputs = Vec::new_in(allocator); let mut outputs = Vec::new_in(allocator); let mut is_forward_reference = false; @@ -643,7 +644,7 @@ fn extract_single_host_directive<'a>( /// Extract a directive reference from an expression. /// /// Returns the directive class name and whether it's a forward reference. -fn extract_directive_reference<'a>(expr: &Expression<'a>) -> (Option>, bool) { +fn extract_directive_reference<'a>(expr: &Expression<'a>) -> (Option>, bool) { match expr { // Simple identifier: ColorDirective Expression::Identifier(id) => (Some(id.name.clone().into()), false), @@ -672,7 +673,7 @@ fn is_forward_ref_call(callee: &Expression<'_>) -> bool { /// Extract the directive name from a forwardRef argument. /// /// Handles: `forwardRef(() => MyDirective)` -fn extract_forward_ref_directive_name<'a>(arg: Option<&Argument<'a>>) -> Option> { +fn extract_forward_ref_directive_name<'a>(arg: Option<&Argument<'a>>) -> Option> { let arg = arg?; match arg { // forwardRef(() => MyDirective) @@ -712,7 +713,7 @@ fn extract_forward_ref_directive_name<'a>(arg: Option<&Argument<'a>>) -> Option< fn extract_io_mappings<'a>( allocator: &'a Allocator, expr: &Expression<'a>, -) -> Vec<'a, (Atom<'a>, Atom<'a>)> { +) -> Vec<'a, (Ident<'a>, Ident<'a>)> { let mut result = Vec::new_in(allocator); let Expression::ArrayExpression(arr) = expr else { @@ -737,7 +738,7 @@ fn extract_io_mappings<'a>( fn parse_mapping_element<'a>( allocator: &'a Allocator, element: &ArrayExpressionElement<'a>, -) -> Option<(Atom<'a>, Atom<'a>)> { +) -> Option<(Ident<'a>, Ident<'a>)> { match element { // Simple string: "color" - same public and internal name ArrayExpressionElement::StringLiteral(lit) => { @@ -748,12 +749,12 @@ fn parse_mapping_element<'a>( let internal_name = value[..colon_pos].trim(); let public_name = value[colon_pos + 1..].trim(); Some(( - Atom::from(allocator.alloc_str(public_name)), - Atom::from(allocator.alloc_str(internal_name)), + Ident::from(allocator.alloc_str(public_name)), + Ident::from(allocator.alloc_str(internal_name)), )) } else { // Same name for both - Some((lit.value.clone(), lit.value.clone())) + Some((lit.value.clone().into(), lit.value.clone().into())) } } @@ -762,11 +763,15 @@ fn parse_mapping_element<'a>( let elements = &arr.elements; if elements.len() >= 2 { let internal = match elements.first() { - Some(ArrayExpressionElement::StringLiteral(lit)) => Some(lit.value.clone()), + Some(ArrayExpressionElement::StringLiteral(lit)) => { + Some(lit.value.clone().into()) + } _ => None, }; let public = match elements.get(1) { - Some(ArrayExpressionElement::StringLiteral(lit)) => Some(lit.value.clone()), + Some(ArrayExpressionElement::StringLiteral(lit)) => { + Some(lit.value.clone().into()) + } _ => None, }; if let (Some(internal_name), Some(public_name)) = (internal, public) { @@ -898,8 +903,8 @@ fn extract_param_dependency<'a>( let mut skip_self = false; let mut self_ = false; let mut host = false; - let mut inject_token: Option> = None; - let mut attribute_name: Option> = None; + let mut inject_token: Option> = None; + let mut attribute_name: Option> = None; for decorator in ¶m.decorators { if let Some(name) = get_decorator_name(&decorator.expression) { @@ -920,7 +925,7 @@ fn extract_param_dependency<'a>( // @Attribute('attrName') - extract the attribute name if let Expression::CallExpression(call) = &decorator.expression { if let Some(Argument::StringLiteral(s)) = call.arguments.first() { - attribute_name = Some(s.value.clone()); + attribute_name = Some(s.value.clone().into()); } } } @@ -965,7 +970,7 @@ fn extract_param_dependency<'a>( } /// Get the name of a decorator from its expression. -fn get_decorator_name<'a>(expr: &'a Expression<'a>) -> Option> { +fn get_decorator_name<'a>(expr: &'a Expression<'a>) -> Option> { match expr { // @Optional Expression::Identifier(id) => Some(id.name.clone().into()), @@ -982,7 +987,7 @@ fn get_decorator_name<'a>(expr: &'a Expression<'a>) -> Option> { } /// Extract the injection token from an @Inject decorator argument. -fn extract_inject_token<'a>(arg: &'a Argument<'a>) -> Option> { +fn extract_inject_token<'a>(arg: &'a Argument<'a>) -> Option> { match arg { Argument::Identifier(id) => Some(id.name.clone().into()), _ => { @@ -997,7 +1002,7 @@ fn extract_inject_token<'a>(arg: &'a Argument<'a>) -> Option> { } /// Extract the injection token from a parameter's type annotation. -fn extract_param_token<'a>(param: &'a oxc_ast::ast::FormalParameter<'a>) -> Option> { +fn extract_param_token<'a>(param: &'a oxc_ast::ast::FormalParameter<'a>) -> Option> { // Get the type annotation (directly on FormalParameter) let type_annotation = param.type_annotation.as_ref()?; let ts_type = &type_annotation.type_annotation; @@ -1089,6 +1094,33 @@ pub fn collect_member_decorator_spans(class: &Class<'_>, spans: &mut std::vec::V } } +/// Collect ALL decorator spans from class members (properties, methods, accessors), +/// regardless of whether they are Angular-specific or not. +/// +/// This is used when lowering a class that has Angular decorators: since the class +/// declaration is converted to a class expression, ALL member decorators must be +/// removed (decorators are not valid on class expressions in TypeScript). +pub fn collect_all_member_decorator_spans(class: &Class<'_>, spans: &mut std::vec::Vec) { + for element in &class.body.body { + let decorators = match element { + ClassElement::PropertyDefinition(prop) => &prop.decorators, + ClassElement::MethodDefinition(method) => { + // Skip constructor - it's handled separately + if method.kind == MethodDefinitionKind::Constructor { + continue; + } + &method.decorators + } + ClassElement::AccessorProperty(accessor) => &accessor.decorators, + _ => continue, + }; + + for decorator in decorators { + spans.push(decorator.span); + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/oxc_angular_compiler/src/component/definition.rs b/crates/oxc_angular_compiler/src/component/definition.rs index b84db57da..8a6418e93 100644 --- a/crates/oxc_angular_compiler/src/component/definition.rs +++ b/crates/oxc_angular_compiler/src/component/definition.rs @@ -13,7 +13,7 @@ //! - Inject dependencies use oxc_allocator::{Allocator, Box, FromIn, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::r3::Identifiers; @@ -23,6 +23,7 @@ use super::metadata::{ ViewEncapsulation, }; use super::namespace_registry::NamespaceRegistry; +use super::transform::TransformOptions; use crate::directive::{ create_host_directive_mappings_array, create_inputs_literal, create_outputs_literal, }; @@ -62,6 +63,7 @@ pub struct ComponentDefinitions<'a> { pub fn generate_component_definitions<'a>( allocator: &'a Allocator, metadata: &ComponentMetadata<'a>, + options: &TransformOptions, job: &mut ComponentCompilationJob<'a>, template_fn: FunctionExpr<'a>, host_binding_result: Option>, @@ -79,6 +81,7 @@ pub fn generate_component_definitions<'a>( let cmp_definition = generate_cmp_definition( allocator, metadata, + options, job, template_fn, host_binding_result, @@ -109,6 +112,7 @@ pub fn generate_component_definitions<'a>( fn generate_cmp_definition<'a>( allocator: &'a Allocator, metadata: &ComponentMetadata<'a>, + options: &TransformOptions, job: &mut ComponentCompilationJob<'a>, template_fn: FunctionExpr<'a>, host_binding_result: Option>, @@ -125,7 +129,7 @@ fn generate_cmp_definition<'a>( // 1. type: ComponentClass entries.push(LiteralMapEntry { - key: Atom::from("type"), + key: Ident::from("type"), value: OutputExpression::ReadVar(Box::new_in( ReadVarExpr { name: metadata.class_name.clone(), source_span: None }, allocator, @@ -138,10 +142,10 @@ fn generate_cmp_definition<'a>( // See: packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts:264-290 // and packages/compiler/src/schema/dom_element_schema_registry.ts:463 let selector_value = - metadata.selector.as_ref().map_or_else(|| Atom::from("ng-component"), |s| s.clone()); + metadata.selector.as_ref().map_or_else(|| Ident::from("ng-component"), |s| s.clone()); let selector_entries = parse_selector_to_array(allocator, &selector_value); entries.push(LiteralMapEntry { - key: Atom::from("selectors"), + key: Ident::from("selectors"), value: selector_entries, quoted: false, }); @@ -152,7 +156,7 @@ fn generate_cmp_definition<'a>( // Per Angular compiler.ts lines 57-63 (baseDirectiveFields) if let Some(content_queries) = content_queries_fn { entries.push(LiteralMapEntry { - key: Atom::from("contentQueries"), + key: Ident::from("contentQueries"), value: content_queries, quoted: false, }); @@ -164,7 +168,7 @@ fn generate_cmp_definition<'a>( // Per Angular compiler.ts lines 65-70 (baseDirectiveFields) if let Some(view_query) = view_query_fn { entries.push(LiteralMapEntry { - key: Atom::from("viewQuery"), + key: Ident::from("viewQuery"), value: view_query, quoted: false, }); @@ -178,7 +182,7 @@ fn generate_cmp_definition<'a>( // 5. hostAttrs: [...] - static host attributes if let Some(host_attrs) = host_result.host_attrs { entries.push(LiteralMapEntry { - key: Atom::from("hostAttrs"), + key: Ident::from("hostAttrs"), value: host_attrs, quoted: false, }); @@ -187,7 +191,7 @@ fn generate_cmp_definition<'a>( // 6. hostVars: number - only if > 0 if let Some(host_vars) = host_result.host_vars { entries.push(LiteralMapEntry { - key: Atom::from("hostVars"), + key: Ident::from("hostVars"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::Number(host_vars as f64), @@ -202,7 +206,7 @@ fn generate_cmp_definition<'a>( // 7. hostBindings: function(rf, ctx) { ... } (if any) if let Some(host_fn) = host_result.host_binding_fn { entries.push(LiteralMapEntry { - key: Atom::from("hostBindings"), + key: Ident::from("hostBindings"), value: OutputExpression::Function(Box::new_in(host_fn, allocator)), quoted: false, }); @@ -214,7 +218,7 @@ fn generate_cmp_definition<'a>( if !metadata.inputs.is_empty() { if let Some(inputs_expr) = create_inputs_literal(allocator, &metadata.inputs) { entries.push(LiteralMapEntry { - key: Atom::from("inputs"), + key: Ident::from("inputs"), value: inputs_expr, quoted: false, }); @@ -226,7 +230,7 @@ fn generate_cmp_definition<'a>( if !metadata.outputs.is_empty() { if let Some(outputs_expr) = create_outputs_literal(allocator, &metadata.outputs) { entries.push(LiteralMapEntry { - key: Atom::from("outputs"), + key: Ident::from("outputs"), value: outputs_expr, quoted: false, }); @@ -244,7 +248,7 @@ fn generate_cmp_definition<'a>( ))); } entries.push(LiteralMapEntry { - key: Atom::from("exportAs"), + key: Ident::from("exportAs"), value: OutputExpression::LiteralArray(Box::new_in( LiteralArrayExpr { entries: export_items, source_span: None }, allocator, @@ -257,7 +261,7 @@ fn generate_cmp_definition<'a>( // Per Angular compiler.ts lines 96-98 (baseDirectiveFields) if !metadata.standalone { entries.push(LiteralMapEntry { - key: Atom::from("standalone"), + key: Ident::from("standalone"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::Boolean(false), source_span: None }, allocator, @@ -270,7 +274,7 @@ fn generate_cmp_definition<'a>( // Per Angular compiler.ts lines 99-101 (baseDirectiveFields) if metadata.is_signal { entries.push(LiteralMapEntry { - key: Atom::from("signals"), + key: Ident::from("signals"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::Boolean(true), source_span: None }, allocator, @@ -287,7 +291,7 @@ fn generate_cmp_definition<'a>( // See: packages/compiler/src/render3/view/compiler.ts:119-161 if let Some(features) = generate_features_array(allocator, metadata, namespace_registry) { entries.push(LiteralMapEntry { - key: Atom::from("features"), + key: Ident::from("features"), value: features, quoted: false, }); @@ -303,14 +307,14 @@ fn generate_cmp_definition<'a>( // The attrs_ref is pre-pooled BEFORE template compilation to ensure correct constant ordering. // TypeScript Angular adds attrs to the pool BEFORE template ingestion/compilation. if let Some(attrs) = attrs_ref { - entries.push(LiteralMapEntry { key: Atom::from("attrs"), value: attrs, quoted: false }); + entries.push(LiteralMapEntry { key: Ident::from("attrs"), value: attrs, quoted: false }); } // 15. ngContentSelectors: [...] - content projection selectors // Per Angular compiler.ts lines 254-256 if let Some(content_selectors) = job.content_selectors.take() { entries.push(LiteralMapEntry { - key: Atom::from("ngContentSelectors"), + key: Ident::from("ngContentSelectors"), value: content_selectors, quoted: false, }); @@ -320,7 +324,7 @@ fn generate_cmp_definition<'a>( // Per Angular compiler.ts line 258 let decls = job.root.decl_count.unwrap_or(0); entries.push(LiteralMapEntry { - key: Atom::from("decls"), + key: Ident::from("decls"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::Number(decls as f64), source_span: None }, allocator, @@ -332,7 +336,7 @@ fn generate_cmp_definition<'a>( // Per Angular compiler.ts line 259 let vars = job.root.vars.unwrap_or(0); entries.push(LiteralMapEntry { - key: Atom::from("vars"), + key: Ident::from("vars"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::Number(vars as f64), source_span: None }, allocator, @@ -391,7 +395,7 @@ fn generate_cmp_definition<'a>( }; entries.push(LiteralMapEntry { - key: Atom::from("consts"), + key: Ident::from("consts"), value: consts_value, quoted: false, }); @@ -400,7 +404,7 @@ fn generate_cmp_definition<'a>( // 19. template: function(rf, ctx) { ... } // Per Angular compiler.ts line 270 entries.push(LiteralMapEntry { - key: Atom::from("template"), + key: Ident::from("template"), value: OutputExpression::Function(Box::new_in(template_fn, allocator)), quoted: false, }); @@ -411,7 +415,7 @@ fn generate_cmp_definition<'a>( generate_dependencies_expression(allocator, metadata, namespace_registry) { entries.push(LiteralMapEntry { - key: Atom::from("dependencies"), + key: Ident::from("dependencies"), value: dependencies, quoted: false, }); @@ -435,23 +439,17 @@ fn generate_cmp_definition<'a>( if !metadata.styles.is_empty() { let mut style_entries: OxcVec<'a, OutputExpression<'a>> = OxcVec::new_in(allocator); for style in &metadata.styles { - // Apply CSS scoping for Emulated encapsulation - let style_value = if metadata.encapsulation == ViewEncapsulation::Emulated { - // Use shim_css_text with %COMP% placeholder - // Angular's runtime will replace %COMP% with the actual component ID - let scoped = crate::styles::shim_css_text(style.as_str(), content_attr, host_attr); - // Skip empty styles - if scoped.trim().is_empty() { - continue; - } - Atom::from_in(scoped.as_str(), allocator) - } else { - // For None/ShadowDom, use styles as-is - if style.trim().is_empty() { - continue; - } - style.clone() - }; + let style = crate::styles::finalize_component_style( + style.as_str(), + metadata.encapsulation == ViewEncapsulation::Emulated, + content_attr, + host_attr, + options.minify_component_styles, + ); + if style.trim().is_empty() { + continue; + } + let style_value = Ident::from_in(style.as_str(), allocator); style_entries.push(OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::String(style_value), source_span: None }, @@ -462,7 +460,7 @@ fn generate_cmp_definition<'a>( if !style_entries.is_empty() { has_styles = true; entries.push(LiteralMapEntry { - key: Atom::from("styles"), + key: Ident::from("styles"), value: OutputExpression::LiteralArray(Box::new_in( LiteralArrayExpr { entries: style_entries, source_span: None }, allocator, @@ -488,7 +486,7 @@ fn generate_cmp_definition<'a>( ViewEncapsulation::ShadowDom => 3, }; entries.push(LiteralMapEntry { - key: Atom::from("encapsulation"), + key: Ident::from("encapsulation"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::Number(encapsulation_value as f64), @@ -507,14 +505,14 @@ fn generate_cmp_definition<'a>( let mut data_entries: OxcVec<'a, LiteralMapEntry<'a>> = OxcVec::with_capacity_in(1, allocator); data_entries.push(LiteralMapEntry { - key: Atom::from("animation"), + key: Ident::from("animation"), // Use the full animations expression directly value: animations.clone_in(allocator), quoted: false, }); entries.push(LiteralMapEntry { - key: Atom::from("data"), + key: Ident::from("data"), value: OutputExpression::LiteralMap(Box::new_in( LiteralMapExpr { entries: data_entries, source_span: None }, allocator, @@ -537,7 +535,7 @@ fn generate_cmp_definition<'a>( // ReadPropExpr { receiver: ReadVarExpr("ChangeDetectionStrategy"), name: "OnPush" } let change_detection_strategy_expr = OutputExpression::ReadVar(Box::new_in( ReadVarExpr { - name: Atom::from(Identifiers::CHANGE_DETECTION_STRATEGY), + name: Ident::from(Identifiers::CHANGE_DETECTION_STRATEGY), source_span: None, }, allocator, @@ -545,14 +543,14 @@ fn generate_cmp_definition<'a>( let strategy_value_expr = OutputExpression::ReadProp(Box::new_in( ReadPropExpr { receiver: Box::new_in(change_detection_strategy_expr, allocator), - name: Atom::from(strategy_name), + name: Ident::from(strategy_name), optional: false, source_span: None, }, allocator, )); entries.push(LiteralMapEntry { - key: Atom::from("changeDetection"), + key: Ident::from("changeDetection"), value: strategy_value_expr, quoted: false, }); @@ -635,11 +633,11 @@ fn generate_constructor_factory<'a>( ) -> OutputExpression<'a> { // Function name: ComponentClass_Factory let fn_name_string = format!("{}_Factory", metadata.class_name); - let fn_name = Atom::from_in(fn_name_string.as_str(), allocator); + let fn_name = Ident::from_in(fn_name_string.as_str(), allocator); // Parameter: __ngFactoryType__ (type override for inheritance/testing) let mut params: OxcVec<'a, FnParam<'a>> = OxcVec::new_in(allocator); - params.push(FnParam { name: Atom::from("__ngFactoryType__") }); + params.push(FnParam { name: Ident::from("__ngFactoryType__") }); // Body: return new (__ngFactoryType__ || ComponentClass)(deps...); let mut statements: OxcVec<'a, OutputStatement<'a>> = OxcVec::new_in(allocator); @@ -650,7 +648,7 @@ fn generate_constructor_factory<'a>( operator: crate::output::ast::BinaryOperator::Or, lhs: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("__ngFactoryType__"), source_span: None }, + ReadVarExpr { name: Ident::from("__ngFactoryType__"), source_span: None }, allocator, )), allocator, @@ -722,15 +720,15 @@ fn generate_inherited_factory<'a>( StmtModifier, }; - let factory_type_param = Atom::from("__ngFactoryType__"); + let factory_type_param = Ident::from("__ngFactoryType__"); // Create base factory variable name: ɵComponentClass_BaseFactory let base_factory_var_name = - Atom::from_in(format!("ɵ{}_BaseFactory", metadata.class_name).as_str(), allocator); + Ident::from_in(format!("ɵ{}_BaseFactory", metadata.class_name).as_str(), allocator); // Function name: ComponentClass_Factory let fn_name_string = format!("{}_Factory", metadata.class_name); - let fn_name = Atom::from_in(fn_name_string.as_str(), allocator); + let fn_name = Ident::from_in(fn_name_string.as_str(), allocator); // Create ɵɵgetInheritedFactory(ComponentClass) call let get_inherited_factory_call = { @@ -738,12 +736,12 @@ fn generate_inherited_factory<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::GET_INHERITED_FACTORY), + name: Ident::from(Identifiers::GET_INHERITED_FACTORY), optional: false, source_span: None, }, @@ -915,12 +913,12 @@ fn create_define_component_call<'a>( crate::output::ast::ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::DEFINE_COMPONENT), + name: Ident::from(Identifiers::DEFINE_COMPONENT), optional: false, source_span: None, }, @@ -955,7 +953,7 @@ fn create_define_component_call<'a>( /// Ported from Angular's `parseSelectorToR3Selector` in `core.ts`. fn parse_selector_to_array<'a>( allocator: &'a Allocator, - selector: &Atom<'a>, + selector: &Ident<'a>, ) -> OutputExpression<'a> { let r3_selectors = parse_selector_to_r3_selector(selector.as_str()); @@ -1196,28 +1194,28 @@ fn create_host_directives_arg<'a>( // directive: DirectiveClass (or i1.DirectiveClass for imports) entries.push(LiteralMapEntry { - key: Atom::from("directive"), + key: Ident::from("directive"), value: directive_ref, quoted: false, }); - // inputs: ['publicName', 'internalName', ...] + // inputs: ['internalName', 'publicName', ...] if !directive.inputs.is_empty() { let inputs_array = create_host_directive_mappings_array(allocator, &directive.inputs); entries.push(LiteralMapEntry { - key: Atom::from("inputs"), + key: Ident::from("inputs"), value: inputs_array, quoted: false, }); } - // outputs: ['publicName', 'internalName', ...] + // outputs: ['internalName', 'publicName', ...] if !directive.outputs.is_empty() { let outputs_array = create_host_directive_mappings_array(allocator, &directive.outputs); entries.push(LiteralMapEntry { - key: Atom::from("outputs"), + key: Ident::from("outputs"), value: outputs_array, quoted: false, }); @@ -1307,12 +1305,12 @@ fn create_angular_fn_ref<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(fn_name), + name: Ident::from(fn_name), optional: false, source_span: None, }, @@ -1473,7 +1471,7 @@ fn compile_declaration_list<'a>( OutputExpression::ReadProp(Box::new_in( ReadPropExpr { receiver: Box::new_in(list, allocator), - name: Atom::from("map"), + name: Ident::from("map"), optional: false, source_span: None, }, @@ -1570,7 +1568,7 @@ pub fn const_value_to_expression<'a>( crate::output::ast::ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, @@ -1595,8 +1593,8 @@ mod tests { fn create_test_metadata<'a>(allocator: &'a Allocator) -> ComponentMetadata<'a> { let mut metadata = - ComponentMetadata::new(allocator, Atom::from("TestComponent"), Span::empty(0), true); - metadata.selector = Some(Atom::from("app-test")); + ComponentMetadata::new(allocator, Ident::from("TestComponent"), Span::empty(0), true); + metadata.selector = Some(Ident::from("app-test")); metadata } @@ -1619,7 +1617,7 @@ mod tests { #[test] fn test_parse_element_selector() { let allocator = Allocator::default(); - let result = parse_selector_to_array(&allocator, &Atom::from("app-root")); + let result = parse_selector_to_array(&allocator, &Ident::from("app-root")); let emitter = JsEmitter::new(); let js = emitter.emit_expression(&result); @@ -1630,7 +1628,7 @@ mod tests { #[test] fn test_parse_attribute_selector() { let allocator = Allocator::default(); - let result = parse_selector_to_array(&allocator, &Atom::from("[type=button]")); + let result = parse_selector_to_array(&allocator, &Ident::from("[type=button]")); let emitter = JsEmitter::new(); let js = emitter.emit_expression(&result); @@ -1651,7 +1649,7 @@ mod tests { let allocator = Allocator::default(); let mut metadata = ComponentMetadata::new( &allocator, - Atom::from("EmptyOutletComponent"), + Ident::from("EmptyOutletComponent"), Span::empty(0), true, ); @@ -1659,7 +1657,7 @@ mod tests { metadata.selector = None; let selector_value = - metadata.selector.as_ref().map_or_else(|| Atom::from("ng-component"), |s| s.clone()); + metadata.selector.as_ref().map_or_else(|| Ident::from("ng-component"), |s| s.clone()); let result = parse_selector_to_array(&allocator, &selector_value); let emitter = JsEmitter::new(); @@ -1673,11 +1671,11 @@ mod tests { fn test_explicit_selector_overrides_default() { let allocator = Allocator::default(); let mut metadata = - ComponentMetadata::new(&allocator, Atom::from("TestComponent"), Span::empty(0), true); - metadata.selector = Some(Atom::from("app-test")); + ComponentMetadata::new(&allocator, Ident::from("TestComponent"), Span::empty(0), true); + metadata.selector = Some(Ident::from("app-test")); let selector_value = - metadata.selector.as_ref().map_or_else(|| Atom::from("ng-component"), |s| s.clone()); + metadata.selector.as_ref().map_or_else(|| Ident::from("ng-component"), |s| s.clone()); let result = parse_selector_to_array(&allocator, &selector_value); let emitter = JsEmitter::new(); @@ -1711,7 +1709,7 @@ mod tests { OxcVec::with_capacity_in(names.len(), allocator); for name in names { entries.push(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(*name), source_span: None }, + ReadVarExpr { name: Ident::from(*name), source_span: None }, allocator, ))); } @@ -1819,8 +1817,8 @@ mod tests { let allocator = Allocator::default(); let mut metadata = create_test_metadata(&allocator); let mut namespace_registry = NamespaceRegistry::new(&allocator); - metadata.external_styles.push(Atom::from("./styles.css")); - metadata.external_styles.push(Atom::from("./theme.css")); + metadata.external_styles.push(Ident::from("./styles.css")); + metadata.external_styles.push(Ident::from("./theme.css")); let result = generate_features_array(&allocator, &metadata, &mut namespace_registry).unwrap(); @@ -1839,7 +1837,7 @@ mod tests { let mut metadata = create_test_metadata(&allocator); let mut namespace_registry = NamespaceRegistry::new(&allocator); - let directive = HostDirectiveMetadata::new(&allocator, Atom::from("MyDirective")); + let directive = HostDirectiveMetadata::new(&allocator, Ident::from("MyDirective")); metadata.host_directives.push(directive); let result = @@ -1858,9 +1856,9 @@ mod tests { let mut metadata = create_test_metadata(&allocator); let mut namespace_registry = NamespaceRegistry::new(&allocator); - let mut directive = HostDirectiveMetadata::new(&allocator, Atom::from("MyDirective")); - directive.inputs.push((Atom::from("publicInput"), Atom::from("internalInput"))); - directive.outputs.push((Atom::from("publicOutput"), Atom::from("internalOutput"))); + let mut directive = HostDirectiveMetadata::new(&allocator, Ident::from("MyDirective")); + directive.inputs.push((Ident::from("publicInput"), Ident::from("internalInput"))); + directive.outputs.push((Ident::from("publicOutput"), Ident::from("internalOutput"))); metadata.host_directives.push(directive); let result = @@ -1883,7 +1881,7 @@ mod tests { let mut metadata = create_test_metadata(&allocator); let mut namespace_registry = NamespaceRegistry::new(&allocator); - let mut directive = HostDirectiveMetadata::new(&allocator, Atom::from("ForwardRefDir")); + let mut directive = HostDirectiveMetadata::new(&allocator, Ident::from("ForwardRefDir")); directive.is_forward_reference = true; metadata.host_directives.push(directive); @@ -1906,8 +1904,8 @@ mod tests { let mut namespace_registry = NamespaceRegistry::new(&allocator); // Create directive with source_module (imported from another module) - let directive = HostDirectiveMetadata::new(&allocator, Atom::from("ExternalDirective")) - .with_source_module(Atom::from("@angular/external")); + let directive = HostDirectiveMetadata::new(&allocator, Ident::from("ExternalDirective")) + .with_source_module(Ident::from("@angular/external")); metadata.host_directives.push(directive); let result = @@ -1934,13 +1932,13 @@ mod tests { let mut namespace_registry = NamespaceRegistry::new(&allocator); // Local directive (no source_module) - let local_directive = HostDirectiveMetadata::new(&allocator, Atom::from("LocalDirective")); + let local_directive = HostDirectiveMetadata::new(&allocator, Ident::from("LocalDirective")); metadata.host_directives.push(local_directive); // Imported directive (with source_module) let imported_directive = - HostDirectiveMetadata::new(&allocator, Atom::from("ImportedDirective")) - .with_source_module(Atom::from("@angular/library")); + HostDirectiveMetadata::new(&allocator, Ident::from("ImportedDirective")) + .with_source_module(Ident::from("@angular/library")); metadata.host_directives.push(imported_directive); let result = @@ -2009,16 +2007,16 @@ mod tests { // Add directives using the TemplateDependency from metadata module let dir = crate::component::metadata::TemplateDependency::directive( &allocator, - Atom::from("MyDirective"), - Atom::from("[myDir]"), + Ident::from("MyDirective"), + Ident::from("[myDir]"), false, ); metadata.declarations.push(dir); let pipe = crate::component::metadata::TemplateDependency::pipe( &allocator, - Atom::from("MyPipe"), - Atom::from("myPipe"), + Ident::from("MyPipe"), + Ident::from("myPipe"), ); metadata.declarations.push(pipe); @@ -2045,8 +2043,8 @@ mod tests { let dir = crate::component::metadata::TemplateDependency::directive( &allocator, - Atom::from("ForwardDir"), - Atom::from("[fwd]"), + Ident::from("ForwardDir"), + Ident::from("[fwd]"), false, ); metadata.declarations.push(dir); @@ -2074,8 +2072,8 @@ mod tests { let dir = crate::component::metadata::TemplateDependency::directive( &allocator, - Atom::from("JitDir"), - Atom::from("[jit]"), + Ident::from("JitDir"), + Ident::from("[jit]"), false, ); metadata.declarations.push(dir); @@ -2124,7 +2122,7 @@ mod tests { metadata.declaration_list_emit_mode = DeclarationListEmitMode::RuntimeResolved; // Test with a variable reference as raw_imports metadata.raw_imports = Some(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("IMPORTS"), source_span: None }, + ReadVarExpr { name: Ident::from("IMPORTS"), source_span: None }, &allocator, ))); @@ -2153,15 +2151,15 @@ mod tests { // Test with an array literal as raw_imports (like imports: [A, B, C]) let mut entries: OxcVec<'_, OutputExpression<'_>> = OxcVec::with_capacity_in(3, &allocator); entries.push(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("A"), source_span: None }, + ReadVarExpr { name: Ident::from("A"), source_span: None }, &allocator, ))); entries.push(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("B"), source_span: None }, + ReadVarExpr { name: Ident::from("B"), source_span: None }, &allocator, ))); entries.push(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("C"), source_span: None }, + ReadVarExpr { name: Ident::from("C"), source_span: None }, &allocator, ))); metadata.raw_imports = Some(OutputExpression::LiteralArray(Box::new_in( @@ -2189,11 +2187,11 @@ mod tests { // Add an imported directive with source_module let dir = crate::component::metadata::TemplateDependency::directive( &allocator, - Atom::from("RouterOutlet"), - Atom::from("router-outlet"), + Ident::from("RouterOutlet"), + Ident::from("router-outlet"), false, ) - .with_source_module(Atom::from("@angular/router")); + .with_source_module(Ident::from("@angular/router")); metadata.declarations.push(dir); metadata.declaration_list_emit_mode = DeclarationListEmitMode::Direct; @@ -2209,7 +2207,7 @@ mod tests { assert!(js.contains("i1.RouterOutlet")); // Verify namespace was registered - assert!(namespace_registry.has_module(&Atom::from("@angular/router"))); + assert!(namespace_registry.has_module(&Ident::from("@angular/router"))); } #[test] @@ -2221,8 +2219,8 @@ mod tests { // Add a local directive (no source_module) let local_dir = crate::component::metadata::TemplateDependency::directive( &allocator, - Atom::from("LocalDirective"), - Atom::from("[local]"), + Ident::from("LocalDirective"), + Ident::from("[local]"), false, ); metadata.declarations.push(local_dir); @@ -2230,11 +2228,11 @@ mod tests { // Add an imported directive let imported_dir = crate::component::metadata::TemplateDependency::directive( &allocator, - Atom::from("CommonModule"), - Atom::from("[common]"), + Ident::from("CommonModule"), + Ident::from("[common]"), false, ) - .with_source_module(Atom::from("@angular/common")); + .with_source_module(Ident::from("@angular/common")); metadata.declarations.push(imported_dir); metadata.declaration_list_emit_mode = DeclarationListEmitMode::Direct; @@ -2260,10 +2258,10 @@ mod tests { metadata.providers = Some(create_test_providers_array(&allocator, &["ServiceA"])); metadata.uses_inheritance = true; metadata.lifecycle = LifecycleMetadata { uses_on_changes: true }; - metadata.external_styles.push(Atom::from("./styles.css")); + metadata.external_styles.push(Ident::from("./styles.css")); // Add host directive - let directive = HostDirectiveMetadata::new(&allocator, Atom::from("HostDir")); + let directive = HostDirectiveMetadata::new(&allocator, Ident::from("HostDir")); metadata.host_directives.push(directive); let result = @@ -2307,7 +2305,7 @@ mod tests { let mut metadata = create_test_metadata(&allocator); // Create an animations expression (identifier reference) metadata.animations = Some(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("myAnimations"), source_span: None }, + ReadVarExpr { name: Ident::from("myAnimations"), source_span: None }, &allocator, ))); diff --git a/crates/oxc_angular_compiler/src/component/dependency.rs b/crates/oxc_angular_compiler/src/component/dependency.rs index e615f203c..42abcb803 100644 --- a/crates/oxc_angular_compiler/src/component/dependency.rs +++ b/crates/oxc_angular_compiler/src/component/dependency.rs @@ -9,7 +9,7 @@ //! Ported from: `packages/compiler/src/render3/r3_factory.ts` use oxc_allocator::{Allocator, Box, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use super::namespace_registry::NamespaceRegistry; use crate::output::ast::{ @@ -29,12 +29,12 @@ use crate::r3::Identifiers; pub struct R3DependencyMetadata<'a> { /// The injection token name (service class name, InjectionToken, etc.). /// `None` represents an invalid/unresolved dependency. - pub token: Option>, + pub token: Option>, /// The source module of the token (e.g., "@angular/core", "@angular/router"). /// Used to generate proper namespace aliases for imported dependencies. /// `None` for local dependencies or when the source is unknown. - pub token_source_module: Option>, + pub token_source_module: Option>, /// Whether the token has an existing named import in the source file. /// If true, the token can be used with a bare name instead of namespace prefix. @@ -47,7 +47,7 @@ pub struct R3DependencyMetadata<'a> { /// For `@Attribute()` dependencies, the attribute name. /// `None` for regular dependencies. - pub attribute_name: Option>, + pub attribute_name: Option>, /// Whether `@Host()` decorator is present. pub host: bool, @@ -64,7 +64,7 @@ pub struct R3DependencyMetadata<'a> { impl<'a> R3DependencyMetadata<'a> { /// Create a new dependency metadata with default flags. - pub fn new(token: Atom<'a>) -> Self { + pub fn new(token: Ident<'a>) -> Self { Self { token: Some(token), token_source_module: None, @@ -116,7 +116,7 @@ impl<'a> R3DependencyMetadata<'a> { } /// Create an `@Attribute()` dependency. - pub fn attribute(attribute_name: Atom<'a>) -> Self { + pub fn attribute(attribute_name: Ident<'a>) -> Self { Self { token: Some(attribute_name.clone()), token_source_module: None, @@ -136,7 +136,7 @@ impl<'a> R3DependencyMetadata<'a> { } /// Set the source module for the token. - pub fn with_token_source_module(mut self, source_module: Atom<'a>) -> Self { + pub fn with_token_source_module(mut self, source_module: Ident<'a>) -> Self { self.token_source_module = Some(source_module); self } @@ -322,7 +322,7 @@ fn compile_inject_dependency<'a>( fn create_token_expression<'a>( allocator: &'a Allocator, dep: &R3DependencyMetadata<'a>, - token_name: &Atom<'a>, + token_name: &Ident<'a>, namespace_registry: &mut NamespaceRegistry<'a>, ) -> OutputExpression<'a> { if let Some(ref source_module) = dep.token_source_module { @@ -391,7 +391,7 @@ fn create_invalid_factory_dep_call<'a>( /// Create an `i0.ɵɵinjectAttribute(attrName)` call. fn create_inject_attribute_call<'a>( allocator: &'a Allocator, - attr_name: Atom<'a>, + attr_name: Ident<'a>, ) -> OutputExpression<'a> { let fn_expr = create_angular_fn_ref(allocator, Identifiers::INJECT_ATTRIBUTE); @@ -460,12 +460,12 @@ fn create_angular_fn_ref<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(fn_name), + name: Ident::from(fn_name), optional: false, source_span: None, }, @@ -494,7 +494,7 @@ mod tests { #[test] fn test_simple_dependency() { let allocator = Allocator::default(); - let dep = R3DependencyMetadata::new(Atom::from("MyService")); + let dep = R3DependencyMetadata::new(Ident::from("MyService")); let mut registry = NamespaceRegistry::new(&allocator); let result = @@ -511,7 +511,7 @@ mod tests { #[test] fn test_optional_dependency() { let allocator = Allocator::default(); - let dep = R3DependencyMetadata::new(Atom::from("MyService")).with_optional(); + let dep = R3DependencyMetadata::new(Ident::from("MyService")).with_optional(); let mut registry = NamespaceRegistry::new(&allocator); let result = @@ -528,7 +528,7 @@ mod tests { #[test] fn test_host_dependency() { let allocator = Allocator::default(); - let dep = R3DependencyMetadata::new(Atom::from("HostService")).with_host(); + let dep = R3DependencyMetadata::new(Ident::from("HostService")).with_host(); let mut registry = NamespaceRegistry::new(&allocator); let result = @@ -545,7 +545,7 @@ mod tests { #[test] fn test_combined_flags() { let allocator = Allocator::default(); - let dep = R3DependencyMetadata::new(Atom::from("Service")).with_optional().with_host(); + let dep = R3DependencyMetadata::new(Ident::from("Service")).with_optional().with_host(); let mut registry = NamespaceRegistry::new(&allocator); let result = @@ -561,7 +561,7 @@ mod tests { #[test] fn test_injectable_uses_inject() { let allocator = Allocator::default(); - let dep = R3DependencyMetadata::new(Atom::from("Service")); + let dep = R3DependencyMetadata::new(Ident::from("Service")); let mut registry = NamespaceRegistry::new(&allocator); let result = compile_inject_dependency( @@ -598,7 +598,7 @@ mod tests { #[test] fn test_attribute_dependency() { let allocator = Allocator::default(); - let dep = R3DependencyMetadata::attribute(Atom::from("title")); + let dep = R3DependencyMetadata::attribute(Ident::from("title")); let mut registry = NamespaceRegistry::new(&allocator); let result = @@ -614,8 +614,8 @@ mod tests { #[test] fn test_imported_dependency_with_namespace() { let allocator = Allocator::default(); - let dep = R3DependencyMetadata::new(Atom::from("AuthService")) - .with_token_source_module(Atom::from("@app/auth")); + let dep = R3DependencyMetadata::new(Ident::from("AuthService")) + .with_token_source_module(Ident::from("@app/auth")); let mut registry = NamespaceRegistry::new(&allocator); let result = @@ -632,10 +632,10 @@ mod tests { #[test] fn test_multiple_imported_dependencies_same_module() { let allocator = Allocator::default(); - let dep1 = R3DependencyMetadata::new(Atom::from("AuthService")) - .with_token_source_module(Atom::from("@app/auth")); - let dep2 = R3DependencyMetadata::new(Atom::from("UserService")) - .with_token_source_module(Atom::from("@app/auth")); + let dep1 = R3DependencyMetadata::new(Ident::from("AuthService")) + .with_token_source_module(Ident::from("@app/auth")); + let dep2 = R3DependencyMetadata::new(Ident::from("UserService")) + .with_token_source_module(Ident::from("@app/auth")); let deps = vec![dep1, dep2]; let mut registry = NamespaceRegistry::new(&allocator); @@ -654,10 +654,10 @@ mod tests { #[test] fn test_multiple_imported_dependencies_different_modules() { let allocator = Allocator::default(); - let dep1 = R3DependencyMetadata::new(Atom::from("AuthService")) - .with_token_source_module(Atom::from("@app/auth")); - let dep2 = R3DependencyMetadata::new(Atom::from("HttpService")) - .with_token_source_module(Atom::from("@app/http")); + let dep1 = R3DependencyMetadata::new(Ident::from("AuthService")) + .with_token_source_module(Ident::from("@app/auth")); + let dep2 = R3DependencyMetadata::new(Ident::from("HttpService")) + .with_token_source_module(Ident::from("@app/http")); let deps = vec![dep1, dep2]; let mut registry = NamespaceRegistry::new(&allocator); @@ -676,8 +676,8 @@ mod tests { #[test] fn test_angular_core_dependency() { let allocator = Allocator::default(); - let dep = R3DependencyMetadata::new(Atom::from("ChangeDetectorRef")) - .with_token_source_module(Atom::from("@angular/core")); + let dep = R3DependencyMetadata::new(Ident::from("ChangeDetectorRef")) + .with_token_source_module(Ident::from("@angular/core")); let mut registry = NamespaceRegistry::new(&allocator); let result = diff --git a/crates/oxc_angular_compiler/src/component/import_elision.rs b/crates/oxc_angular_compiler/src/component/import_elision.rs index 5a5c377a7..fcafb803f 100644 --- a/crates/oxc_angular_compiler/src/component/import_elision.rs +++ b/crates/oxc_angular_compiler/src/component/import_elision.rs @@ -41,7 +41,7 @@ use oxc_ast::ast::{ Program, Statement, TSType, }; use oxc_semantic::{Semantic, SemanticBuilder, SymbolFlags}; -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashSet; use crate::optimizer::Edit; @@ -55,7 +55,7 @@ const PARAM_DECORATORS: &[&str] = &["Inject", "Optional", "Self", "SkipSelf", "H /// Analyzer for determining which imports are type-only and can be elided. pub struct ImportElisionAnalyzer<'a> { /// Set of import specifier local names that should be removed (type-only). - type_only_specifiers: FxHashSet>, + type_only_specifiers: FxHashSet>, } impl<'a> ImportElisionAnalyzer<'a> { @@ -98,7 +98,7 @@ impl<'a> ImportElisionAnalyzer<'a> { continue; } - let name: Atom<'a> = spec.local.name.clone().into(); + let name: Ident<'a> = spec.local.name.clone().into(); // Check if this import has only type references if Self::is_type_only_import(&spec.local, semantic) { @@ -110,7 +110,7 @@ impl<'a> ImportElisionAnalyzer<'a> { } } ImportDeclarationSpecifier::ImportDefaultSpecifier(spec) => { - let name: Atom<'a> = spec.local.name.clone().into(); + let name: Ident<'a> = spec.local.name.clone().into(); if Self::is_type_only_import(&spec.local, semantic) { type_only_specifiers.insert(name.clone()); @@ -145,7 +145,7 @@ impl<'a> ImportElisionAnalyzer<'a> { /// Computed property keys like `[fromEmail]` in type literals reference runtime values, /// even when they appear in type contexts. TypeScript considers these as value references /// and preserves their imports. - fn collect_computed_property_key_idents(program: &'a Program<'a>) -> FxHashSet> { + fn collect_computed_property_key_idents(program: &'a Program<'a>) -> FxHashSet> { let mut result = FxHashSet::default(); for stmt in &program.body { @@ -158,7 +158,7 @@ impl<'a> ImportElisionAnalyzer<'a> { /// Walk a statement collecting computed property key identifiers from type annotations. fn collect_computed_keys_from_statement( stmt: &'a Statement<'a>, - result: &mut FxHashSet>, + result: &mut FxHashSet>, ) { match stmt { Statement::ClassDeclaration(class) => { @@ -185,7 +185,7 @@ impl<'a> ImportElisionAnalyzer<'a> { /// Walk class members collecting computed property key identifiers from type annotations. fn collect_computed_keys_from_class( class: &'a oxc_ast::ast::Class<'a>, - result: &mut FxHashSet>, + result: &mut FxHashSet>, ) { for element in &class.body.body { if let ClassElement::PropertyDefinition(prop) = element { @@ -200,7 +200,7 @@ impl<'a> ImportElisionAnalyzer<'a> { /// Recursively walk a TypeScript type collecting computed property key identifiers. fn collect_computed_keys_from_ts_type( ts_type: &'a TSType<'a>, - result: &mut FxHashSet>, + result: &mut FxHashSet>, ) { match ts_type { TSType::TSTypeLiteral(type_lit) => { @@ -256,7 +256,7 @@ impl<'a> ImportElisionAnalyzer<'a> { /// Collect identifier names from an expression (for computed property keys). fn collect_idents_from_expr( expr: &'a oxc_ast::ast::PropertyKey<'a>, - result: &mut FxHashSet>, + result: &mut FxHashSet>, ) { match expr { oxc_ast::ast::PropertyKey::StaticIdentifier(_) => { @@ -272,7 +272,7 @@ impl<'a> ImportElisionAnalyzer<'a> { } /// Collect identifier names from an expression. - fn collect_idents_from_expression(expr: &'a Expression<'a>, result: &mut FxHashSet>) { + fn collect_idents_from_expression(expr: &'a Expression<'a>, result: &mut FxHashSet>) { match expr { Expression::Identifier(id) => { result.insert(id.name.clone().into()); @@ -641,11 +641,11 @@ impl<'a> ImportElisionAnalyzer<'a> { /// Check if a specifier name should be elided (removed). pub fn should_elide(&self, name: &str) -> bool { - self.type_only_specifiers.contains(name) + self.type_only_specifiers.contains(&Ident::from(name)) } /// Get the set of type-only specifier names. - pub fn type_only_specifiers(&self) -> &FxHashSet> { + pub fn type_only_specifiers(&self) -> &FxHashSet> { &self.type_only_specifiers } @@ -704,7 +704,7 @@ impl<'a> ImportElisionAnalyzer<'a> { let local_name = &spec.local.name; // Skip if already marked as type-only by semantic analysis - if analyzer.type_only_specifiers.contains(local_name.as_str()) { + if analyzer.type_only_specifiers.contains(local_name) { continue; } @@ -726,6 +726,7 @@ impl<'a> ImportElisionAnalyzer<'a> { /// Returns edits that remove type-only import specifiers from the source. /// Entire import declarations are removed if all their specifiers are type-only, /// or if the import has no specifiers at all (`import {} from 'module'`). +/// Type-only export declarations (`export type { X }`, `export { type X }`) are also removed. pub fn import_elision_edits<'a>( source: &str, program: &Program<'a>, @@ -741,7 +742,21 @@ pub fn import_elision_edits<'a>( false }); - if !analyzer.has_type_only_imports() && !has_empty_imports { + // Check if there are type-only exports that need removal + let has_type_only_exports = program.body.iter().any(|stmt| { + if let Statement::ExportNamedDeclaration(export_decl) = stmt { + if export_decl.source.is_some() || export_decl.declaration.is_some() { + return export_decl.export_kind.is_type(); + } + if export_decl.export_kind.is_type() { + return true; + } + return export_decl.specifiers.iter().any(|spec| spec.export_kind.is_type()); + } + false + }); + + if !analyzer.has_type_only_imports() && !has_empty_imports && !has_type_only_exports { return Vec::new(); } @@ -856,6 +871,79 @@ pub fn import_elision_edits<'a>( } } + // Process type-only export declarations + for stmt in &program.body { + let Statement::ExportNamedDeclaration(export_decl) = stmt else { + continue; + }; + + // Skip exports with declarations (e.g. `export class X {}`) + if export_decl.declaration.is_some() { + continue; + } + + if export_decl.export_kind.is_type() { + // `export type { X }` or `export type { X } from './foo'` — remove entirely + let start = export_decl.span.start as usize; + let mut end = export_decl.span.end as usize; + let bytes = source.as_bytes(); + while end < bytes.len() && (bytes[end] == b'\n' || bytes[end] == b'\r') { + end += 1; + } + edits.push(Edit::delete(start as u32, end as u32)); + continue; + } + + // Check for individual type-only specifiers (`export { type X, Y }`) + let (type_specs, value_specs): (Vec<_>, Vec<_>) = + export_decl.specifiers.iter().partition(|spec| spec.export_kind.is_type()); + + if type_specs.is_empty() { + continue; + } + + let start = export_decl.span.start as usize; + let mut end = export_decl.span.end as usize; + let bytes = source.as_bytes(); + while end < bytes.len() && (bytes[end] == b'\n' || bytes[end] == b'\r') { + end += 1; + } + + if value_specs.is_empty() { + // All specifiers are type-only — remove entire statement + edits.push(Edit::delete(start as u32, end as u32)); + } else { + // Partial removal — reconstruct with only value specifiers + let mut named_specifiers: Vec = Vec::new(); + for spec in &value_specs { + let local_name = spec.local.name().as_str(); + let exported_name = spec.exported.name().as_str(); + if local_name == exported_name { + named_specifiers.push(local_name.to_string()); + } else { + named_specifiers.push(format!("{local_name} as {exported_name}")); + } + } + + let mut new_export = String::from("export { "); + new_export.push_str(&named_specifiers.join(", ")); + new_export.push_str(" }"); + + if let Some(source_lit) = &export_decl.source { + new_export.push_str(" from \""); + new_export.push_str(source_lit.value.as_str()); + new_export.push('"'); + } + new_export.push(';'); + + if end > export_decl.span.end as usize { + new_export.push('\n'); + } + + edits.push(Edit::replace(start as u32, end as u32, new_export)); + } + } + edits } @@ -1990,4 +2078,97 @@ class UsersTableComponent {} filtered ); } + + #[test] + fn test_export_type_with_import_both_removed() { + let source = r#" +import { Config } from './config'; +export type { Config }; +"#; + let filtered = filter_source(source); + assert!( + !filtered.contains("Config"), + "Both import and export type should be removed.\nFiltered:\n{}", + filtered + ); + assert!( + !filtered.contains("export"), + "export type declaration should be removed.\nFiltered:\n{}", + filtered + ); + } + + #[test] + fn test_export_type_multiple_specifiers_removed() { + let source = r#" +import { Foo, Bar } from './types'; +export type { Foo, Bar }; +"#; + let filtered = filter_source(source); + assert!(!filtered.contains("Foo"), "Foo should be removed.\nFiltered:\n{}", filtered); + assert!(!filtered.contains("Bar"), "Bar should be removed.\nFiltered:\n{}", filtered); + } + + #[test] + fn test_export_mixed_type_and_value_specifiers() { + let source = r#" +import { Component } from '@angular/core'; +import { Foo, Bar } from './types'; +export { type Foo, Bar }; + +@Component({ selector: 'test' }) +class TestComponent { + value = Bar; +} +"#; + let filtered = filter_source(source); + // type Foo should be removed, Bar should remain + assert!( + !filtered.contains("Foo"), + "type-only Foo should be removed from export.\nFiltered:\n{}", + filtered + ); + assert!( + filtered.contains("export { Bar }"), + "Value export Bar should remain.\nFiltered:\n{}", + filtered + ); + } + + #[test] + fn test_export_type_with_source_removed() { + let source = r#" +import { Component } from '@angular/core'; +export type { Config } from './config'; + +@Component({ selector: 'test' }) +class TestComponent {} +"#; + let filtered = filter_source(source); + assert!( + !filtered.contains("Config"), + "export type with source should be removed.\nFiltered:\n{}", + filtered + ); + } + + #[test] + fn test_value_export_not_affected() { + let source = r#" +import { Component } from '@angular/core'; +import { helper } from './utils'; +export { helper }; + +@Component({ selector: 'test' }) +class TestComponent { + value = helper(); +} +"#; + let filtered = filter_source(source); + assert!( + filtered.contains("export { helper }"), + "Value export should not be affected.\nFiltered:\n{}", + filtered + ); + } } diff --git a/crates/oxc_angular_compiler/src/component/metadata.rs b/crates/oxc_angular_compiler/src/component/metadata.rs index 62f6f84ed..75f06db53 100644 --- a/crates/oxc_angular_compiler/src/component/metadata.rs +++ b/crates/oxc_angular_compiler/src/component/metadata.rs @@ -3,7 +3,7 @@ //! This module defines the metadata extracted from `@Component` decorators. use oxc_allocator::Vec; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use super::dependency::R3DependencyMetadata; use crate::directive::R3InputMetadata; @@ -117,25 +117,25 @@ pub enum ChangeDetectionStrategy { #[derive(Debug)] pub struct ComponentMetadata<'a> { /// The name of the component class. - pub class_name: Atom<'a>, + pub class_name: Ident<'a>, /// The span of the class declaration. pub class_span: Span, /// The CSS selector for this component. - pub selector: Option>, + pub selector: Option>, /// Inline template string. - pub template: Option>, + pub template: Option>, /// URL to an external template file. - pub template_url: Option>, + pub template_url: Option>, /// Inline styles array. - pub styles: Vec<'a, Atom<'a>>, + pub styles: Vec<'a, Ident<'a>>, /// URLs to external stylesheet files. - pub style_urls: Vec<'a, Atom<'a>>, + pub style_urls: Vec<'a, Ident<'a>>, /// Whether this is a standalone component. pub standalone: bool, @@ -150,13 +150,13 @@ pub struct ComponentMetadata<'a> { pub host: Option>, /// Component imports (for standalone components). - pub imports: Vec<'a, Atom<'a>>, + pub imports: Vec<'a, Ident<'a>>, /// Exported names for template references. /// /// In Angular, `exportAs` can be a comma-separated string (e.g., "foo, bar"), /// which is split into an array and emitted as `exportAs: ["foo", "bar"]`. - pub export_as: Vec<'a, Atom<'a>>, + pub export_as: Vec<'a, Ident<'a>>, /// Whether to preserve whitespace in templates. pub preserve_whitespaces: bool, @@ -185,7 +185,7 @@ pub struct ComponentMetadata<'a> { /// Extracted from class property decorators like: /// - `@Output() valueChange = new EventEmitter();` /// - `@Output('changed') valueChange = new EventEmitter();` - pub outputs: Vec<'a, (Atom<'a>, Atom<'a>)>, + pub outputs: Vec<'a, (Ident<'a>, Ident<'a>)>, // ========================================================================= // Feature-related fields @@ -226,7 +226,7 @@ pub struct ComponentMetadata<'a> { /// External stylesheet URLs that were resolved and need to be loaded. /// /// Corresponds to resolved `styleUrls`. Used to generate `ɵɵExternalStylesFeature`. - pub external_styles: Vec<'a, Atom<'a>>, + pub external_styles: Vec<'a, Ident<'a>>, // ========================================================================= // Template Dependency Fields @@ -269,7 +269,7 @@ pub struct ComponentMetadata<'a> { /// Corresponds to the `schemas` property in `@Component`. /// Common values are `CUSTOM_ELEMENTS_SCHEMA` and `NO_ERRORS_SCHEMA`. /// These are stored as identifier names. - pub schemas: Vec<'a, Atom<'a>>, + pub schemas: Vec<'a, Ident<'a>>, /// Whether this component uses signal-based inputs. /// @@ -287,20 +287,20 @@ pub struct ComponentMetadata<'a> { #[derive(Debug)] pub struct HostDirectiveMetadata<'a> { /// The directive class name. - pub directive: Atom<'a>, + pub directive: Ident<'a>, /// The source module of the directive (e.g., "@angular/common", "./my-directive"). /// Used to generate proper namespace aliases for imported host directives. /// `None` for local directives defined in the same file. - pub source_module: Option>, + pub source_module: Option>, /// Input mappings: (publicName, internalName) pairs. /// Empty if no inputs are exposed. - pub inputs: Vec<'a, (Atom<'a>, Atom<'a>)>, + pub inputs: Vec<'a, (Ident<'a>, Ident<'a>)>, /// Output mappings: (publicName, internalName) pairs. /// Empty if no outputs are exposed. - pub outputs: Vec<'a, (Atom<'a>, Atom<'a>)>, + pub outputs: Vec<'a, (Ident<'a>, Ident<'a>)>, /// Whether this is a forward reference (requires wrapping in a function). pub is_forward_reference: bool, @@ -308,7 +308,7 @@ pub struct HostDirectiveMetadata<'a> { impl<'a> HostDirectiveMetadata<'a> { /// Create a new HostDirectiveMetadata. - pub fn new(allocator: &'a oxc_allocator::Allocator, directive: Atom<'a>) -> Self { + pub fn new(allocator: &'a oxc_allocator::Allocator, directive: Ident<'a>) -> Self { Self { directive, source_module: None, @@ -324,7 +324,7 @@ impl<'a> HostDirectiveMetadata<'a> { } /// Set the source module for this host directive. - pub fn with_source_module(mut self, source_module: Atom<'a>) -> Self { + pub fn with_source_module(mut self, source_module: Ident<'a>) -> Self { self.source_module = Some(source_module); self } @@ -394,30 +394,30 @@ pub struct TemplateDependency<'a> { pub kind: TemplateDependencyKind, /// The type expression (class name or import reference). - pub type_name: Atom<'a>, + pub type_name: Ident<'a>, /// The source module of the dependency (e.g., "@angular/common", "./my-directive"). /// Used to generate proper namespace aliases for imported dependencies. /// `None` for local dependencies defined in the same file. - pub source_module: Option>, + pub source_module: Option>, /// For directives: the CSS selector. - pub selector: Option>, + pub selector: Option>, /// For directives: input property names. - pub inputs: Vec<'a, Atom<'a>>, + pub inputs: Vec<'a, Ident<'a>>, /// For directives: output property names. - pub outputs: Vec<'a, Atom<'a>>, + pub outputs: Vec<'a, Ident<'a>>, /// For directives: export names (exportAs). - pub export_as: Vec<'a, Atom<'a>>, + pub export_as: Vec<'a, Ident<'a>>, /// For directives: whether this is a component. pub is_component: bool, /// For pipes: the pipe name used in templates. - pub pipe_name: Option>, + pub pipe_name: Option>, /// Whether this is a forward reference. pub is_forward_reference: bool, @@ -427,8 +427,8 @@ impl<'a> TemplateDependency<'a> { /// Create a new directive dependency. pub fn directive( allocator: &'a oxc_allocator::Allocator, - type_name: Atom<'a>, - selector: Atom<'a>, + type_name: Ident<'a>, + selector: Ident<'a>, is_component: bool, ) -> Self { Self { @@ -448,8 +448,8 @@ impl<'a> TemplateDependency<'a> { /// Create a new pipe dependency. pub fn pipe( allocator: &'a oxc_allocator::Allocator, - type_name: Atom<'a>, - pipe_name: Atom<'a>, + type_name: Ident<'a>, + pipe_name: Ident<'a>, ) -> Self { Self { kind: TemplateDependencyKind::Pipe, @@ -472,7 +472,7 @@ impl<'a> TemplateDependency<'a> { } /// Set the source module for this dependency. - pub fn with_source_module(mut self, source_module: Atom<'a>) -> Self { + pub fn with_source_module(mut self, source_module: Ident<'a>) -> Self { self.source_module = Some(source_module); self } @@ -484,21 +484,21 @@ impl<'a> TemplateDependency<'a> { #[derive(Debug)] pub struct HostMetadata<'a> { /// Host property bindings: `{ '[class.active]': 'isActive' }` - pub properties: Vec<'a, (Atom<'a>, Atom<'a>)>, + pub properties: Vec<'a, (Ident<'a>, Ident<'a>)>, /// Host attribute bindings: `{ 'role': 'button' }` - pub attributes: Vec<'a, (Atom<'a>, Atom<'a>)>, + pub attributes: Vec<'a, (Ident<'a>, Ident<'a>)>, /// Host event listeners: `{ '(click)': 'onClick()' }` - pub listeners: Vec<'a, (Atom<'a>, Atom<'a>)>, + pub listeners: Vec<'a, (Ident<'a>, Ident<'a>)>, /// Special attribute for static class binding: `{ 'class': 'foo bar' }` /// Captured separately to generate class instructions during compilation. - pub class_attr: Option>, + pub class_attr: Option>, /// Special attribute for static style binding: `{ 'style': 'color: red' }` /// Captured separately to generate style instructions during compilation. - pub style_attr: Option>, + pub style_attr: Option>, } impl<'a> HostMetadata<'a> { @@ -524,7 +524,7 @@ impl<'a> ComponentMetadata<'a> { /// - `true` when the Angular version is unknown (assume latest) pub fn new( allocator: &'a oxc_allocator::Allocator, - class_name: Atom<'a>, + class_name: Ident<'a>, class_span: Span, implicit_standalone: bool, ) -> Self { diff --git a/crates/oxc_angular_compiler/src/component/namespace_registry.rs b/crates/oxc_angular_compiler/src/component/namespace_registry.rs index 9adc851b6..4d7eaec55 100644 --- a/crates/oxc_angular_compiler/src/component/namespace_registry.rs +++ b/crates/oxc_angular_compiler/src/component/namespace_registry.rs @@ -1,5 +1,5 @@ use oxc_allocator::{Allocator, FromIn}; -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; /// Registry for assigning namespace aliases to imported modules. @@ -9,7 +9,7 @@ use rustc_hash::FxHashMap; /// - i1, i2, i3... are assigned to other modules in order of first reference pub struct NamespaceRegistry<'a> { /// Map of module_path -> namespace alias - modules: FxHashMap, Atom<'a>>, + modules: FxHashMap, Ident<'a>>, /// Counter for next alias index (starts at 1, since i0 is reserved for @angular/core) next_index: usize, /// The allocator for creating atoms @@ -27,18 +27,18 @@ impl<'a> NamespaceRegistry<'a> { allocator, }; // Pre-register @angular/core as i0 - registry.modules.insert(Atom::from("@angular/core"), Atom::from("i0")); + registry.modules.insert(Ident::from("@angular/core"), Ident::from("i0")); registry } /// Get the namespace alias for a module, assigning one if not yet assigned. - pub fn get_or_assign(&mut self, module_path: &Atom<'a>) -> Atom<'a> { + pub fn get_or_assign(&mut self, module_path: &Ident<'a>) -> Ident<'a> { if let Some(alias) = self.modules.get(module_path) { return alias.clone(); } // Assign new alias - let alias = Atom::from_in(format!("i{}", self.next_index).as_str(), self.allocator); + let alias = Ident::from_in(format!("i{}", self.next_index).as_str(), self.allocator); self.next_index += 1; self.modules.insert(module_path.clone(), alias.clone()); alias @@ -46,14 +46,14 @@ impl<'a> NamespaceRegistry<'a> { /// Get all registered modules and their aliases. /// Returns in a deterministic order (sorted by alias). - pub fn get_all_modules(&self) -> Vec<(&Atom<'a>, &Atom<'a>)> { + pub fn get_all_modules(&self) -> Vec<(&Ident<'a>, &Ident<'a>)> { let mut entries: Vec<_> = self.modules.iter().collect(); entries.sort_by_key(|(_, alias)| alias.as_str()); entries } /// Check if a module has been registered. - pub fn has_module(&self, module_path: &Atom<'a>) -> bool { + pub fn has_module(&self, module_path: &Ident<'a>) -> bool { self.modules.contains_key(module_path) } @@ -112,7 +112,7 @@ mod tests { let allocator = Allocator::default(); let mut registry = NamespaceRegistry::new(&allocator); - let core_alias = registry.get_or_assign(&Atom::from("@angular/core")); + let core_alias = registry.get_or_assign(&Ident::from("@angular/core")); assert_eq!(core_alias.as_str(), "i0"); } @@ -121,9 +121,9 @@ mod tests { let allocator = Allocator::default(); let mut registry = NamespaceRegistry::new(&allocator); - let forms_alias = registry.get_or_assign(&Atom::from("@angular/forms")); - let router_alias = registry.get_or_assign(&Atom::from("@angular/router")); - let http_alias = registry.get_or_assign(&Atom::from("@angular/common/http")); + let forms_alias = registry.get_or_assign(&Ident::from("@angular/forms")); + let router_alias = registry.get_or_assign(&Ident::from("@angular/router")); + let http_alias = registry.get_or_assign(&Ident::from("@angular/common/http")); assert_eq!(forms_alias.as_str(), "i1"); assert_eq!(router_alias.as_str(), "i2"); @@ -135,8 +135,8 @@ mod tests { let allocator = Allocator::default(); let mut registry = NamespaceRegistry::new(&allocator); - let first = registry.get_or_assign(&Atom::from("@angular/forms")); - let second = registry.get_or_assign(&Atom::from("@angular/forms")); + let first = registry.get_or_assign(&Ident::from("@angular/forms")); + let second = registry.get_or_assign(&Ident::from("@angular/forms")); assert_eq!(first.as_str(), second.as_str()); assert_eq!(first.as_str(), "i1"); @@ -148,14 +148,14 @@ mod tests { let mut registry = NamespaceRegistry::new(&allocator); // @angular/core is pre-registered - assert!(registry.has_module(&Atom::from("@angular/core"))); + assert!(registry.has_module(&Ident::from("@angular/core"))); // Not yet registered - assert!(!registry.has_module(&Atom::from("@angular/forms"))); + assert!(!registry.has_module(&Ident::from("@angular/forms"))); // Register it - registry.get_or_assign(&Atom::from("@angular/forms")); - assert!(registry.has_module(&Atom::from("@angular/forms"))); + registry.get_or_assign(&Ident::from("@angular/forms")); + assert!(registry.has_module(&Ident::from("@angular/forms"))); } #[test] @@ -163,8 +163,8 @@ mod tests { let allocator = Allocator::default(); let mut registry = NamespaceRegistry::new(&allocator); - registry.get_or_assign(&Atom::from("@angular/router")); - registry.get_or_assign(&Atom::from("@angular/forms")); + registry.get_or_assign(&Ident::from("@angular/router")); + registry.get_or_assign(&Ident::from("@angular/forms")); let all_modules = registry.get_all_modules(); @@ -184,15 +184,15 @@ mod tests { let mut registry = NamespaceRegistry::new(&allocator); // Register other modules first - registry.get_or_assign(&Atom::from("@angular/forms")); - registry.get_or_assign(&Atom::from("@angular/router")); + registry.get_or_assign(&Ident::from("@angular/forms")); + registry.get_or_assign(&Ident::from("@angular/router")); // @angular/core should still be i0 - let core_alias = registry.get_or_assign(&Atom::from("@angular/core")); + let core_alias = registry.get_or_assign(&Ident::from("@angular/core")); assert_eq!(core_alias.as_str(), "i0"); // Next module should be i3 - let http_alias = registry.get_or_assign(&Atom::from("@angular/common/http")); + let http_alias = registry.get_or_assign(&Ident::from("@angular/common/http")); assert_eq!(http_alias.as_str(), "i3"); } @@ -201,8 +201,8 @@ mod tests { let allocator = Allocator::default(); let mut registry = NamespaceRegistry::new(&allocator); - registry.get_or_assign(&Atom::from("@angular/router")); - registry.get_or_assign(&Atom::from("@bitwarden/common/auth/abstractions/auth.service")); + registry.get_or_assign(&Ident::from("@angular/router")); + registry.get_or_assign(&Ident::from("@bitwarden/common/auth/abstractions/auth.service")); let imports = registry.generate_import_statements(); @@ -232,23 +232,23 @@ import * as i2 from '@bitwarden/common/auth/abstractions/auth.service'; let mut registry2 = NamespaceRegistry::new(&allocator); // Registry 1 has @angular/router as i1 - registry1.get_or_assign(&Atom::from("@angular/router")); + registry1.get_or_assign(&Ident::from("@angular/router")); // Registry 2 has @angular/forms as i1, @angular/common as i2 - registry2.get_or_assign(&Atom::from("@angular/forms")); - registry2.get_or_assign(&Atom::from("@angular/common")); + registry2.get_or_assign(&Ident::from("@angular/forms")); + registry2.get_or_assign(&Ident::from("@angular/common")); // Merge registry2 into registry1 registry1.merge_from(®istry2); // registry1 should now have all modules - assert!(registry1.has_module(&Atom::from("@angular/core"))); - assert!(registry1.has_module(&Atom::from("@angular/router"))); - assert!(registry1.has_module(&Atom::from("@angular/forms"))); - assert!(registry1.has_module(&Atom::from("@angular/common"))); + assert!(registry1.has_module(&Ident::from("@angular/core"))); + assert!(registry1.has_module(&Ident::from("@angular/router"))); + assert!(registry1.has_module(&Ident::from("@angular/forms"))); + assert!(registry1.has_module(&Ident::from("@angular/common"))); // New module should get next available index - let http_alias = registry1.get_or_assign(&Atom::from("@angular/common/http")); + let http_alias = registry1.get_or_assign(&Ident::from("@angular/common/http")); assert_eq!(http_alias.as_str(), "i3"); } } diff --git a/crates/oxc_angular_compiler/src/component/transform.rs b/crates/oxc_angular_compiler/src/component/transform.rs index e497c8bdd..8cf80acf9 100644 --- a/crates/oxc_angular_compiler/src/component/transform.rs +++ b/crates/oxc_angular_compiler/src/component/transform.rs @@ -5,6 +5,8 @@ use std::collections::HashMap; +use std::path::Path; + use oxc_allocator::{Allocator, Vec as OxcVec}; use oxc_ast::ast::{ Argument, ArrayExpressionElement, Declaration, ExportDefaultDeclarationKind, Expression, @@ -12,7 +14,7 @@ use oxc_ast::ast::{ }; use oxc_diagnostics::OxcDiagnostic; use oxc_parser::Parser; -use oxc_span::{Atom, GetSpan, SourceType, Span}; +use oxc_span::{GetSpan, Ident, SourceType, Span}; use rustc_hash::FxHashMap; use crate::optimizer::{Edit, apply_edits, apply_edits_with_sourcemap}; @@ -174,6 +176,12 @@ pub struct TransformOptions { /// /// Default: false (metadata is dev-only and usually stripped in production) pub emit_class_metadata: bool, + + /// Minify final component styles before emitting them into `styles: [...]`. + /// + /// This runs after Angular style encapsulation, so it applies to the same + /// final CSS strings that are embedded in component definitions. + pub minify_component_styles: bool, } /// Input for host metadata when passed via TransformOptions. @@ -223,6 +231,7 @@ impl Default for TransformOptions { resolved_imports: None, // Class metadata for TestBed support (disabled by default) emit_class_metadata: false, + minify_component_styles: false, } } } @@ -395,7 +404,7 @@ pub struct CompiledComponent<'a> { #[derive(Debug, Clone)] pub struct ImportInfo<'a> { /// The source module path (e.g., "@angular/core", "./services"). - pub source_module: Atom<'a>, + pub source_module: Ident<'a>, /// Whether this is a named import that can be reused with bare name. /// True for: `import { AuthService } from "module"` /// False for: `import * as core from "module"` (namespace imports) @@ -410,7 +419,7 @@ pub struct ImportInfo<'a> { /// /// Used to look up where a constructor dependency token was imported from /// and whether it can be reused with a bare name or requires namespace prefix. -pub type ImportMap<'a> = FxHashMap, ImportInfo<'a>>; +pub type ImportMap<'a> = FxHashMap, ImportInfo<'a>>; /// Build an import map from the program's import declarations. /// @@ -467,7 +476,7 @@ pub fn build_import_map<'a>( // or aliased: `import { AuthService as Auth } from "module"` // We use the local name as the key // Named imports CAN be reused with bare name - let local_name: Atom<'a> = spec.local.name.clone().into(); + let local_name: Ident<'a> = spec.local.name.clone().into(); // Type-only if the declaration is `import type { ... }` or the specifier // is `import { type X }` (inline type specifier) @@ -477,8 +486,8 @@ pub fn build_import_map<'a>( // Check if we have a resolved path for this identifier let source_module = resolved_imports .and_then(|m| m.get(local_name.as_str())) - .map(|resolved| Atom::from(allocator.alloc_str(resolved))) - .unwrap_or_else(|| default_source_module.clone()); + .map(|resolved| Ident::from(allocator.alloc_str(resolved))) + .unwrap_or_else(|| default_source_module.clone().into()); import_map.insert( local_name, @@ -488,13 +497,13 @@ pub fn build_import_map<'a>( ImportDeclarationSpecifier::ImportDefaultSpecifier(spec) => { // Default import: `import DefaultService from "module"` // Default imports CAN be reused with bare name - let local_name: Atom<'a> = spec.local.name.clone().into(); + let local_name: Ident<'a> = spec.local.name.clone().into(); // Check if we have a resolved path for this identifier let source_module = resolved_imports .and_then(|m| m.get(local_name.as_str())) - .map(|resolved| Atom::from(allocator.alloc_str(resolved))) - .unwrap_or_else(|| default_source_module.clone()); + .map(|resolved| Ident::from(allocator.alloc_str(resolved))) + .unwrap_or_else(|| default_source_module.clone().into()); import_map.insert( local_name, @@ -508,13 +517,13 @@ pub fn build_import_map<'a>( ImportDeclarationSpecifier::ImportNamespaceSpecifier(spec) => { // Namespace import: `import * as core from "module"` // Namespace imports CANNOT be reused with bare name for individual symbols - let local_name: Atom<'a> = spec.local.name.clone().into(); + let local_name: Ident<'a> = spec.local.name.clone().into(); // Check if we have a resolved path for this identifier let source_module = resolved_imports .and_then(|m| m.get(local_name.as_str())) - .map(|resolved| Atom::from(allocator.alloc_str(resolved))) - .unwrap_or_else(|| default_source_module.clone()); + .map(|resolved| Ident::from(allocator.alloc_str(resolved))) + .unwrap_or_else(|| default_source_module.clone().into()); import_map.insert( local_name, @@ -676,8 +685,8 @@ enum AngularDecoratorKind { struct JitClassInfo { /// The class name. class_name: String, - /// Span of the decorator (including @). - decorator_span: Span, + /// Spans of ALL class-level decorators (including @) to be removed. + all_class_decorator_spans: std::vec::Vec, /// Start of the statement (includes export keyword if present). stmt_start: u32, /// Start of the class keyword. @@ -688,12 +697,16 @@ struct JitClassInfo { is_exported: bool, /// Whether the class is export default. is_default_export: bool, + /// Whether the class is abstract. + is_abstract: bool, /// Constructor parameter info for ctorParameters. ctor_params: std::vec::Vec, - /// Member decorator info for propDecorators. + /// Member decorator info for propDecorators (Angular decorators like @Input, @Output). member_decorators: std::vec::Vec, - /// The modified decorator expression text for __decorate call. - decorator_text: String, + /// All class-level decorator expression texts for __decorate call, in source order. + all_class_decorator_texts: std::vec::Vec, + /// Non-Angular member decorators that need __decorate() calls. + non_angular_member_decorators: std::vec::Vec, } /// Constructor parameter info for JIT ctorParameters generation. @@ -720,6 +733,19 @@ struct JitMemberDecorator { decorators: std::vec::Vec, } +/// A non-Angular member decorator that needs to be lowered via __decorate(). +struct JitNonAngularMemberDecorator { + /// The member name. + member_name: String, + /// Whether the member is static. + is_static: bool, + /// Whether this is a property (field) vs a method/accessor. + /// TypeScript uses `void 0` for properties and `null` for methods/accessors. + is_property: bool, + /// The decorator expression texts (e.g., "Selector()", "Action(AddTodo)"). + decorator_texts: std::vec::Vec, +} + /// Find any Angular decorator on a class and return its kind and the decorator reference. fn find_angular_decorator<'a>( class: &'a oxc_ast::ast::Class<'a>, @@ -813,38 +839,71 @@ fn extract_jit_ctor_params( params } -/// Extract Angular member decorators for JIT propDecorators generation. +/// Angular field decorators that go into `static propDecorators`. +/// Matches Angular's official `FIELD_DECORATORS` constant from `@angular/compiler-cli`. +const ANGULAR_FIELD_DECORATORS: &[&str] = &[ + "Input", + "Output", + "HostBinding", + "HostListener", + "ViewChild", + "ViewChildren", + "ContentChild", + "ContentChildren", +]; + +/// All Angular decorator names from `@angular/core`. +/// Any decorator with one of these names is treated as Angular and excluded from +/// non-Angular `__decorate()` lowering. Angular identifies decorators by import source; +/// we use names since they're unique to `@angular/core`. +const ANGULAR_DECORATOR_NAMES: &[&str] = &[ + // Field decorators (→ propDecorators) + "Input", + "Output", + "HostBinding", + "HostListener", + "ViewChild", + "ViewChildren", + "ContentChild", + "ContentChildren", + // Parameter decorators (→ ctorParameters) + "Inject", + "Optional", + "Self", + "SkipSelf", + "Host", + "Attribute", + // Class decorators (→ class __decorate) + "Component", + "Directive", + "Pipe", + "Injectable", + "NgModule", +]; + +/// Extract all member decorators for JIT transformation in a single pass. /// -/// Collects all Angular-relevant decorators from class properties/methods -/// (excluding constructor) so they can be emitted as a `static propDecorators` property. -fn extract_jit_member_decorators( +/// Returns two collections: +/// - Angular field decorators → emitted as `static propDecorators = { ... }` +/// - Non-Angular decorators → emitted as `__decorate([...], target, "name", desc)` calls +fn extract_all_jit_member_decorators( source: &str, class: &oxc_ast::ast::Class<'_>, -) -> std::vec::Vec { +) -> (std::vec::Vec, std::vec::Vec) { use oxc_ast::ast::{ClassElement, MethodDefinitionKind, PropertyKey}; - const ANGULAR_MEMBER_DECORATORS: &[&str] = &[ - "Input", - "Output", - "HostBinding", - "HostListener", - "ViewChild", - "ViewChildren", - "ContentChild", - "ContentChildren", - ]; - - let mut result: std::vec::Vec = std::vec::Vec::new(); + let mut angular_members: std::vec::Vec = std::vec::Vec::new(); + let mut non_angular_members: std::vec::Vec = std::vec::Vec::new(); for element in &class.body.body { - let (member_name, decorators) = match element { + let (member_name, is_static, is_property, decorators) = match element { ClassElement::PropertyDefinition(prop) => { let name = match &prop.key { PropertyKey::StaticIdentifier(id) => id.name.to_string(), PropertyKey::StringLiteral(s) => s.value.to_string(), _ => continue, }; - (name, &prop.decorators) + (name, prop.r#static, true, &prop.decorators) } ClassElement::MethodDefinition(method) => { if method.kind == MethodDefinitionKind::Constructor { @@ -855,7 +914,7 @@ fn extract_jit_member_decorators( PropertyKey::StringLiteral(s) => s.value.to_string(), _ => continue, }; - (name, &method.decorators) + (name, method.r#static, false, &method.decorators) } ClassElement::AccessorProperty(accessor) => { let name = match &accessor.key { @@ -863,12 +922,13 @@ fn extract_jit_member_decorators( PropertyKey::StringLiteral(s) => s.value.to_string(), _ => continue, }; - (name, &accessor.decorators) + (name, accessor.r#static, false, &accessor.decorators) } _ => continue, }; let mut angular_decs: std::vec::Vec = std::vec::Vec::new(); + let mut non_angular_texts: std::vec::Vec = std::vec::Vec::new(); for decorator in decorators { let (dec_name, call_args) = match &decorator.expression { @@ -891,17 +951,37 @@ fn extract_jit_member_decorators( _ => continue, }; - if ANGULAR_MEMBER_DECORATORS.contains(&dec_name.as_str()) { + if ANGULAR_FIELD_DECORATORS.contains(&dec_name.as_str()) { + // Angular field decorator → goes into propDecorators angular_decs.push(JitParamDecorator { name: dec_name, args: call_args }); + } else if !ANGULAR_DECORATOR_NAMES.contains(&dec_name.as_str()) { + // Non-Angular decorator → goes into __decorate() call + let expr_start = decorator.expression.span().start; + let expr_end = decorator.expression.span().end; + non_angular_texts.push(source[expr_start as usize..expr_end as usize].to_string()); } + // Angular non-field decorators (e.g. @Inject on a member) are silently dropped + // since they have no meaningful effect on members. } if !angular_decs.is_empty() { - result.push(JitMemberDecorator { member_name, decorators: angular_decs }); + angular_members.push(JitMemberDecorator { + member_name: member_name.clone(), + decorators: angular_decs, + }); + } + + if !non_angular_texts.is_empty() { + non_angular_members.push(JitNonAngularMemberDecorator { + member_name, + is_static, + is_property, + decorator_texts: non_angular_texts, + }); } } - result + (angular_members, non_angular_members) } /// Build the propDecorators static property text for JIT member decorator metadata. @@ -1131,6 +1211,37 @@ fn build_jit_decorator_text( /// Transform an Angular TypeScript file in JIT (Just-In-Time) compilation mode. /// +/// Strip TypeScript syntax from JIT output using oxc_transformer. +/// +/// This runs as a post-pass after JIT text-edits, converting TypeScript → JavaScript. +/// It handles abstract members, type annotations, parameter properties, etc. +fn strip_typescript(allocator: &Allocator, path: &str, code: &str) -> String { + let source_type = SourceType::from_path(path).unwrap_or_default(); + let parser_ret = Parser::new(allocator, code, source_type).parse(); + if parser_ret.panicked { + return code.to_string(); + } + + let mut program = parser_ret.program; + + let semantic_ret = + oxc_semantic::SemanticBuilder::new().with_excess_capacity(2.0).build(&program); + + let ts_options = + oxc_transformer::TypeScriptOptions { only_remove_type_imports: true, ..Default::default() }; + + let transform_options = + oxc_transformer::TransformOptions { typescript: ts_options, ..Default::default() }; + + let transformer = + oxc_transformer::Transformer::new(allocator, Path::new(path), &transform_options); + transformer.build_with_scoping(semantic_ret.semantic.into_scoping(), &mut program); + + let codegen_ret = oxc_codegen::Codegen::new().with_source_text(code).build(&program); + + codegen_ret.code +} + /// JIT mode produces output compatible with Angular's JIT runtime compiler: /// - Decorators are downleveled using `__decorate` from tslib /// - `templateUrl` is replaced with `angular:jit:template:file;` imports @@ -1190,36 +1301,56 @@ fn transform_angular_file_jit( continue; }; - let Some((decorator_kind, decorator)) = find_angular_decorator(class) else { + let Some((decorator_kind, angular_decorator)) = find_angular_decorator(class) else { continue; }; - // Build modified decorator text (replaces templateUrl/styleUrl with resource imports) - let decorator_text = build_jit_decorator_text( - source, - decorator, - decorator_kind, - &mut resource_counter, - &mut resource_imports, - ); + // Collect ALL class-level decorator spans and texts (in source order) + let mut all_class_decorator_spans: std::vec::Vec = std::vec::Vec::new(); + let mut all_class_decorator_texts: std::vec::Vec = std::vec::Vec::new(); + + for dec in &class.decorators { + all_class_decorator_spans.push(dec.span); + + // Check if this is the Angular decorator that needs special text transformation + if dec.span == angular_decorator.span { + let text = build_jit_decorator_text( + source, + dec, + decorator_kind, + &mut resource_counter, + &mut resource_imports, + ); + all_class_decorator_texts.push(text); + } else { + // Non-Angular decorator: extract expression text from source (without @) + let expr_start = dec.expression.span().start; + let expr_end = dec.expression.span().end; + all_class_decorator_texts + .push(source[expr_start as usize..expr_end as usize].to_string()); + } + } // Extract constructor parameters for ctorParameters let ctor_params = extract_jit_ctor_params(source, class); - // Extract member decorators for propDecorators - let member_decorators = extract_jit_member_decorators(source, class); + // Extract Angular and non-Angular member decorators + let (member_decorators, non_angular_member_decorators) = + extract_all_jit_member_decorators(source, class); jit_classes.push(JitClassInfo { class_name, - decorator_span: decorator.span, + all_class_decorator_spans, stmt_start, class_start: class.span.start, class_body_end: class.body.span.end, is_exported, is_default_export, + is_abstract: class.r#abstract, ctor_params, member_decorators, - decorator_text, + all_class_decorator_texts, + non_angular_member_decorators, }); result.component_count += @@ -1300,9 +1431,9 @@ fn transform_angular_file_jit( continue; }; - // 4a. Remove the Angular decorator (including @ and trailing whitespace) - { - let mut end = jit_info.decorator_span.end as usize; + // 4a. Remove ALL class-level decorators (including @ and trailing whitespace) + for decorator_span in &jit_info.all_class_decorator_spans { + let mut end = decorator_span.end as usize; let bytes = source.as_bytes(); while end < bytes.len() { let c = bytes[end]; @@ -1312,14 +1443,14 @@ fn transform_angular_file_jit( break; } } - edits.push(Edit::delete(jit_info.decorator_span.start, end as u32)); + edits.push(Edit::delete(decorator_span.start, end as u32)); } - // 4b. Remove member decorators (@Input, @Output, etc.) and constructor param decorators + // 4b. Remove ALL member decorators and constructor param decorators { let mut decorator_spans: std::vec::Vec = std::vec::Vec::new(); super::decorator::collect_constructor_decorator_spans(class, &mut decorator_spans); - super::decorator::collect_member_decorator_spans(class, &mut decorator_spans); + super::decorator::collect_all_member_decorator_spans(class, &mut decorator_spans); for span in &decorator_spans { let mut end = span.end as usize; let bytes = source.as_bytes(); @@ -1336,15 +1467,25 @@ fn transform_angular_file_jit( } // 4c. Class restructuring: `export class X` → `let X = class X` + // For abstract classes, also strip the `abstract` keyword since class expressions can't be abstract. + let class_keyword_start = if jit_info.is_abstract { + let rest = &source[jit_info.class_start as usize..]; + let offset = rest.find("class").unwrap_or(0); + jit_info.class_start + offset as u32 + } else { + jit_info.class_start + }; + if jit_info.is_exported || jit_info.is_default_export { edits.push(Edit::replace( jit_info.stmt_start, - jit_info.class_start, + class_keyword_start, format!("let {} = ", jit_info.class_name), )); } else { - edits.push(Edit::insert( + edits.push(Edit::replace( jit_info.class_start, + class_keyword_start, format!("let {} = ", jit_info.class_name), )); } @@ -1364,11 +1505,41 @@ fn transform_angular_file_jit( } } - // 4e. After class body, add __decorate call and export - let mut after_class = format!( - ";\n{} = __decorate([\n {}\n], {});\n", - jit_info.class_name, jit_info.decorator_text, jit_info.class_name - ); + // 4e. After class body, add member __decorate calls, then class __decorate call, then export + let mut after_class = String::from(";\n"); + + // Emit __decorate() for non-Angular member decorators (before class __decorate). + // Match TypeScript's ordering: instance (prototype) members first, then static members. + // Within each group, preserve source declaration order. + for member_dec in jit_info + .non_angular_member_decorators + .iter() + .filter(|m| !m.is_static) + .chain(jit_info.non_angular_member_decorators.iter().filter(|m| m.is_static)) + { + let target = if member_dec.is_static { + jit_info.class_name.clone() + } else { + format!("{}.prototype", jit_info.class_name) + }; + // TypeScript uses `null` for methods/accessors (reads existing descriptor) + // and `void 0` for properties (no existing descriptor). + let desc = if member_dec.is_property { "void 0" } else { "null" }; + after_class.push_str(&format!( + "__decorate([{}], {}, \"{}\", {});\n", + member_dec.decorator_texts.join(", "), + target, + member_dec.member_name, + desc + )); + } + + // Emit class-level __decorate() with ALL class decorators + let all_decorator_text = jit_info.all_class_decorator_texts.join(",\n "); + after_class.push_str(&format!( + "{} = __decorate([\n {}\n], {});\n", + jit_info.class_name, all_decorator_text, jit_info.class_name + )); if jit_info.is_exported { after_class.push_str(&format!("export {{ {} }};\n", jit_info.class_name)); @@ -1388,6 +1559,9 @@ fn transform_angular_file_jit( result.code = apply_edits(source, edits); } + // 5. Strip TypeScript syntax from JIT output + result.code = strip_typescript(allocator, path, &result.code); + result } @@ -1415,9 +1589,11 @@ pub fn transform_angular_file( allocator: &Allocator, path: &str, source: &str, - options: &TransformOptions, + options: Option<&TransformOptions>, resolved_resources: Option<&ResolvedResources>, ) -> TransformResult { + let default_options = TransformOptions::default(); + let options = options.unwrap_or_else(|| &default_options); // JIT mode uses a completely different code path if options.jit { return transform_angular_file_jit(allocator, path, source, options); @@ -1528,7 +1704,7 @@ pub fn transform_angular_file( // a TypeScript type checker we cannot otherwise distinguish interfaces from classes. #[cfg(feature = "cross_file_elision")] for (name, is_type_only) in &cross_file_type_only { - if let Some(info) = import_map.get_mut(name.as_str()) { + if let Some(info) = import_map.get_mut(&Ident::from(name.as_str())) { if *is_type_only { info.is_type_only = true; } @@ -1593,6 +1769,7 @@ pub fn transform_angular_file( template, &mut metadata, path, + source, options, view_queries, content_queries, @@ -1681,7 +1858,7 @@ pub fn transform_angular_file( let type_expr = crate::output::ast::OutputExpression::ReadVar( oxc_allocator::Box::new_in( ReadVarExpr { - name: Atom::from(class_name.as_str()), + name: Ident::from(class_name.as_str()), source_span: None, }, allocator, @@ -2259,6 +2436,7 @@ fn compile_component_full<'a>( template: &'a str, metadata: &mut ComponentMetadata<'a>, file_path: &str, + source: &str, options: &TransformOptions, view_queries: OxcVec<'a, R3QueryMetadata<'a>>, content_queries: OxcVec<'a, R3QueryMetadata<'a>>, @@ -2329,7 +2507,7 @@ fn compile_component_full<'a>( // Stage 3-5: Ingest and compile // Build ingest options from metadata and transform options - let component_name_atom = Atom::from_in(metadata.class_name.as_str(), allocator); + let component_name_atom = Ident::from_in(metadata.class_name.as_str(), allocator); // OXC is a single-file compiler, equivalent to Angular's local compilation mode. // In local compilation mode, Angular ALWAYS sets hasDirectiveDependencies=true, @@ -2353,9 +2531,9 @@ fn compile_component_full<'a>( // Build relative paths for debug locations let relative_template_path = - if enable_debug_locations { Some(Atom::from_in(file_path, allocator)) } else { None }; + if enable_debug_locations { Some(Ident::from_in(file_path, allocator)) } else { None }; - let relative_context_file_path = Some(Atom::from_in(file_path, allocator)); + let relative_context_file_path = Some(Ident::from_in(file_path, allocator)); let ingest_options = IngestOptions { mode, @@ -2453,6 +2631,7 @@ fn compile_component_full<'a>( let definitions = generate_component_definitions( allocator, metadata, + options, &mut job, compiled.template_fn, host_binding_result, @@ -2505,11 +2684,11 @@ fn compile_component_full<'a>( let mut hmr_meta = HmrMetadata::new( component_type, metadata.class_name.clone(), - Atom::from_in(file_path, allocator), + Ident::from_in(file_path, allocator), ); // Add the @angular/core namespace dependency (i0) - hmr_meta.add_namespace_dependency(Atom::from("@angular/core"), Atom::from("i0")); + hmr_meta.add_namespace_dependency(Ident::from("@angular/core"), Ident::from("i0")); // Generate the HMR initializer expression let hmr_expr = compile_hmr_initializer(allocator, &hmr_meta); @@ -2532,13 +2711,22 @@ fn compile_component_full<'a>( allocator, )); - // Build the debug info - // Note: line_number is 1-indexed. We don't have access to the actual source - // line number here, but Angular uses the class declaration position. - // For now we use line 1, but this could be enhanced if we pass line info. + // Compute the 1-indexed line number of the class declaration from its byte offset + let class_line_number = { + let offset = metadata.class_span.start as usize; + let mut line = 1u32; + for &byte in &source.as_bytes()[..offset.min(source.len())] { + if byte == b'\n' { + line += 1; + } + } + line + }; + + // Build the debug info with the actual class declaration line number let debug_info = R3ClassDebugInfo::new(component_type, metadata.class_name.clone()) - .with_file_path(Atom::from_in(file_path, allocator)) - .with_line_number(1); // TODO: Get actual line number from class declaration + .with_file_path(Ident::from_in(file_path, allocator)) + .with_line_number(class_line_number); // Compile to IIFE-wrapped expression let debug_info_expr = compile_class_debug_info(allocator, &debug_info); @@ -2607,7 +2795,7 @@ fn resolve_styles<'a>( if let Some(style_contents) = resources.styles.get(style_url.as_str()) { // Add all resolved style contents to the metadata styles for style in style_contents { - metadata.styles.push(Atom::from_in(style.as_str(), allocator)); + metadata.styles.push(Ident::from_in(style.as_str(), allocator)); } } } @@ -2681,7 +2869,7 @@ pub fn compile_component_template<'a>( // Stage 3-5: Ingest and compile use oxc_allocator::FromIn; - let component_name_atom = Atom::from_in(component_name, allocator); + let component_name_atom = Ident::from_in(component_name, allocator); let mut job = ingest_component(allocator, component_name_atom, r3_result.nodes); let compiled = compile_template(&mut job); @@ -2802,7 +2990,7 @@ pub fn compile_template_to_js_with_options<'a>( }; // Stage 3-5: Ingest and compile - let component_name_atom = Atom::from_in(component_name, allocator); + let component_name_atom = Ident::from_in(component_name, allocator); let mut job = ingest_component_with_options( allocator, component_name_atom, @@ -2975,7 +3163,7 @@ pub fn compile_template_for_hmr<'a>( }; // Stage 3-5: Ingest and compile - let component_name_atom = Atom::from_in(component_name, allocator); + let component_name_atom = Ident::from_in(component_name, allocator); let mut job = ingest_component_with_options( allocator, component_name_atom, @@ -3133,7 +3321,7 @@ fn compile_component_host_bindings<'a>( // Get component name and selector let component_name = metadata.class_name.clone(); - let component_selector = metadata.selector.clone().unwrap_or_else(|| Atom::from("")); + let component_selector = metadata.selector.clone().unwrap_or_else(|| Ident::from("")); // Convert HostMetadata to HostBindingInput let input = convert_host_metadata_to_input(allocator, host, component_name, component_selector); @@ -3157,8 +3345,8 @@ fn compile_component_host_bindings<'a>( fn convert_host_metadata_to_input<'a>( allocator: &'a Allocator, host: &HostMetadata<'a>, - component_name: Atom<'a>, - component_selector: Atom<'a>, + component_name: Ident<'a>, + component_selector: Ident<'a>, ) -> HostBindingInput<'a> { use oxc_allocator::FromIn; @@ -3185,11 +3373,11 @@ fn convert_host_metadata_to_input<'a>( let parse_result = binding_parser.parse_binding(value_str, empty_span); properties.push(R3BoundAttribute { - name: Atom::from_in(final_name, allocator), + name: Ident::from_in(final_name, allocator), binding_type, security_context: SecurityContext::None, value: parse_result.ast, - unit: unit.map(|u| Atom::from_in(u, allocator)), + unit: unit.map(|u| Ident::from_in(u, allocator)), source_span: empty_span, key_span: empty_span, value_span: Some(empty_span), @@ -3217,10 +3405,10 @@ fn convert_host_metadata_to_input<'a>( let parse_result = binding_parser.parse_event(value_str, empty_span); events.push(R3BoundEvent { - name: Atom::from_in(final_event_name, allocator), + name: Ident::from_in(final_event_name, allocator), event_type: ParsedEventType::Regular, handler: parse_result.ast, - target: target.map(|t| Atom::from_in(t, allocator)), + target: target.map(|t| Ident::from_in(t, allocator)), phase: None, source_span: empty_span, handler_span: empty_span, @@ -3230,7 +3418,7 @@ fn convert_host_metadata_to_input<'a>( // Convert static attributes: "role" -> OutputExpression::Literal // This matches TypeScript which uses `o.literal(value)` for host attributes - let mut attributes: FxHashMap, crate::output::ast::OutputExpression<'a>> = + let mut attributes: FxHashMap, crate::output::ast::OutputExpression<'a>> = FxHashMap::default(); for (key, value) in host.attributes.iter() { @@ -3256,7 +3444,7 @@ fn convert_host_metadata_to_input<'a>( }, allocator, )); - attributes.insert(Atom::from("style"), expr); + attributes.insert(Ident::from("style"), expr); } if let Some(ref class_attr) = host.class_attr { @@ -3267,7 +3455,7 @@ fn convert_host_metadata_to_input<'a>( }, allocator, )); - attributes.insert(Atom::from("class"), expr); + attributes.insert(Ident::from("class"), expr); } HostBindingInput { component_name, component_selector, properties, attributes, events } @@ -3331,30 +3519,30 @@ fn convert_host_metadata_input_to_host_metadata<'a>( ) -> HostMetadata<'a> { use oxc_allocator::FromIn; - let mut properties: OxcVec<'a, (Atom<'a>, Atom<'a>)> = OxcVec::new_in(allocator); + let mut properties: OxcVec<'a, (Ident<'a>, Ident<'a>)> = OxcVec::new_in(allocator); for (k, v) in &input.properties { properties - .push((Atom::from_in(k.as_str(), allocator), Atom::from_in(v.as_str(), allocator))); + .push((Ident::from_in(k.as_str(), allocator), Ident::from_in(v.as_str(), allocator))); } - let mut attributes: OxcVec<'a, (Atom<'a>, Atom<'a>)> = OxcVec::new_in(allocator); + let mut attributes: OxcVec<'a, (Ident<'a>, Ident<'a>)> = OxcVec::new_in(allocator); for (k, v) in &input.attributes { attributes - .push((Atom::from_in(k.as_str(), allocator), Atom::from_in(v.as_str(), allocator))); + .push((Ident::from_in(k.as_str(), allocator), Ident::from_in(v.as_str(), allocator))); } - let mut listeners: OxcVec<'a, (Atom<'a>, Atom<'a>)> = OxcVec::new_in(allocator); + let mut listeners: OxcVec<'a, (Ident<'a>, Ident<'a>)> = OxcVec::new_in(allocator); for (k, v) in &input.listeners { listeners - .push((Atom::from_in(k.as_str(), allocator), Atom::from_in(v.as_str(), allocator))); + .push((Ident::from_in(k.as_str(), allocator), Ident::from_in(v.as_str(), allocator))); } HostMetadata { properties, attributes, listeners, - class_attr: input.class_attr.as_ref().map(|s| Atom::from_in(s.as_str(), allocator)), - style_attr: input.style_attr.as_ref().map(|s| Atom::from_in(s.as_str(), allocator)), + class_attr: input.class_attr.as_ref().map(|s| Ident::from_in(s.as_str(), allocator)), + style_attr: input.style_attr.as_ref().map(|s| Ident::from_in(s.as_str(), allocator)), } } @@ -3390,7 +3578,7 @@ fn pool_selector_attrs<'a>( for attr in selector_attrs { attr_entries.push(OutputExpression::Literal(oxc_allocator::Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from_in(attr.as_str(), allocator)), + value: LiteralValue::String(Ident::from_in(attr.as_str(), allocator)), source_span: None, }, allocator, @@ -3437,9 +3625,9 @@ fn compile_host_bindings_from_input<'a>( let host = convert_host_metadata_input_to_host_metadata(allocator, host_input); // Get component name and selector as atoms - let component_name_atom = Atom::from_in(component_name, allocator); + let component_name_atom = Ident::from_in(component_name, allocator); let component_selector = - selector.map(|s| Atom::from_in(s, allocator)).unwrap_or_else(|| Atom::from("")); + selector.map(|s| Ident::from_in(s, allocator)).unwrap_or_else(|| Ident::from("")); // Convert to HostBindingInput and compile let input = @@ -3602,7 +3790,7 @@ pub fn compile_template_for_linker<'a>( angular_version: None, }; - let component_name_atom = Atom::from_in(component_name, allocator); + let component_name_atom = Ident::from_in(component_name, allocator); let mut job = ingest_component_with_options( allocator, component_name_atom, @@ -3738,13 +3926,7 @@ export class HelloComponent { } "#; - let result = transform_angular_file( - &allocator, - "hello.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "hello.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors()); @@ -3780,13 +3962,7 @@ import { Component } from '@angular/core'; export class TestComponent {} "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors()); @@ -3839,13 +4015,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors()); @@ -3872,13 +4042,7 @@ export class RegularClass { } "#; - let result = transform_angular_file( - &allocator, - "regular.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "regular.ts", source, None, None); assert_eq!(result.component_count, 0); assert!(!result.has_errors()); @@ -3906,13 +4070,7 @@ export class FirstComponent {} export class SecondComponent {} "#; - let result = transform_angular_file( - &allocator, - "multi.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "multi.component.ts", source, None, None); assert_eq!(result.component_count, 2); assert!(!result.has_errors()); @@ -3951,13 +4109,7 @@ export class ButtonComponent { } "#; - let result = transform_angular_file( - &allocator, - "button.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "button.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors()); @@ -3994,13 +4146,8 @@ import { Component } from '@angular/core'; export class BitErrorSummaryComponent {} "#; - let result = transform_angular_file( - &allocator, - "error-summary.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = + transform_angular_file(&allocator, "error-summary.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors()); @@ -4041,13 +4188,7 @@ import { Component } from '@angular/core'; export class AppComponent {} "#; - let result = transform_angular_file( - &allocator, - "app.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "app.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors()); @@ -4092,13 +4233,7 @@ import { Directive } from '@angular/core'; export class MyDirective {} "#; - let result = transform_angular_file( - &allocator, - "my.directive.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "my.directive.ts", source, None, None); // The @Directive decorator should be removed from the output assert!( @@ -4145,13 +4280,7 @@ export class NavBaseDirective {} export class NavComponent extends NavBaseDirective {} "#; - let result = transform_angular_file( - &allocator, - "nav.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "nav.component.ts", source, None, None); // Both decorators should be removed assert!( @@ -4196,13 +4325,8 @@ export abstract class NavBaseComponent { } "#; - let result = transform_angular_file( - &allocator, - "nav-base.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = + transform_angular_file(&allocator, "nav-base.component.ts", source, None, None); // The @Directive() decorator should be removed assert!( @@ -4246,13 +4370,7 @@ export class MyService { } "#; - let result = transform_angular_file( - &allocator, - "my.service.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "my.service.ts", source, None, None); // The @Injectable decorator should be removed from the output assert!( @@ -4291,13 +4409,7 @@ import { Injectable } from '@angular/core'; export class LocalService {} "#; - let result = transform_angular_file( - &allocator, - "local.service.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "local.service.ts", source, None, None); // The @Injectable() decorator should be removed assert!( @@ -4316,10 +4428,10 @@ export class LocalService {} result.code.contains("ɵɵdefineInjectable"), "Code should contain ɵɵdefineInjectable" ); - // Should default to providedIn: "root" (Angular's default behavior) + // Should NOT have providedIn when not explicitly specified assert!( - result.code.contains(r#"providedIn:"root""#), - "Code should contain providedIn:\"root\" by default, but got:\n{}", + !result.code.contains("providedIn"), + "Code should NOT contain providedIn when not specified, but got:\n{}", result.code ); } @@ -4340,13 +4452,7 @@ export class MyService { } "#; - let result = transform_angular_file( - &allocator, - "factory.service.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "factory.service.ts", source, None, None); // The @Injectable decorator should be removed assert!( @@ -4381,13 +4487,7 @@ import { Injectable } from '@angular/core'; export class Config {} "#; - let result = transform_angular_file( - &allocator, - "config.service.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "config.service.ts", source, None, None); // The @Injectable decorator should be removed assert!( @@ -4427,13 +4527,7 @@ export class DataComponent { } "#; - let result = transform_angular_file( - &allocator, - "data.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "data.component.ts", source, None, None); // Both decorators should be removed assert!( @@ -4471,13 +4565,7 @@ import { Injectable } from '@angular/core'; export class PlatformService {} "#; - let result = transform_angular_file( - &allocator, - "platform.service.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "platform.service.ts", source, None, None); // ɵprov should be generated assert!( @@ -4507,13 +4595,7 @@ export class UppercasePipe implements PipeTransform { } "#; - let result = transform_angular_file( - &allocator, - "uppercase.pipe.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "uppercase.pipe.ts", source, None, None); // The @Pipe decorator should be removed from the output assert!( @@ -4558,13 +4640,7 @@ import { Pipe } from '@angular/core'; export class MyPurePipe {} "#; - let result = transform_angular_file( - &allocator, - "my-pure.pipe.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "my-pure.pipe.ts", source, None, None); assert!( result.code.contains("static ɵpipe = "), @@ -4589,13 +4665,7 @@ import { Pipe } from '@angular/core'; export class ImpurePipe {} "#; - let result = transform_angular_file( - &allocator, - "impure.pipe.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "impure.pipe.ts", source, None, None); assert!( result.code.contains("static ɵpipe = "), @@ -4624,13 +4694,7 @@ import { Pipe } from '@angular/core'; export class LegacyPipe {} "#; - let result = transform_angular_file( - &allocator, - "legacy.pipe.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "legacy.pipe.ts", source, None, None); assert!( result.code.contains("static ɵpipe = "), @@ -4667,13 +4731,7 @@ export class DataComponent { } "#; - let result = transform_angular_file( - &allocator, - "data.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "data.component.ts", source, None, None); // Both decorators should be removed assert!(!result.code.contains("@Pipe"), "Code should NOT contain @Pipe decorator"); @@ -4732,13 +4790,7 @@ export class AppComponent { } "#; - let result = transform_angular_file( - &allocator, - "app.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "app.component.ts", source, None, None); // All decorators should be removed assert!(!result.code.contains("@Pipe"), "Code should NOT contain @Pipe decorator"); @@ -4803,13 +4855,7 @@ import { NgModule } from '@angular/core'; export class AppModule {} "#; - let result = transform_angular_file( - &allocator, - "app.module.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "app.module.ts", source, None, None); // The @NgModule decorator should be removed from the output assert!( @@ -4848,13 +4894,7 @@ import { NgModule } from '@angular/core'; export class EmptyModule {} "#; - let result = transform_angular_file( - &allocator, - "empty.module.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "empty.module.ts", source, None, None); // The @NgModule() decorator should be removed assert!( @@ -4885,13 +4925,8 @@ import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; export class CustomElementsModule {} "#; - let result = transform_angular_file( - &allocator, - "custom-elements.module.ts", - source, - &TransformOptions::default(), - None, - ); + let result = + transform_angular_file(&allocator, "custom-elements.module.ts", source, None, None); // ɵmod should be generated with schemas assert!( @@ -4915,13 +4950,7 @@ import { NgModule } from '@angular/core'; export class IdentifiedModule {} "#; - let result = transform_angular_file( - &allocator, - "identified.module.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "identified.module.ts", source, None, None); // ɵmod should be generated assert!( @@ -4958,13 +4987,7 @@ export class AppComponent {} export class AppModule {} "#; - let result = transform_angular_file( - &allocator, - "app.module.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "app.module.ts", source, None, None); // Both decorators should be removed assert!(!result.code.contains("@NgModule"), "Code should NOT contain @NgModule decorator"); @@ -5019,13 +5042,7 @@ export class AppComponent { export class AppModule {} "#; - let result = transform_angular_file( - &allocator, - "app.module.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "app.module.ts", source, None, None); // All decorators should be removed assert!(!result.code.contains("@Pipe"), "Code should NOT contain @Pipe decorator"); @@ -5093,13 +5110,8 @@ import { NgModule, forwardRef } from '@angular/core'; export class AppModule {} "#; - let result = transform_angular_file( - &allocator, - "forward-ref.module.ts", - source, - &TransformOptions::default(), - None, - ); + let result = + transform_angular_file(&allocator, "forward-ref.module.ts", source, None, None); // ɵmod should be generated assert!( @@ -5129,7 +5141,7 @@ export class AppModule {} build_import_map(&allocator, &parser_ret.program.body, None); assert_eq!( import_map_without_resolved - .get(&Atom::from("AriaDisableDirective")) + .get(&Ident::from("AriaDisableDirective")) .map(|i| i.source_module.as_str()), Some("../a11y") ); @@ -5147,7 +5159,7 @@ export class AppModule {} // AriaDisableDirective should have the resolved path assert_eq!( import_map_with_resolved - .get(&Atom::from("AriaDisableDirective")) + .get(&Ident::from("AriaDisableDirective")) .map(|i| i.source_module.as_str()), Some("../a11y/aria-disable.directive"), "AriaDisableDirective should use resolved path" @@ -5156,7 +5168,7 @@ export class AppModule {} // OtherDirective should still use the original import path (not in resolved_imports) assert_eq!( import_map_with_resolved - .get(&Atom::from("OtherDirective")) + .get(&Ident::from("OtherDirective")) .map(|i| i.source_module.as_str()), Some("./other"), "OtherDirective should use original import path" @@ -5189,7 +5201,7 @@ export class TestComponent {} options.resolved_imports = Some(resolved_imports); let result = - transform_angular_file(&allocator, "test.component.ts", source, &options, None); + transform_angular_file(&allocator, "test.component.ts", source, Some(&options), None); assert!(!result.has_errors()); // The output should contain the host directive feature @@ -5280,7 +5292,7 @@ export class ButtonComponent {} &allocator, component_path.to_str().unwrap(), component_source, - &options, + Some(&options), None, ); @@ -5334,7 +5346,7 @@ export class TestComponent { options.emit_class_metadata = true; let result = - transform_angular_file(&allocator, "test.component.ts", source, &options, None); + transform_angular_file(&allocator, "test.component.ts", source, Some(&options), None); // @Inject(DARK_THEME) now uses namespace imports (i1 for @app/theme), // so rxjs gets i2 for Observable. @@ -5391,7 +5403,7 @@ export class TestComponent { options.emit_class_metadata = true; let result = - transform_angular_file(&allocator, "test.component.ts", source, &options, None); + transform_angular_file(&allocator, "test.component.ts", source, Some(&options), None); // The type 'AbstractService' is imported from './service' and should get a namespace import // even though the @Inject token 'SERVICE_TOKEN' is also from './service'. @@ -5441,7 +5453,7 @@ export class TestComponent { options.emit_class_metadata = true; let result = - transform_angular_file(&allocator, "test.component.ts", source, &options, None); + transform_angular_file(&allocator, "test.component.ts", source, Some(&options), None); // Should NOT generate a namespace import for ./some.interface // (the original `import type` statement may still appear, but no `import * as iN`) @@ -5477,7 +5489,7 @@ export class TestComponent { options.emit_class_metadata = true; let result = - transform_angular_file(&allocator, "test.component.ts", source, &options, None); + transform_angular_file(&allocator, "test.component.ts", source, Some(&options), None); // Should NOT generate a namespace import for ./some.interface assert!( @@ -5508,13 +5520,7 @@ export class MyDirective { } "#; - let result = transform_angular_file( - &allocator, - "my.directive.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "my.directive.ts", source, None, None); assert!(!result.has_errors(), "Transform should not have errors: {:?}", result.diagnostics); @@ -5573,13 +5579,7 @@ export class OSTypeIconPipe implements PipeTransform { } "#; - let result = transform_angular_file( - &allocator, - "os-type-icon.pipe.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "os-type-icon.pipe.ts", source, None, None); // Both decorators should be removed assert!( @@ -5627,13 +5627,7 @@ export class OSTypeIconPipe implements PipeTransform { } "#; - let result = transform_angular_file( - &allocator, - "os-type-icon.pipe.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "os-type-icon.pipe.ts", source, None, None); // Both decorators should be removed assert!( @@ -5681,13 +5675,7 @@ import { Component, Injectable } from '@angular/core'; export class TestCmp {} "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!( !result.code.contains("@Component"), @@ -5731,13 +5719,7 @@ import { Directive, Injectable } from '@angular/core'; export class TestDir {} "#; - let result = transform_angular_file( - &allocator, - "test.directive.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.directive.ts", source, None, None); assert!( !result.code.contains("@Directive"), @@ -5779,13 +5761,7 @@ import { NgModule, Injectable } from '@angular/core'; export class TestNgModule {} "#; - let result = transform_angular_file( - &allocator, - "test.module.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.module.ts", source, None, None); assert!( !result.code.contains("@NgModule"), @@ -5837,13 +5813,7 @@ import { BrnTooltipTrigger } from '@spartan-ng/brain/tooltip'; export class UnityTooltipTrigger {} "#; - let result = transform_angular_file( - &allocator, - "tooltip.directive.ts", - source, - &TransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "tooltip.directive.ts", source, None, None); assert!(!result.has_errors(), "Transform should not have errors: {:?}", result.diagnostics); diff --git a/crates/oxc_angular_compiler/src/directive/compiler.rs b/crates/oxc_angular_compiler/src/directive/compiler.rs index 960b31fce..c88b5ed8e 100644 --- a/crates/oxc_angular_compiler/src/directive/compiler.rs +++ b/crates/oxc_angular_compiler/src/directive/compiler.rs @@ -15,7 +15,7 @@ //! ``` use oxc_allocator::{Allocator, Box, Vec}; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use rustc_hash::FxHashMap; use super::metadata::{ @@ -108,7 +108,7 @@ fn build_base_directive_fields<'a>( // type: MyDirective entries.push(LiteralMapEntry { - key: Atom::from("type"), + key: Ident::from("type"), value: metadata.r#type.clone_in(allocator), quoted: false, }); @@ -117,7 +117,7 @@ fn build_base_directive_fields<'a>( if let Some(selector) = &metadata.selector { if let Some(selectors_expr) = parse_selector_to_r3_selector(allocator, selector) { entries.push(LiteralMapEntry { - key: Atom::from("selectors"), + key: Ident::from("selectors"), value: selectors_expr, quoted: false, }); @@ -135,7 +135,7 @@ fn build_base_directive_fields<'a>( None, ); entries.push(LiteralMapEntry { - key: Atom::from("contentQueries"), + key: Ident::from("contentQueries"), value: content_queries_fn, quoted: false, }); @@ -152,7 +152,7 @@ fn build_base_directive_fields<'a>( None, ); entries.push(LiteralMapEntry { - key: Atom::from("viewQuery"), + key: Ident::from("viewQuery"), value: view_queries_fn, quoted: false, }); @@ -175,7 +175,7 @@ fn build_base_directive_fields<'a>( // as they are dynamic bindings handled by hostBindings function if let Some(host_attrs) = result.host_attrs { entries.push(LiteralMapEntry { - key: Atom::from("hostAttrs"), + key: Ident::from("hostAttrs"), value: host_attrs, quoted: false, }); @@ -184,7 +184,7 @@ fn build_base_directive_fields<'a>( // hostVars: number - only if > 0 if let Some(host_vars) = result.host_vars { entries.push(LiteralMapEntry { - key: Atom::from("hostVars"), + key: Ident::from("hostVars"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::Number(host_vars as f64), @@ -199,7 +199,7 @@ fn build_base_directive_fields<'a>( // hostBindings: function(rf, ctx) { ... } if let Some(host_fn) = result.host_binding_fn { entries.push(LiteralMapEntry { - key: Atom::from("hostBindings"), + key: Ident::from("hostBindings"), value: OutputExpression::Function(Box::new_in(host_fn, allocator)), quoted: false, }); @@ -214,7 +214,7 @@ fn build_base_directive_fields<'a>( if !metadata.inputs.is_empty() { if let Some(inputs_expr) = create_inputs_literal(allocator, &metadata.inputs) { entries.push(LiteralMapEntry { - key: Atom::from("inputs"), + key: Ident::from("inputs"), value: inputs_expr, quoted: false, }); @@ -225,7 +225,7 @@ fn build_base_directive_fields<'a>( if !metadata.outputs.is_empty() { if let Some(outputs_expr) = create_outputs_literal(allocator, &metadata.outputs) { entries.push(LiteralMapEntry { - key: Atom::from("outputs"), + key: Ident::from("outputs"), value: outputs_expr, quoted: false, }); @@ -242,7 +242,7 @@ fn build_base_directive_fields<'a>( ))); } entries.push(LiteralMapEntry { - key: Atom::from("exportAs"), + key: Ident::from("exportAs"), value: OutputExpression::LiteralArray(Box::new_in( LiteralArrayExpr { entries: export_items, source_span: None }, allocator, @@ -254,7 +254,7 @@ fn build_base_directive_fields<'a>( // standalone: false (only if not standalone, since true is default) if !metadata.is_standalone { entries.push(LiteralMapEntry { - key: Atom::from("standalone"), + key: Ident::from("standalone"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::Boolean(false), source_span: None }, allocator, @@ -266,7 +266,7 @@ fn build_base_directive_fields<'a>( // signals: true (only if signal-based) if metadata.is_signal { entries.push(LiteralMapEntry { - key: Atom::from("signals"), + key: Ident::from("signals"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::Boolean(true), source_span: None }, allocator, @@ -316,7 +316,7 @@ fn add_features<'a>( if !features.is_empty() { definition_map.push(LiteralMapEntry { - key: Atom::from("features"), + key: Ident::from("features"), value: OutputExpression::LiteralArray(Box::new_in( LiteralArrayExpr { entries: features, source_span: None }, allocator, @@ -336,12 +336,12 @@ fn create_define_directive_call<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::DEFINE_DIRECTIVE), + name: Ident::from(Identifiers::DEFINE_DIRECTIVE), optional: false, source_span: None, }, @@ -382,7 +382,7 @@ fn create_define_directive_call<'a>( /// Ported from Angular's `parseSelectorToR3Selector` in `core.ts`. fn parse_selector_to_r3_selector<'a>( allocator: &'a Allocator, - selector: &Atom<'a>, + selector: &Ident<'a>, ) -> Option> { let selector_str = selector.as_str(); if selector_str.is_empty() { @@ -424,6 +424,13 @@ pub enum InputFlags { HasDecoratorInputTransform = 2, // 1 << 1 } +/// Check if an object property key needs quoting because it contains unsafe characters. +/// +/// Matches Angular's `UNSAFE_OBJECT_KEY_NAME_REGEXP = /[-.]/` from `render3/view/util.ts`. +fn needs_object_key_quoting(key: &str) -> bool { + key.contains('.') || key.contains('-') +} + /// Creates the inputs literal map. /// /// Ported from Angular's `conditionallyCreateDirectiveBindingLiteral` in `render3/view/util.ts`. @@ -512,7 +519,8 @@ pub fn create_inputs_literal<'a>( )) }; - entries.push(LiteralMapEntry { key: declared_name.clone(), value, quoted: false }); + let quoted = needs_object_key_quoting(declared_name); + entries.push(LiteralMapEntry { key: declared_name.clone(), value, quoted }); } Some(OutputExpression::LiteralMap(Box::new_in( @@ -524,7 +532,7 @@ pub fn create_inputs_literal<'a>( /// Creates the outputs literal map. pub fn create_outputs_literal<'a>( allocator: &'a Allocator, - outputs: &[(Atom<'a>, Atom<'a>)], + outputs: &[(Ident<'a>, Ident<'a>)], ) -> Option> { if outputs.is_empty() { return None; @@ -533,6 +541,7 @@ pub fn create_outputs_literal<'a>( let mut entries = Vec::new_in(allocator); for (class_name, binding_name) in outputs { + let quoted = needs_object_key_quoting(class_name); entries.push(LiteralMapEntry { key: class_name.clone(), value: OutputExpression::Literal(Box::new_in( @@ -542,7 +551,7 @@ pub fn create_outputs_literal<'a>( }, allocator, )), - quoted: false, + quoted, }); } @@ -577,7 +586,7 @@ fn compile_directive_host_bindings<'a>( // Get directive name and selector let directive_name = metadata.name.clone(); - let directive_selector = metadata.selector.clone().unwrap_or_else(|| Atom::from("")); + let directive_selector = metadata.selector.clone().unwrap_or_else(|| Ident::from("")); // Convert R3HostMetadata to HostBindingInput let input = @@ -597,17 +606,17 @@ fn compile_directive_host_bindings<'a>( /// Convert R3HostMetadata to HostBindingInput. /// /// R3HostMetadata has: -/// - `attributes`: Vec<(Atom, OutputExpression)> - already compiled expressions -/// - `properties`: Vec<(Atom, Atom)> - unparsed property binding strings -/// - `listeners`: Vec<(Atom, Atom)> - unparsed event handler strings +/// - `attributes`: Vec<(Ident, OutputExpression)> - already compiled expressions +/// - `properties`: Vec<(Ident, Ident)> - unparsed property binding strings +/// - `listeners`: Vec<(Ident, Ident)> - unparsed event handler strings /// /// This function parses the property and listener strings and passes through /// the already-compiled attribute expressions. fn convert_r3_host_metadata_to_input<'a>( allocator: &'a Allocator, host: &R3HostMetadata<'a>, - directive_name: Atom<'a>, - directive_selector: Atom<'a>, + directive_name: Ident<'a>, + directive_selector: Ident<'a>, ) -> HostBindingInput<'a> { use oxc_allocator::FromIn; @@ -634,11 +643,11 @@ fn convert_r3_host_metadata_to_input<'a>( let parse_result = binding_parser.parse_binding(value_str, empty_span); properties.push(R3BoundAttribute { - name: Atom::from_in(final_name, allocator), + name: Ident::from_in(final_name, allocator), binding_type, security_context: SecurityContext::None, value: parse_result.ast, - unit: unit.map(|u| Atom::from_in(u, allocator)), + unit: unit.map(|u| Ident::from_in(u, allocator)), source_span: empty_span, key_span: empty_span, value_span: Some(empty_span), @@ -666,10 +675,10 @@ fn convert_r3_host_metadata_to_input<'a>( let parse_result = binding_parser.parse_event(value_str, empty_span); events.push(R3BoundEvent { - name: Atom::from_in(final_event_name, allocator), + name: Ident::from_in(final_event_name, allocator), event_type: ParsedEventType::Regular, handler: parse_result.ast, - target: target.map(|t| Atom::from_in(t, allocator)), + target: target.map(|t| Ident::from_in(t, allocator)), phase: None, source_span: empty_span, handler_span: empty_span, @@ -679,7 +688,7 @@ fn convert_r3_host_metadata_to_input<'a>( // Copy attributes directly - they are already OutputExpressions // Handle special style_attr and class_attr if present - let mut attributes: FxHashMap, OutputExpression<'a>> = FxHashMap::default(); + let mut attributes: FxHashMap, OutputExpression<'a>> = FxHashMap::default(); for (key, value) in host.attributes.iter() { // Use clone_in to deep clone the OutputExpression with the allocator @@ -692,7 +701,7 @@ fn convert_r3_host_metadata_to_input<'a>( LiteralExpr { value: LiteralValue::String(style_attr.clone()), source_span: None }, allocator, )); - attributes.insert(Atom::from("style"), expr); + attributes.insert(Ident::from("style"), expr); } if let Some(ref class_attr) = host.class_attr { @@ -700,7 +709,7 @@ fn convert_r3_host_metadata_to_input<'a>( LiteralExpr { value: LiteralValue::String(class_attr.clone()), source_span: None }, allocator, )); - attributes.insert(Atom::from("class"), expr); + attributes.insert(Ident::from("class"), expr); } HostBindingInput { @@ -762,12 +771,12 @@ fn create_feature_call<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(feature_name), + name: Ident::from(feature_name), optional: false, source_span: None, }, @@ -795,12 +804,12 @@ fn create_feature_ref<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(feature_name), + name: Ident::from(feature_name), optional: false, source_span: None, }, @@ -849,12 +858,12 @@ fn create_host_directives_feature_arg<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::FORWARD_REF), + name: Ident::from(Identifiers::FORWARD_REF), optional: false, source_span: None, }, @@ -876,7 +885,7 @@ fn create_host_directives_feature_arg<'a>( }; entries.push(LiteralMapEntry { - key: Atom::from("directive"), + key: Ident::from("directive"), value: directive_expr, quoted: false, }); @@ -885,7 +894,7 @@ fn create_host_directives_feature_arg<'a>( if !hd.inputs.is_empty() { let inputs_array = create_host_directive_mappings_array(allocator, &hd.inputs); entries.push(LiteralMapEntry { - key: Atom::from("inputs"), + key: Ident::from("inputs"), value: inputs_array, quoted: false, }); @@ -895,7 +904,7 @@ fn create_host_directives_feature_arg<'a>( if !hd.outputs.is_empty() { let outputs_array = create_host_directive_mappings_array(allocator, &hd.outputs); entries.push(LiteralMapEntry { - key: Atom::from("outputs"), + key: Ident::from("outputs"), value: outputs_array, quoted: false, }); @@ -915,23 +924,23 @@ fn create_host_directives_feature_arg<'a>( /// Creates a host directive mappings array. /// -/// Format: `['publicName', 'internalName', 'publicName2', 'internalName2']` +/// Format: `['internalName', 'publicName', 'internalName2', 'publicName2']` /// /// Shared between directive and component compilers, mirroring Angular's /// `createHostDirectivesMappingArray` in `view/compiler.ts`. pub(crate) fn create_host_directive_mappings_array<'a>( allocator: &'a Allocator, - mappings: &[(Atom<'a>, Atom<'a>)], + mappings: &[(Ident<'a>, Ident<'a>)], ) -> OutputExpression<'a> { let mut entries = Vec::with_capacity_in(mappings.len() * 2, allocator); for (public_name, internal_name) in mappings { entries.push(OutputExpression::Literal(Box::new_in( - LiteralExpr { value: LiteralValue::String(public_name.clone()), source_span: None }, + LiteralExpr { value: LiteralValue::String(internal_name.clone()), source_span: None }, allocator, ))); entries.push(OutputExpression::Literal(Box::new_in( - LiteralExpr { value: LiteralValue::String(internal_name.clone()), source_span: None }, + LiteralExpr { value: LiteralValue::String(public_name.clone()), source_span: None }, allocator, ))); } @@ -952,16 +961,16 @@ mod tests { fn test_compile_simple_directive() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("MyDirective"), source_span: None }, + ReadVarExpr { name: Ident::from("MyDirective"), source_span: None }, &allocator, )); let metadata = R3DirectiveMetadata { - name: Atom::from("MyDirective"), + name: Ident::from("MyDirective"), r#type: type_expr, type_argument_count: 0, deps: None, - selector: Some(Atom::from("[myDir]")), + selector: Some(Ident::from("[myDir]")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: R3HostMetadata::new(&allocator), @@ -990,22 +999,22 @@ mod tests { fn test_compile_directive_with_inputs_outputs() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TestDirective"), source_span: None }, + ReadVarExpr { name: Ident::from("TestDirective"), source_span: None }, &allocator, )); let mut inputs = Vec::new_in(&allocator); - inputs.push(R3InputMetadata::simple(Atom::from("myInput"))); + inputs.push(R3InputMetadata::simple(Ident::from("myInput"))); let mut outputs = Vec::new_in(&allocator); - outputs.push((Atom::from("myOutput"), Atom::from("myOutput"))); + outputs.push((Ident::from("myOutput"), Ident::from("myOutput"))); let metadata = R3DirectiveMetadata { - name: Atom::from("TestDirective"), + name: Ident::from("TestDirective"), r#type: type_expr, type_argument_count: 0, deps: None, - selector: Some(Atom::from("[test]")), + selector: Some(Ident::from("[test]")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: R3HostMetadata::new(&allocator), @@ -1035,7 +1044,7 @@ mod tests { // Test: Simple input (same name, no transform) -> just string let allocator = Allocator::default(); let mut inputs = Vec::new_in(&allocator); - inputs.push(R3InputMetadata::simple(Atom::from("value"))); + inputs.push(R3InputMetadata::simple(Ident::from("value"))); let result = create_inputs_literal(&allocator, &inputs); let emitter = JsEmitter::new(); @@ -1053,8 +1062,8 @@ mod tests { let allocator = Allocator::default(); let mut inputs = Vec::new_in(&allocator); inputs.push(R3InputMetadata { - class_property_name: Atom::from("count"), - binding_property_name: Atom::from("itemCount"), + class_property_name: Ident::from("count"), + binding_property_name: Ident::from("itemCount"), required: false, is_signal: false, transform_function: None, @@ -1079,12 +1088,12 @@ mod tests { let allocator = Allocator::default(); let mut inputs = Vec::new_in(&allocator); let transform_fn = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("booleanAttribute"), source_span: None }, + ReadVarExpr { name: Ident::from("booleanAttribute"), source_span: None }, &allocator, )); inputs.push(R3InputMetadata { - class_property_name: Atom::from("disabled"), - binding_property_name: Atom::from("disabled"), + class_property_name: Ident::from("disabled"), + binding_property_name: Ident::from("disabled"), required: false, is_signal: false, transform_function: Some(transform_fn), @@ -1108,8 +1117,8 @@ mod tests { let allocator = Allocator::default(); let mut inputs = Vec::new_in(&allocator); inputs.push(R3InputMetadata { - class_property_name: Atom::from("border"), - binding_property_name: Atom::from("border"), + class_property_name: Ident::from("border"), + binding_property_name: Ident::from("border"), required: false, is_signal: true, transform_function: None, @@ -1140,8 +1149,8 @@ mod tests { let allocator = Allocator::default(); let mut inputs = Vec::new_in(&allocator); inputs.push(R3InputMetadata { - class_property_name: Atom::from("borderWidth"), - binding_property_name: Atom::from("border"), + class_property_name: Ident::from("borderWidth"), + binding_property_name: Ident::from("border"), required: false, is_signal: true, transform_function: None, @@ -1166,12 +1175,12 @@ mod tests { let allocator = Allocator::default(); let mut inputs = Vec::new_in(&allocator); let transform_fn = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("toNumber"), source_span: None }, + ReadVarExpr { name: Ident::from("toNumber"), source_span: None }, &allocator, )); inputs.push(R3InputMetadata { - class_property_name: Atom::from("count"), - binding_property_name: Atom::from("count"), + class_property_name: Ident::from("count"), + binding_property_name: Ident::from("count"), required: false, is_signal: true, transform_function: Some(transform_fn), @@ -1196,12 +1205,12 @@ mod tests { let mut inputs = Vec::new_in(&allocator); // Simple input (flags = 0, uses string format) - inputs.push(R3InputMetadata::simple(Atom::from("simple"))); + inputs.push(R3InputMetadata::simple(Ident::from("simple"))); // Signal input (flags = 1) inputs.push(R3InputMetadata { - class_property_name: Atom::from("signalInput"), - binding_property_name: Atom::from("signalInput"), + class_property_name: Ident::from("signalInput"), + binding_property_name: Ident::from("signalInput"), required: false, is_signal: true, transform_function: None, @@ -1209,12 +1218,12 @@ mod tests { // Transform input (flags = 2) let transform_fn = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("booleanAttribute"), source_span: None }, + ReadVarExpr { name: Ident::from("booleanAttribute"), source_span: None }, &allocator, )); inputs.push(R3InputMetadata { - class_property_name: Atom::from("boolInput"), - binding_property_name: Atom::from("boolInput"), + class_property_name: Ident::from("boolInput"), + binding_property_name: Ident::from("boolInput"), required: false, is_signal: false, transform_function: Some(transform_fn), @@ -1249,16 +1258,16 @@ mod tests { fn test_compile_directive_with_features() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("FeatureDirective"), source_span: None }, + ReadVarExpr { name: Ident::from("FeatureDirective"), source_span: None }, &allocator, )); let metadata = R3DirectiveMetadata { - name: Atom::from("FeatureDirective"), + name: Ident::from("FeatureDirective"), r#type: type_expr, type_argument_count: 0, deps: None, - selector: Some(Atom::from("[feature]")), + selector: Some(Ident::from("[feature]")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: R3HostMetadata::new(&allocator), @@ -1287,20 +1296,20 @@ mod tests { fn test_compile_directive_with_export_as() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ExportDirective"), source_span: None }, + ReadVarExpr { name: Ident::from("ExportDirective"), source_span: None }, &allocator, )); let mut export_as = Vec::new_in(&allocator); - export_as.push(Atom::from("myExport")); - export_as.push(Atom::from("otherExport")); + export_as.push(Ident::from("myExport")); + export_as.push(Ident::from("otherExport")); let metadata = R3DirectiveMetadata { - name: Atom::from("ExportDirective"), + name: Ident::from("ExportDirective"), r#type: type_expr, type_argument_count: 0, deps: None, - selector: Some(Atom::from("[export]")), + selector: Some(Ident::from("[export]")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: R3HostMetadata::new(&allocator), @@ -1331,16 +1340,16 @@ mod tests { // This was a bug where the selector was being kept as a single string ["ng-template[body]"] let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("BodyTemplateDirective"), source_span: None }, + ReadVarExpr { name: Ident::from("BodyTemplateDirective"), source_span: None }, &allocator, )); let metadata = R3DirectiveMetadata { - name: Atom::from("BodyTemplateDirective"), + name: Ident::from("BodyTemplateDirective"), r#type: type_expr, type_argument_count: 0, deps: None, - selector: Some(Atom::from("ng-template[body]")), + selector: Some(Ident::from("ng-template[body]")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: R3HostMetadata::new(&allocator), @@ -1386,16 +1395,16 @@ mod tests { // (8 = CLASS flag) let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("PrimaryButtonDirective"), source_span: None }, + ReadVarExpr { name: Ident::from("PrimaryButtonDirective"), source_span: None }, &allocator, )); let metadata = R3DirectiveMetadata { - name: Atom::from("PrimaryButtonDirective"), + name: Ident::from("PrimaryButtonDirective"), r#type: type_expr, type_argument_count: 0, deps: None, - selector: Some(Atom::from("button.primary")), + selector: Some(Ident::from("button.primary")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: R3HostMetadata::new(&allocator), @@ -1431,17 +1440,17 @@ mod tests { // ["publicName", "internalName"], NOT objects {publicName: "internalName"} let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TooltipTrigger"), source_span: None }, + ReadVarExpr { name: Ident::from("TooltipTrigger"), source_span: None }, &allocator, )); let directive_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("BrnTooltipTrigger"), source_span: None }, + ReadVarExpr { name: Ident::from("BrnTooltipTrigger"), source_span: None }, &allocator, )); let mut host_directive_inputs = Vec::new_in(&allocator); - host_directive_inputs.push((Atom::from("uTooltip"), Atom::from("brnTooltipTrigger"))); + host_directive_inputs.push((Ident::from("uTooltip"), Ident::from("brnTooltipTrigger"))); let mut host_directives = Vec::new_in(&allocator); host_directives.push(R3HostDirectiveMetadata { @@ -1452,11 +1461,11 @@ mod tests { }); let metadata = R3DirectiveMetadata { - name: Atom::from("TooltipTrigger"), + name: Ident::from("TooltipTrigger"), r#type: type_expr, type_argument_count: 0, deps: None, - selector: Some(Atom::from("[uTooltip]")), + selector: Some(Ident::from("[uTooltip]")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: R3HostMetadata::new(&allocator), @@ -1476,10 +1485,11 @@ mod tests { let output = emitter.emit_expression(&result.expression); let normalized = output.replace([' ', '\n', '\t'], ""); - // Must contain flat array format: inputs:["uTooltip","brnTooltipTrigger"] + // Must contain flat array format: inputs:["brnTooltipTrigger","uTooltip"] + // (internalName first, then publicName — matching Angular's createHostDirectivesMappingArray) assert!( - normalized.contains(r#"inputs:["uTooltip","brnTooltipTrigger"]"#), - "Host directive inputs should be flat array [\"publicName\",\"internalName\"], not object. Got:\n{}", + normalized.contains(r#"inputs:["brnTooltipTrigger","uTooltip"]"#), + "Host directive inputs should be flat array [\"internalName\",\"publicName\"]. Got:\n{}", output ); // Must NOT contain object format: inputs:{uTooltip:"brnTooltipTrigger"} @@ -1495,17 +1505,17 @@ mod tests { // Issue #67: output mappings must also be flat arrays let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("MyDirective"), source_span: None }, + ReadVarExpr { name: Ident::from("MyDirective"), source_span: None }, &allocator, )); let directive_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ClickTracker"), source_span: None }, + ReadVarExpr { name: Ident::from("ClickTracker"), source_span: None }, &allocator, )); let mut host_directive_outputs = Vec::new_in(&allocator); - host_directive_outputs.push((Atom::from("clicked"), Atom::from("trackClick"))); + host_directive_outputs.push((Ident::from("clicked"), Ident::from("trackClick"))); let mut host_directives = Vec::new_in(&allocator); host_directives.push(R3HostDirectiveMetadata { @@ -1516,11 +1526,11 @@ mod tests { }); let metadata = R3DirectiveMetadata { - name: Atom::from("MyDirective"), + name: Ident::from("MyDirective"), r#type: type_expr, type_argument_count: 0, deps: None, - selector: Some(Atom::from("[myDir]")), + selector: Some(Ident::from("[myDir]")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: R3HostMetadata::new(&allocator), @@ -1540,11 +1550,105 @@ mod tests { let output = emitter.emit_expression(&result.expression); let normalized = output.replace([' ', '\n', '\t'], ""); - // Must contain flat array format: outputs:["clicked","trackClick"] + // Must contain flat array format: outputs:["trackClick","clicked"] + // (internalName first, then publicName — matching Angular's createHostDirectivesMappingArray) assert!( - normalized.contains(r#"outputs:["clicked","trackClick"]"#), - "Host directive outputs should be flat array. Got:\n{}", + normalized.contains(r#"outputs:["trackClick","clicked"]"#), + "Host directive outputs should be flat array [\"internalName\",\"publicName\"]. Got:\n{}", output ); } + + #[test] + fn test_create_inputs_literal_quotes_dotted_key() { + let allocator = Allocator::default(); + let inputs = vec![R3InputMetadata { + class_property_name: Ident::from("fxFlexAlign.xs"), + binding_property_name: Ident::from("fxFlexAlign.xs"), + required: false, + is_signal: false, + transform_function: None, + }]; + let expr = create_inputs_literal(&allocator, &inputs).unwrap(); + let emitter = JsEmitter::new(); + let output = emitter.emit_expression(&expr); + assert!( + output.contains(r#""fxFlexAlign.xs""#), + "Dotted key should be quoted. Got:\n{output}" + ); + } + + #[test] + fn test_create_inputs_literal_quotes_hyphenated_key() { + let allocator = Allocator::default(); + let inputs = vec![R3InputMetadata { + class_property_name: Ident::from("fxFlexAlign.lt-sm"), + binding_property_name: Ident::from("fxFlexAlign.lt-sm"), + required: false, + is_signal: false, + transform_function: None, + }]; + let expr = create_inputs_literal(&allocator, &inputs).unwrap(); + let emitter = JsEmitter::new(); + let output = emitter.emit_expression(&expr); + assert!( + output.contains(r#""fxFlexAlign.lt-sm""#), + "Hyphenated key should be quoted. Got:\n{output}" + ); + } + + #[test] + fn test_create_inputs_literal_no_quotes_for_simple_identifier() { + let allocator = Allocator::default(); + let inputs = vec![R3InputMetadata { + class_property_name: Ident::from("fxFlexAlign"), + binding_property_name: Ident::from("fxFlexAlign"), + required: false, + is_signal: false, + transform_function: None, + }]; + let expr = create_inputs_literal(&allocator, &inputs).unwrap(); + let emitter = JsEmitter::new(); + let output = emitter.emit_expression(&expr); + // Key should be bare (unquoted), followed by colon + assert!( + output.contains("fxFlexAlign:"), + "Simple identifier key should be bare. Got:\n{output}" + ); + // Key should NOT be quoted — check that no quoted form appears before the colon + assert!( + !output.contains(r#""fxFlexAlign":"#), + "Simple identifier key should not be quoted. Got:\n{output}" + ); + } + + #[test] + fn test_create_outputs_literal_quotes_dotted_key() { + let allocator = Allocator::default(); + let outputs = vec![(Ident::from("activate.xs"), Ident::from("activateXs"))]; + let expr = create_outputs_literal(&allocator, &outputs).unwrap(); + let emitter = JsEmitter::new(); + let output = emitter.emit_expression(&expr); + assert!( + output.contains(r#""activate.xs""#), + "Dotted output key should be quoted. Got:\n{output}" + ); + } + + #[test] + fn test_create_outputs_literal_no_quotes_for_simple_identifier() { + let allocator = Allocator::default(); + let outputs = vec![(Ident::from("activate"), Ident::from("activate"))]; + let expr = create_outputs_literal(&allocator, &outputs).unwrap(); + let emitter = JsEmitter::new(); + let output = emitter.emit_expression(&expr); + assert!( + output.contains("activate:"), + "Simple identifier output key should be bare. Got:\n{output}" + ); + assert!( + !output.contains(r#""activate":"#), + "Simple identifier output key should not be quoted. Got:\n{output}" + ); + } } diff --git a/crates/oxc_angular_compiler/src/directive/decorator.rs b/crates/oxc_angular_compiler/src/directive/decorator.rs index de018f214..834194333 100644 --- a/crates/oxc_angular_compiler/src/directive/decorator.rs +++ b/crates/oxc_angular_compiler/src/directive/decorator.rs @@ -8,7 +8,7 @@ use oxc_ast::ast::{ Argument, ArrayExpressionElement, Class, ClassElement, Decorator, Expression, MethodDefinitionKind, ObjectPropertyKind, PropertyKey, }; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use super::metadata::{ R3DirectiveMetadata, R3DirectiveMetadataBuilder, R3HostDirectiveMetadata, R3HostMetadata, @@ -79,7 +79,7 @@ pub fn extract_directive_metadata<'a>( implicit_standalone: bool, ) -> Option> { // Get the class name - let class_name: Atom<'a> = class.id.as_ref()?.name.clone().into(); + let class_name: Ident<'a> = class.id.as_ref()?.name.clone().into(); // Find the @Directive decorator let directive_decorator = find_directive_decorator(&class.decorators)?; @@ -136,7 +136,7 @@ pub fn extract_directive_metadata<'a>( let trimmed = part.trim(); if !trimmed.is_empty() { builder = builder - .add_export_as(Atom::from(allocator.alloc_str(trimmed))); + .add_export_as(Ident::from(allocator.alloc_str(trimmed))); } } } @@ -297,7 +297,7 @@ fn extract_param_dependency<'a>( let mut self_ = false; let mut host = false; let mut inject_token: Option> = None; - let mut attribute_name: Option> = None; + let mut attribute_name: Option> = None; for decorator in ¶m.decorators { if let Some(name) = get_decorator_name_from_expr(&decorator.expression) { @@ -318,7 +318,7 @@ fn extract_param_dependency<'a>( // @Attribute('attrName') - extract the attribute name if let Expression::CallExpression(call) = &decorator.expression { if let Some(Argument::StringLiteral(s)) = call.arguments.first() { - attribute_name = Some(s.value.clone()); + attribute_name = Some(s.value.clone().into()); } } } @@ -354,7 +354,7 @@ fn extract_param_dependency<'a>( } /// Get the name of a decorator from its expression. -fn get_decorator_name_from_expr<'a>(expr: &'a Expression<'a>) -> Option> { +fn get_decorator_name_from_expr<'a>(expr: &'a Expression<'a>) -> Option> { match expr { // @Optional Expression::Identifier(id) => Some(id.name.clone().into()), @@ -433,21 +433,21 @@ fn has_ng_on_changes_method(class: &Class<'_>) -> bool { } /// Get the name of a property key as a string. -fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { +fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { match key { PropertyKey::StaticIdentifier(id) => Some(id.name.clone().into()), - PropertyKey::StringLiteral(lit) => Some(lit.value.clone()), + PropertyKey::StringLiteral(lit) => Some(lit.value.clone().into()), _ => None, } } /// Extract a string value from an expression. -fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { +fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { match expr { - Expression::StringLiteral(lit) => Some(lit.value.clone()), + Expression::StringLiteral(lit) => Some(lit.value.clone().into()), Expression::TemplateLiteral(tpl) if tpl.expressions.is_empty() => { // Simple template literal with no expressions - tpl.quasis.first().and_then(|q| q.value.cooked.clone()) + tpl.quasis.first().and_then(|q| q.value.cooked.clone().map(Into::into)) } _ => None, } @@ -456,7 +456,7 @@ fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { /// Extract a boolean value from an expression. fn extract_boolean_value(expr: &Expression<'_>) -> Option { match expr { - Expression::BooleanLiteral(lit) => Some(lit.value), + Expression::BooleanLiteral(lit) => Some(lit.value.into()), _ => None, } } @@ -651,7 +651,7 @@ fn is_forward_ref_call(callee: &Expression<'_>) -> bool { /// Extract the directive name from a forwardRef argument. /// /// Handles: `forwardRef(() => MyDirective)` -fn extract_forward_ref_directive_name<'a>(arg: Option<&Argument<'a>>) -> Option> { +fn extract_forward_ref_directive_name<'a>(arg: Option<&Argument<'a>>) -> Option> { let arg = arg?; match arg { Argument::ArrowFunctionExpression(arrow) => { @@ -682,7 +682,7 @@ fn extract_forward_ref_directive_name<'a>(arg: Option<&Argument<'a>>) -> Option< fn extract_io_mappings<'a>( allocator: &'a Allocator, expr: &Expression<'a>, -) -> Vec<'a, (Atom<'a>, Atom<'a>)> { +) -> Vec<'a, (Ident<'a>, Ident<'a>)> { let mut result = Vec::new_in(allocator); let Expression::ArrayExpression(arr) = expr else { @@ -707,7 +707,7 @@ fn extract_io_mappings<'a>( fn parse_mapping_element<'a>( allocator: &'a Allocator, element: &ArrayExpressionElement<'a>, -) -> Option<(Atom<'a>, Atom<'a>)> { +) -> Option<(Ident<'a>, Ident<'a>)> { match element { // Simple string: "color" - same public and internal name ArrayExpressionElement::StringLiteral(lit) => { @@ -718,12 +718,12 @@ fn parse_mapping_element<'a>( let internal_name = value[..colon_pos].trim(); let public_name = value[colon_pos + 1..].trim(); Some(( - Atom::from(allocator.alloc_str(public_name)), - Atom::from(allocator.alloc_str(internal_name)), + Ident::from(allocator.alloc_str(public_name)), + Ident::from(allocator.alloc_str(internal_name)), )) } else { // Same name for both - Some((lit.value.clone(), lit.value.clone())) + Some((lit.value.clone().into(), lit.value.clone().into())) } } @@ -732,11 +732,15 @@ fn parse_mapping_element<'a>( let elements = &arr.elements; if elements.len() >= 2 { let internal = match elements.first() { - Some(ArrayExpressionElement::StringLiteral(lit)) => Some(lit.value.clone()), + Some(ArrayExpressionElement::StringLiteral(lit)) => { + Some(lit.value.clone().into()) + } _ => None, }; let public = match elements.get(1) { - Some(ArrayExpressionElement::StringLiteral(lit)) => Some(lit.value.clone()), + Some(ArrayExpressionElement::StringLiteral(lit)) => { + Some(lit.value.clone().into()) + } _ => None, }; if let (Some(internal_name), Some(public_name)) = (internal, public) { diff --git a/crates/oxc_angular_compiler/src/directive/definition.rs b/crates/oxc_angular_compiler/src/directive/definition.rs index 0478a7018..ba5d03e11 100644 --- a/crates/oxc_angular_compiler/src/directive/definition.rs +++ b/crates/oxc_angular_compiler/src/directive/definition.rs @@ -193,20 +193,20 @@ mod tests { use crate::output::ast::ReadVarExpr; use crate::output::emitter::JsEmitter; use oxc_allocator::Box; - use oxc_span::Atom; + use oxc_span::Ident; fn create_test_metadata<'a>(allocator: &'a Allocator) -> R3DirectiveMetadata<'a> { let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TestDirective"), source_span: None }, + ReadVarExpr { name: Ident::from("TestDirective"), source_span: None }, allocator, )); R3DirectiveMetadata { - name: Atom::from("TestDirective"), + name: Ident::from("TestDirective"), r#type: type_expr, type_argument_count: 0, deps: None, - selector: Some(Atom::from("[testDir]")), + selector: Some(Ident::from("[testDir]")), queries: Vec::new_in(allocator), view_queries: Vec::new_in(allocator), host: R3HostMetadata::new(allocator), @@ -270,16 +270,16 @@ mod tests { // an inherited factory pattern let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ChildDirective"), source_span: None }, + ReadVarExpr { name: Ident::from("ChildDirective"), source_span: None }, &allocator, )); let metadata = R3DirectiveMetadata { - name: Atom::from("ChildDirective"), + name: Ident::from("ChildDirective"), r#type: type_expr, type_argument_count: 0, deps: None, // No explicit constructor deps - selector: Some(Atom::from("[childDir]")), + selector: Some(Ident::from("[childDir]")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: R3HostMetadata::new(&allocator), @@ -309,16 +309,16 @@ mod tests { fn test_generate_fac_definition_with_empty_deps() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TestDirective"), source_span: None }, + ReadVarExpr { name: Ident::from("TestDirective"), source_span: None }, &allocator, )); let metadata = R3DirectiveMetadata { - name: Atom::from("TestDirective"), + name: Ident::from("TestDirective"), r#type: type_expr, type_argument_count: 0, deps: Some(Vec::new_in(&allocator)), // Empty deps - has constructor but no params - selector: Some(Atom::from("[testDir]")), + selector: Some(Ident::from("[testDir]")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: R3HostMetadata::new(&allocator), @@ -350,12 +350,12 @@ mod tests { fn test_generate_fac_definition_with_deps() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TestDirective"), source_span: None }, + ReadVarExpr { name: Ident::from("TestDirective"), source_span: None }, &allocator, )); let dep_token = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("SomeService"), source_span: None }, + ReadVarExpr { name: Ident::from("SomeService"), source_span: None }, &allocator, )); @@ -363,11 +363,11 @@ mod tests { deps.push(crate::factory::R3DependencyMetadata::simple(dep_token)); let metadata = R3DirectiveMetadata { - name: Atom::from("TestDirective"), + name: Ident::from("TestDirective"), r#type: type_expr, type_argument_count: 0, deps: Some(deps), - selector: Some(Atom::from("[testDir]")), + selector: Some(Ident::from("[testDir]")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: R3HostMetadata::new(&allocator), @@ -418,20 +418,20 @@ mod tests { // Create first directive with host bindings let type_expr1 = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("Dir1"), source_span: None }, + ReadVarExpr { name: Ident::from("Dir1"), source_span: None }, &allocator, )); let mut host1 = R3HostMetadata::new(&allocator); // Add a property binding which will use the constant pool - host1.properties.push((Atom::from("[attr.role]"), Atom::from("'button'"))); + host1.properties.push((Ident::from("[attr.role]"), Ident::from("'button'"))); let metadata1 = R3DirectiveMetadata { - name: Atom::from("Dir1"), + name: Ident::from("Dir1"), r#type: type_expr1, type_argument_count: 0, deps: None, - selector: Some(Atom::from("[dir1]")), + selector: Some(Ident::from("[dir1]")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: host1, @@ -456,19 +456,19 @@ mod tests { // Create second directive with host bindings using the returned pool index let type_expr2 = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("Dir2"), source_span: None }, + ReadVarExpr { name: Ident::from("Dir2"), source_span: None }, &allocator, )); let mut host2 = R3HostMetadata::new(&allocator); - host2.properties.push((Atom::from("[attr.id]"), Atom::from("'test'"))); + host2.properties.push((Ident::from("[attr.id]"), Ident::from("'test'"))); let metadata2 = R3DirectiveMetadata { - name: Atom::from("Dir2"), + name: Ident::from("Dir2"), r#type: type_expr2, type_argument_count: 0, deps: None, - selector: Some(Atom::from("[dir2]")), + selector: Some(Ident::from("[dir2]")), queries: Vec::new_in(&allocator), view_queries: Vec::new_in(&allocator), host: host2, diff --git a/crates/oxc_angular_compiler/src/directive/metadata.rs b/crates/oxc_angular_compiler/src/directive/metadata.rs index 440dc7450..aba78a5c7 100644 --- a/crates/oxc_angular_compiler/src/directive/metadata.rs +++ b/crates/oxc_angular_compiler/src/directive/metadata.rs @@ -4,7 +4,7 @@ use oxc_allocator::{Allocator, Vec}; use oxc_ast::ast::Class; -use oxc_span::Atom; +use oxc_span::Ident; use crate::factory::R3DependencyMetadata; use crate::output::ast::OutputExpression; @@ -15,10 +15,10 @@ use crate::output::ast::OutputExpression; #[derive(Debug)] pub struct R3InputMetadata<'a> { /// The property name on the class. - pub class_property_name: Atom<'a>, + pub class_property_name: Ident<'a>, /// The binding property name (can differ from class property name). - pub binding_property_name: Atom<'a>, + pub binding_property_name: Ident<'a>, /// Whether this input is required. pub required: bool, @@ -33,7 +33,7 @@ pub struct R3InputMetadata<'a> { impl<'a> R3InputMetadata<'a> { /// Create a simple input with matching class and binding names. - pub fn simple(name: Atom<'a>) -> Self { + pub fn simple(name: Ident<'a>) -> Self { Self { class_property_name: name.clone(), binding_property_name: name, @@ -44,7 +44,7 @@ impl<'a> R3InputMetadata<'a> { } /// Create a required input. - pub fn required(name: Atom<'a>) -> Self { + pub fn required(name: Ident<'a>) -> Self { Self { class_property_name: name.clone(), binding_property_name: name, @@ -55,7 +55,7 @@ impl<'a> R3InputMetadata<'a> { } /// Create a signal-based input. - pub fn signal(name: Atom<'a>) -> Self { + pub fn signal(name: Ident<'a>) -> Self { Self { class_property_name: name.clone(), binding_property_name: name, @@ -72,7 +72,7 @@ impl<'a> R3InputMetadata<'a> { #[derive(Debug)] pub struct R3QueryMetadata<'a> { /// Name of the property on the class to update with query results. - pub property_name: Atom<'a>, + pub property_name: Ident<'a>, /// Whether to read only the first matching result. pub first: bool, @@ -103,12 +103,12 @@ pub enum QueryPredicate<'a> { Type(OutputExpression<'a>), /// String selectors. - Selectors(Vec<'a, Atom<'a>>), + Selectors(Vec<'a, Ident<'a>>), } impl<'a> R3QueryMetadata<'a> { /// Create a new query metadata with defaults. - pub fn new(allocator: &'a Allocator, property_name: Atom<'a>) -> Self { + pub fn new(allocator: &'a Allocator, property_name: Ident<'a>) -> Self { Self { property_name, first: false, @@ -128,19 +128,19 @@ impl<'a> R3QueryMetadata<'a> { #[derive(Debug)] pub struct R3HostMetadata<'a> { /// A mapping of attribute binding keys to expressions. - pub attributes: Vec<'a, (Atom<'a>, OutputExpression<'a>)>, + pub attributes: Vec<'a, (Ident<'a>, OutputExpression<'a>)>, /// A mapping of event binding keys to unparsed expressions. - pub listeners: Vec<'a, (Atom<'a>, Atom<'a>)>, + pub listeners: Vec<'a, (Ident<'a>, Ident<'a>)>, /// A mapping of property binding keys to unparsed expressions. - pub properties: Vec<'a, (Atom<'a>, Atom<'a>)>, + pub properties: Vec<'a, (Ident<'a>, Ident<'a>)>, /// Special style attribute value. - pub style_attr: Option>, + pub style_attr: Option>, /// Special class attribute value. - pub class_attr: Option>, + pub class_attr: Option>, } impl<'a> R3HostMetadata<'a> { @@ -178,11 +178,11 @@ pub struct R3HostDirectiveMetadata<'a> { /// Inputs from the host directive that will be exposed on the host. /// Key: public name, Value: internal name - pub inputs: Vec<'a, (Atom<'a>, Atom<'a>)>, + pub inputs: Vec<'a, (Ident<'a>, Ident<'a>)>, /// Outputs from the host directive that will be exposed on the host. /// Key: public name, Value: internal name - pub outputs: Vec<'a, (Atom<'a>, Atom<'a>)>, + pub outputs: Vec<'a, (Ident<'a>, Ident<'a>)>, } /// Metadata needed to compile a directive. @@ -191,7 +191,7 @@ pub struct R3HostDirectiveMetadata<'a> { #[derive(Debug)] pub struct R3DirectiveMetadata<'a> { /// Name of the directive type. - pub name: Atom<'a>, + pub name: Ident<'a>, /// An expression representing a reference to the directive itself. pub r#type: OutputExpression<'a>, @@ -203,7 +203,7 @@ pub struct R3DirectiveMetadata<'a> { pub deps: Option>>, /// Unparsed selector of the directive, or None if there was no selector. - pub selector: Option>, + pub selector: Option>, /// Content queries made by the directive. pub queries: Vec<'a, R3QueryMetadata<'a>>, @@ -221,13 +221,13 @@ pub struct R3DirectiveMetadata<'a> { pub inputs: Vec<'a, R3InputMetadata<'a>>, /// Outputs of the directive (class property name -> binding property name). - pub outputs: Vec<'a, (Atom<'a>, Atom<'a>)>, + pub outputs: Vec<'a, (Ident<'a>, Ident<'a>)>, /// Whether or not the directive inherits from another class. pub uses_inheritance: bool, /// Export names for template references. - pub export_as: Vec<'a, Atom<'a>>, + pub export_as: Vec<'a, Ident<'a>>, /// The list of providers defined in the directive. pub providers: Option>, @@ -254,19 +254,19 @@ impl<'a> R3DirectiveMetadata<'a> { /// Builder for R3DirectiveMetadata. pub struct R3DirectiveMetadataBuilder<'a> { - name: Option>, + name: Option>, r#type: Option>, type_argument_count: u32, deps: Option>>, - selector: Option>, + selector: Option>, queries: Vec<'a, R3QueryMetadata<'a>>, view_queries: Vec<'a, R3QueryMetadata<'a>>, host: R3HostMetadata<'a>, uses_on_changes: bool, inputs: Vec<'a, R3InputMetadata<'a>>, - outputs: Vec<'a, (Atom<'a>, Atom<'a>)>, + outputs: Vec<'a, (Ident<'a>, Ident<'a>)>, uses_inheritance: bool, - export_as: Vec<'a, Atom<'a>>, + export_as: Vec<'a, Ident<'a>>, providers: Option>, is_standalone: bool, is_signal: bool, @@ -298,7 +298,7 @@ impl<'a> R3DirectiveMetadataBuilder<'a> { } /// Set the directive name. - pub fn name(mut self, name: Atom<'a>) -> Self { + pub fn name(mut self, name: Ident<'a>) -> Self { self.name = Some(name); self } @@ -322,7 +322,7 @@ impl<'a> R3DirectiveMetadataBuilder<'a> { } /// Set the selector. - pub fn selector(mut self, selector: Atom<'a>) -> Self { + pub fn selector(mut self, selector: Ident<'a>) -> Self { self.selector = Some(selector); self } @@ -358,7 +358,7 @@ impl<'a> R3DirectiveMetadataBuilder<'a> { } /// Add an output. - pub fn add_output(mut self, class_name: Atom<'a>, binding_name: Atom<'a>) -> Self { + pub fn add_output(mut self, class_name: Ident<'a>, binding_name: Ident<'a>) -> Self { self.outputs.push((class_name, binding_name)); self } @@ -370,7 +370,7 @@ impl<'a> R3DirectiveMetadataBuilder<'a> { } /// Add an export name. - pub fn add_export_as(mut self, export_name: Atom<'a>) -> Self { + pub fn add_export_as(mut self, export_name: Ident<'a>) -> Self { self.export_as.push(export_name); self } @@ -440,7 +440,8 @@ impl<'a> R3DirectiveMetadataBuilder<'a> { let host_bindings = super::property_decorators::extract_host_bindings(allocator, class); for (host_prop, class_prop) in host_bindings { // Add to host.properties with wrapped key - let wrapped_key = Atom::from(allocator.alloc_str(&format!("[{}]", host_prop.as_str()))); + let wrapped_key = + Ident::from(allocator.alloc_str(&format!("[{}]", host_prop.as_str()))); self.host.properties.push((wrapped_key, class_prop)); } @@ -451,15 +452,15 @@ impl<'a> R3DirectiveMetadataBuilder<'a> { for (event_name, method_name, args) in host_listeners { // Wrap event name: "click" -> "(click)" let wrapped_key = - Atom::from(allocator.alloc_str(&format!("({})", event_name.as_str()))); + Ident::from(allocator.alloc_str(&format!("({})", event_name.as_str()))); // Build method expression with args: "handleClick" + ["$event"] -> "handleClick($event)" let method_expr = if args.is_empty() { - Atom::from(allocator.alloc_str(&format!("{}()", method_name.as_str()))) + Ident::from(allocator.alloc_str(&format!("{}()", method_name.as_str()))) } else { let args_str: String = args.iter().map(|a| a.as_str()).collect::>().join(","); - Atom::from(allocator.alloc_str(&format!("{}({})", method_name.as_str(), args_str))) + Ident::from(allocator.alloc_str(&format!("{}({})", method_name.as_str(), args_str))) }; // Add to host.listeners @@ -542,9 +543,9 @@ mod tests { fn test_builder_basic() { let allocator = Allocator::default(); let builder = R3DirectiveMetadataBuilder::new(&allocator) - .name(Atom::from("TestDirective")) - .r#type(OutputAstBuilder::variable(&allocator, Atom::from("TestDirective"))) - .selector(Atom::from("[test]")); + .name(Ident::from("TestDirective")) + .r#type(OutputAstBuilder::variable(&allocator, Ident::from("TestDirective"))) + .selector(Ident::from("[test]")); let metadata = builder.build(); assert!(metadata.is_some()); @@ -559,7 +560,7 @@ mod tests { fn test_builder_missing_name_returns_none() { let allocator = Allocator::default(); let builder = R3DirectiveMetadataBuilder::new(&allocator) - .r#type(OutputAstBuilder::variable(&allocator, Atom::from("TestDirective"))); + .r#type(OutputAstBuilder::variable(&allocator, Ident::from("TestDirective"))); let metadata = builder.build(); assert!(metadata.is_none()); @@ -568,7 +569,8 @@ mod tests { #[test] fn test_builder_missing_type_returns_none() { let allocator = Allocator::default(); - let builder = R3DirectiveMetadataBuilder::new(&allocator).name(Atom::from("TestDirective")); + let builder = + R3DirectiveMetadataBuilder::new(&allocator).name(Ident::from("TestDirective")); let metadata = builder.build(); assert!(metadata.is_none()); @@ -593,8 +595,8 @@ mod tests { assert!(class.is_some()); let builder = R3DirectiveMetadataBuilder::new(&allocator) - .name(Atom::from("TestDirective")) - .r#type(OutputAstBuilder::variable(&allocator, Atom::from("TestDirective"))) + .name(Ident::from("TestDirective")) + .r#type(OutputAstBuilder::variable(&allocator, Ident::from("TestDirective"))) .extract_from_class(&allocator, class.unwrap()); let metadata = builder.build(); @@ -631,8 +633,8 @@ mod tests { assert!(class.is_some()); let builder = R3DirectiveMetadataBuilder::new(&allocator) - .name(Atom::from("TestDirective")) - .r#type(OutputAstBuilder::variable(&allocator, Atom::from("TestDirective"))) + .name(Ident::from("TestDirective")) + .r#type(OutputAstBuilder::variable(&allocator, Ident::from("TestDirective"))) .extract_from_class(&allocator, class.unwrap()); let metadata = builder.build(); @@ -664,8 +666,8 @@ mod tests { assert!(class.is_some()); let builder = R3DirectiveMetadataBuilder::new(&allocator) - .name(Atom::from("TestComponent")) - .r#type(OutputAstBuilder::variable(&allocator, Atom::from("TestComponent"))) + .name(Ident::from("TestComponent")) + .r#type(OutputAstBuilder::variable(&allocator, Ident::from("TestComponent"))) .extract_from_class(&allocator, class.unwrap()); let metadata = builder.build(); @@ -697,8 +699,8 @@ mod tests { assert!(class.is_some()); let builder = R3DirectiveMetadataBuilder::new(&allocator) - .name(Atom::from("TestComponent")) - .r#type(OutputAstBuilder::variable(&allocator, Atom::from("TestComponent"))) + .name(Ident::from("TestComponent")) + .r#type(OutputAstBuilder::variable(&allocator, Ident::from("TestComponent"))) .extract_from_class(&allocator, class.unwrap()); let metadata = builder.build(); @@ -730,8 +732,8 @@ mod tests { assert!(class.is_some()); let builder = R3DirectiveMetadataBuilder::new(&allocator) - .name(Atom::from("TestDirective")) - .r#type(OutputAstBuilder::variable(&allocator, Atom::from("TestDirective"))) + .name(Ident::from("TestDirective")) + .r#type(OutputAstBuilder::variable(&allocator, Ident::from("TestDirective"))) .extract_from_class(&allocator, class.unwrap()); let metadata = builder.build(); @@ -765,8 +767,8 @@ mod tests { assert!(class.is_some()); let builder = R3DirectiveMetadataBuilder::new(&allocator) - .name(Atom::from("TestDirective")) - .r#type(OutputAstBuilder::variable(&allocator, Atom::from("TestDirective"))) + .name(Ident::from("TestDirective")) + .r#type(OutputAstBuilder::variable(&allocator, Ident::from("TestDirective"))) .extract_from_class(&allocator, class.unwrap()); let metadata = builder.build(); @@ -803,8 +805,8 @@ mod tests { assert!(class.is_some()); let builder = R3DirectiveMetadataBuilder::new(&allocator) - .name(Atom::from("TestComponent")) - .r#type(OutputAstBuilder::variable(&allocator, Atom::from("TestComponent"))) + .name(Ident::from("TestComponent")) + .r#type(OutputAstBuilder::variable(&allocator, Ident::from("TestComponent"))) .extract_from_class(&allocator, class.unwrap()); let metadata = builder.build(); @@ -833,8 +835,8 @@ mod tests { assert!(class.is_some()); let builder = R3DirectiveMetadataBuilder::new(&allocator) - .name(Atom::from("EmptyDirective")) - .r#type(OutputAstBuilder::variable(&allocator, Atom::from("EmptyDirective"))) + .name(Ident::from("EmptyDirective")) + .r#type(OutputAstBuilder::variable(&allocator, Ident::from("EmptyDirective"))) .extract_from_class(&allocator, class.unwrap()); let metadata = builder.build(); @@ -865,9 +867,9 @@ mod tests { // Pre-add an input before extract_from_class let builder = R3DirectiveMetadataBuilder::new(&allocator) - .name(Atom::from("TestDirective")) - .r#type(OutputAstBuilder::variable(&allocator, Atom::from("TestDirective"))) - .add_input(R3InputMetadata::simple(Atom::from("existingInput"))) + .name(Ident::from("TestDirective")) + .r#type(OutputAstBuilder::variable(&allocator, Ident::from("TestDirective"))) + .add_input(R3InputMetadata::simple(Ident::from("existingInput"))) .extract_from_class(&allocator, class.unwrap()); let metadata = builder.build(); diff --git a/crates/oxc_angular_compiler/src/directive/property_decorators.rs b/crates/oxc_angular_compiler/src/directive/property_decorators.rs index 11c200512..cf8c0d61e 100644 --- a/crates/oxc_angular_compiler/src/directive/property_decorators.rs +++ b/crates/oxc_angular_compiler/src/directive/property_decorators.rs @@ -16,7 +16,7 @@ use oxc_ast::ast::{ Argument, ArrayExpressionElement, Class, ClassElement, Decorator, Expression, MethodDefinitionKind, ObjectPropertyKind, PropertyKey, }; -use oxc_span::Atom; +use oxc_span::Ident; use super::metadata::{QueryPredicate, R3InputMetadata, R3QueryMetadata}; use crate::output::ast::OutputExpression; @@ -50,10 +50,10 @@ fn find_decorator_by_name<'a>( /// Get the property key name as an Atom. /// /// Handles both identifier keys and string literal keys. -fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { +fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { match key { PropertyKey::StaticIdentifier(id) => Some(id.name.clone().into()), - PropertyKey::StringLiteral(lit) => Some(lit.value.clone()), + PropertyKey::StringLiteral(lit) => Some(lit.value.clone().into()), _ => None, } } @@ -61,11 +61,11 @@ fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { /// Extract a string value from an expression. /// /// Handles string literals and simple template literals (no expressions). -fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { +fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { match expr { - Expression::StringLiteral(lit) => Some(lit.value.clone()), + Expression::StringLiteral(lit) => Some(lit.value.clone().into()), Expression::TemplateLiteral(tpl) if tpl.expressions.is_empty() => { - tpl.quasis.first().and_then(|q| q.value.cooked.clone()) + tpl.quasis.first().and_then(|q| q.value.cooked.clone().map(Into::into)) } _ => None, } @@ -74,7 +74,7 @@ fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { /// Extract a boolean value from an expression. fn extract_boolean_value(expr: &Expression<'_>) -> Option { match expr { - Expression::BooleanLiteral(lit) => Some(lit.value), + Expression::BooleanLiteral(lit) => Some(lit.value.into()), _ => None, } } @@ -128,7 +128,7 @@ fn try_unwrap_forward_ref<'a>(expr: &'a Expression<'a>) -> Option<&'a Expression /// Parsed @Input decorator configuration. struct InputConfig<'a> { /// Alias name for the input binding (different from property name). - alias: Option>, + alias: Option>, /// Whether this input is required. required: bool, /// Transform function for the input value. @@ -162,7 +162,7 @@ fn parse_input_config<'a>( match first_arg { // @Input('alias') Argument::StringLiteral(lit) => { - InputConfig { alias: Some(lit.value.clone()), ..Default::default() } + InputConfig { alias: Some(lit.value.clone().into()), ..Default::default() } } // @Input({ alias: 'name', required: true, transform: fn }) @@ -205,7 +205,7 @@ struct ModelMapping<'a> { input: R3InputMetadata<'a>, /// The output metadata (class property name, binding property name). /// Output binding name is always `inputName + "Change"`. - output: (Atom<'a>, Atom<'a>), + output: (Ident<'a>, Ident<'a>), } /// Try to detect and parse a signal-based model from a property initializer. @@ -224,7 +224,7 @@ struct ModelMapping<'a> { fn try_parse_signal_model<'a>( allocator: &'a Allocator, value: &Expression<'a>, - property_name: Atom<'a>, + property_name: Ident<'a>, ) -> Option> { // Check if the value is a call expression let call_expr = match value { @@ -247,7 +247,7 @@ fn try_parse_signal_model<'a>( } else if member.property.name == "model" { // Handle namespaced calls like `core.model()` if let Expression::Identifier(_) = &member.object { - let output_binding = Atom::from( + let output_binding = Ident::from( allocator.alloc_str(&format!("{}Change", property_name.as_str())), ); return Some(ModelMapping { @@ -274,7 +274,7 @@ fn try_parse_signal_model<'a>( // For model.required(): first arg is options let options_arg_index = if is_required { 0 } else { 1 }; - let mut alias: Option> = None; + let mut alias: Option> = None; if let Some(options_arg) = call_expr.arguments.get(options_arg_index) { if let Argument::ObjectExpression(obj) = options_arg { @@ -295,7 +295,7 @@ fn try_parse_signal_model<'a>( let binding_property_name = alias.unwrap_or_else(|| property_name.clone()); // Output binding name is always `bindingPropertyName + "Change"` let output_binding_name = - Atom::from(allocator.alloc_str(&format!("{}Change", binding_property_name.as_str()))); + Ident::from(allocator.alloc_str(&format!("{}Change", binding_property_name.as_str()))); Some(ModelMapping { input: R3InputMetadata { @@ -324,8 +324,8 @@ fn try_parse_signal_model<'a>( /// Based on Angular's `output_function.ts` in the compiler-cli. fn try_parse_signal_output<'a>( value: &Expression<'a>, - property_name: Atom<'a>, -) -> Option<(Atom<'a>, Atom<'a>)> { + property_name: Ident<'a>, +) -> Option<(Ident<'a>, Ident<'a>)> { // Check if the value is a call expression let call_expr = match value { Expression::CallExpression(call) => call, @@ -353,7 +353,7 @@ fn try_parse_signal_output<'a>( // Parse options from the first argument (options are the first arg for output()) // Options can contain an alias - let mut alias: Option> = None; + let mut alias: Option> = None; if let Some(first_arg) = call_expr.arguments.first() { if let Argument::ObjectExpression(obj) = first_arg { @@ -393,7 +393,7 @@ fn try_parse_signal_output<'a>( fn try_parse_signal_input<'a>( _allocator: &'a Allocator, value: &Expression<'a>, - property_name: Atom<'a>, + property_name: Ident<'a>, ) -> Option> { // Check if the value is a call expression let call_expr = match value { @@ -437,7 +437,7 @@ fn try_parse_signal_input<'a>( // For input.required(): first arg is options let options_arg_index = if is_required { 0 } else { 1 }; - let mut alias: Option> = None; + let mut alias: Option> = None; if let Some(options_arg) = call_expr.arguments.get(options_arg_index) { if let Argument::ObjectExpression(obj) = options_arg { @@ -589,7 +589,7 @@ pub fn extract_input_metadata<'a>( /// Parsed @Output decorator configuration. struct OutputConfig<'a> { /// Alias name for the output binding (different from property name). - alias: Option>, + alias: Option>, } impl<'a> Default for OutputConfig<'a> { @@ -614,7 +614,7 @@ fn parse_output_config<'a>(decorator: &'a Decorator<'a>) -> OutputConfig<'a> { match first_arg { // @Output('alias') - Argument::StringLiteral(lit) => OutputConfig { alias: Some(lit.value.clone()) }, + Argument::StringLiteral(lit) => OutputConfig { alias: Some(lit.value.clone().into()) }, _ => OutputConfig::default(), } } @@ -635,7 +635,7 @@ fn parse_output_config<'a>(decorator: &'a Decorator<'a>) -> OutputConfig<'a> { pub fn extract_output_metadata<'a>( allocator: &'a Allocator, class: &'a Class<'a>, -) -> Vec<'a, (Atom<'a>, Atom<'a>)> { +) -> Vec<'a, (Ident<'a>, Ident<'a>)> { let mut outputs = Vec::new_in(allocator); for element in &class.body.body { @@ -770,7 +770,7 @@ fn parse_query_config<'a>( // @ViewChild('refName') - string selector Argument::StringLiteral(lit) => { let mut selectors = Vec::new_in(allocator); - selectors.push(lit.value.clone()); + selectors.push(lit.value.clone().into()); config.predicate = Some(QueryPredicate::Selectors(selectors)); } @@ -865,7 +865,7 @@ impl SignalQueryType { fn try_parse_signal_query<'a>( allocator: &'a Allocator, value: &'a Expression<'a>, - property_name: Atom<'a>, + property_name: Ident<'a>, ) -> Option<(SignalQueryType, R3QueryMetadata<'a>)> { // Check if the value is a call expression let call_expr = match value { @@ -933,7 +933,7 @@ fn try_parse_signal_query<'a>( // String selector: viewChild('myRef') Argument::StringLiteral(lit) => { let mut selectors = oxc_allocator::Vec::new_in(allocator); - selectors.push(lit.value.clone()); + selectors.push(lit.value.clone().into()); QueryPredicate::Selectors(selectors) } // Type predicate: viewChild(TemplateRef) or viewChild(forwardRef(() => MyClass)) @@ -1285,7 +1285,7 @@ pub fn extract_content_queries<'a>( pub fn extract_host_bindings<'a>( allocator: &'a Allocator, class: &'a Class<'a>, -) -> Vec<'a, (Atom<'a>, Atom<'a>)> { +) -> Vec<'a, (Ident<'a>, Ident<'a>)> { let mut bindings = Vec::new_in(allocator); for element in &class.body.body { @@ -1323,7 +1323,7 @@ pub fn extract_host_bindings<'a>( /// Extract the binding name from a @HostBinding decorator. /// /// Returns the string argument if present, None otherwise. -fn extract_host_binding_name<'a>(decorator: &'a Decorator<'a>) -> Option> { +fn extract_host_binding_name<'a>(decorator: &'a Decorator<'a>) -> Option> { let Expression::CallExpression(call) = &decorator.expression else { return None; }; @@ -1331,7 +1331,7 @@ fn extract_host_binding_name<'a>(decorator: &'a Decorator<'a>) -> Option Some(lit.value.clone()), + Argument::StringLiteral(lit) => Some(lit.value.clone().into()), _ => { let expr = first_arg.to_expression(); extract_string_value(expr) @@ -1357,7 +1357,7 @@ fn extract_host_binding_name<'a>(decorator: &'a Decorator<'a>) -> Option( allocator: &'a Allocator, class: &'a Class<'a>, -) -> Vec<'a, (Atom<'a>, Atom<'a>, Vec<'a, Atom<'a>>)> { +) -> Vec<'a, (Ident<'a>, Ident<'a>, Vec<'a, Ident<'a>>)> { let mut listeners = Vec::new_in(allocator); for element in &class.body.body { @@ -1398,7 +1398,7 @@ pub fn extract_host_listeners<'a>( fn parse_host_listener_config<'a>( allocator: &'a Allocator, decorator: &'a Decorator<'a>, -) -> (Option>, Vec<'a, Atom<'a>>) { +) -> (Option>, Vec<'a, Ident<'a>>) { let mut args = Vec::new_in(allocator); let Expression::CallExpression(call) = &decorator.expression else { @@ -1407,7 +1407,7 @@ fn parse_host_listener_config<'a>( // First argument: event name let event_name = call.arguments.first().and_then(|arg| match arg { - Argument::StringLiteral(lit) => Some(lit.value.clone()), + Argument::StringLiteral(lit) => Some(lit.value.clone().into()), _ => { let expr = arg.to_expression(); extract_string_value(expr) @@ -1420,7 +1420,7 @@ fn parse_host_listener_config<'a>( for elem in &arr.elements { match elem { ArrayExpressionElement::StringLiteral(lit) => { - args.push(lit.value.clone()); + args.push(lit.value.clone().into()); } ArrayExpressionElement::Elision(_) => {} _ => { diff --git a/crates/oxc_angular_compiler/src/directive/query.rs b/crates/oxc_angular_compiler/src/directive/query.rs index afa7f9c75..3930f63a4 100644 --- a/crates/oxc_angular_compiler/src/directive/query.rs +++ b/crates/oxc_angular_compiler/src/directive/query.rs @@ -6,7 +6,7 @@ //! in directive definitions. use oxc_allocator::{Allocator, Box, FromIn, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use super::metadata::{QueryPredicate, R3QueryMetadata}; use crate::output::ast::{ @@ -107,12 +107,12 @@ fn import_expr<'a>(allocator: &'a Allocator, identifier: &'static str) -> Output ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(identifier), + name: Ident::from(identifier), optional: false, source_span: None, }, @@ -123,7 +123,7 @@ fn import_expr<'a>(allocator: &'a Allocator, identifier: &'static str) -> Output /// Create a variable reference. fn variable<'a>(allocator: &'a Allocator, name: &'static str) -> OutputExpression<'a> { OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(name), source_span: None }, + ReadVarExpr { name: Ident::from(name), source_span: None }, allocator, )) } @@ -137,7 +137,7 @@ fn literal_number<'a>(allocator: &'a Allocator, value: u32) -> OutputExpression< } /// Create ctx.propertyName. -fn context_prop<'a>(allocator: &'a Allocator, property_name: &Atom<'a>) -> OutputExpression<'a> { +fn context_prop<'a>(allocator: &'a Allocator, property_name: &Ident<'a>) -> OutputExpression<'a> { OutputExpression::ReadProp(Box::new_in( ReadPropExpr { receiver: Box::new_in(variable(allocator, CONTEXT_NAME), allocator), @@ -234,7 +234,7 @@ fn get_query_predicate<'a>( if !trimmed.is_empty() { entries.push(OutputExpression::Literal(Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from(trimmed)), + value: LiteralValue::String(Ident::from(trimmed)), source_span: None, }, allocator, @@ -413,7 +413,7 @@ impl TempAllocator { // Angular always reuses the same _t variable - see temporaryAllocator in util.ts self.allocated = true; OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(TEMPORARY_NAME), source_span: None }, + ReadVarExpr { name: Ident::from(TEMPORARY_NAME), source_span: None }, allocator, )) } @@ -519,7 +519,7 @@ pub fn create_view_queries_function<'a>( OutputExpression::ReadProp(Box::new_in( ReadPropExpr { receiver: Box::new_in(temp.clone_in(allocator), allocator), - name: Atom::from("first"), + name: Ident::from("first"), optional: false, source_span: None, }, @@ -562,7 +562,7 @@ pub fn create_view_queries_function<'a>( if temp_allocator.needs_declaration() { final_update_statements.push(OutputStatement::DeclareVar(Box::new_in( DeclareVarStmt { - name: Atom::from(TEMPORARY_NAME), + name: Ident::from(TEMPORARY_NAME), value: None, modifiers: StmtModifier::NONE, leading_comment: None, @@ -592,13 +592,13 @@ pub fn create_view_queries_function<'a>( // Build function parameters let mut params = Vec::new_in(allocator); - params.push(FnParam { name: Atom::from(RENDER_FLAGS) }); - params.push(FnParam { name: Atom::from(CONTEXT_NAME) }); + params.push(FnParam { name: Ident::from(RENDER_FLAGS) }); + params.push(FnParam { name: Ident::from(CONTEXT_NAME) }); // Create function name let fn_name = name.map(|n| { let formatted = format!("{n}_Query"); - Atom::from_in(formatted.as_str(), allocator) + Ident::from_in(formatted.as_str(), allocator) }); OutputExpression::Function(Box::new_in( @@ -704,7 +704,7 @@ pub fn create_content_queries_function<'a>( OutputExpression::ReadProp(Box::new_in( ReadPropExpr { receiver: Box::new_in(temp.clone_in(allocator), allocator), - name: Atom::from("first"), + name: Ident::from("first"), optional: false, source_span: None, }, @@ -745,7 +745,7 @@ pub fn create_content_queries_function<'a>( if temp_allocator.needs_declaration() { final_update_statements.push(OutputStatement::DeclareVar(Box::new_in( DeclareVarStmt { - name: Atom::from(TEMPORARY_NAME), + name: Ident::from(TEMPORARY_NAME), value: None, modifiers: StmtModifier::NONE, leading_comment: None, @@ -774,14 +774,14 @@ pub fn create_content_queries_function<'a>( // Build function parameters (rf, ctx, dirIndex) let mut params = Vec::new_in(allocator); - params.push(FnParam { name: Atom::from(RENDER_FLAGS) }); - params.push(FnParam { name: Atom::from(CONTEXT_NAME) }); - params.push(FnParam { name: Atom::from("dirIndex") }); + params.push(FnParam { name: Ident::from(RENDER_FLAGS) }); + params.push(FnParam { name: Ident::from(CONTEXT_NAME) }); + params.push(FnParam { name: Ident::from("dirIndex") }); // Create function name let fn_name = name.map(|n| { let formatted = format!("{n}_ContentQueries"); - Atom::from_in(formatted.as_str(), allocator) + Ident::from_in(formatted.as_str(), allocator) }); OutputExpression::Function(Box::new_in( @@ -846,10 +846,10 @@ mod tests { // Create a signal query with a type predicate (e.g., SomeComponent) let query = R3QueryMetadata { - property_name: Atom::from("myQuery"), + property_name: Ident::from("myQuery"), first: true, predicate: QueryPredicate::Type(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("SomeComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("SomeComponent"), source_span: None }, &allocator, ))), descendants: true, @@ -888,10 +888,10 @@ mod tests { // Create a signal content query with a type predicate let query = R3QueryMetadata { - property_name: Atom::from("myContent"), + property_name: Ident::from("myContent"), first: true, predicate: QueryPredicate::Type(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ContentComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("ContentComponent"), source_span: None }, &allocator, ))), descendants: true, @@ -929,10 +929,10 @@ mod tests { // Create two signal queries let query1 = R3QueryMetadata { - property_name: Atom::from("query1"), + property_name: Ident::from("query1"), first: true, predicate: QueryPredicate::Type(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("Component1"), source_span: None }, + ReadVarExpr { name: Ident::from("Component1"), source_span: None }, &allocator, ))), descendants: true, @@ -943,10 +943,10 @@ mod tests { }; let query2 = R3QueryMetadata { - property_name: Atom::from("query2"), + property_name: Ident::from("query2"), first: true, predicate: QueryPredicate::Type(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("Component2"), source_span: None }, + ReadVarExpr { name: Ident::from("Component2"), source_span: None }, &allocator, ))), descendants: true, @@ -988,10 +988,10 @@ mod tests { let allocator = Allocator::default(); let query1 = R3QueryMetadata { - property_name: Atom::from("myChild"), + property_name: Ident::from("myChild"), first: true, predicate: QueryPredicate::Type(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ChildComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("ChildComponent"), source_span: None }, &allocator, ))), descendants: true, @@ -1002,10 +1002,10 @@ mod tests { }; let query2 = R3QueryMetadata { - property_name: Atom::from("myOther"), + property_name: Ident::from("myOther"), first: false, predicate: QueryPredicate::Type(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("OtherComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("OtherComponent"), source_span: None }, &allocator, ))), descendants: true, @@ -1054,10 +1054,10 @@ mod tests { let allocator = Allocator::default(); let query1 = R3QueryMetadata { - property_name: Atom::from("items"), + property_name: Ident::from("items"), first: false, predicate: QueryPredicate::Type(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ItemComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("ItemComponent"), source_span: None }, &allocator, ))), descendants: true, @@ -1068,10 +1068,10 @@ mod tests { }; let query2 = R3QueryMetadata { - property_name: Atom::from("headers"), + property_name: Ident::from("headers"), first: true, predicate: QueryPredicate::Type(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("HeaderComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("HeaderComponent"), source_span: None }, &allocator, ))), descendants: false, @@ -1118,10 +1118,10 @@ mod tests { // Create a signal query with a string selector let mut selectors = Vec::new_in(&allocator); - selectors.push(Atom::from("myRef")); + selectors.push(Ident::from("myRef")); let query = R3QueryMetadata { - property_name: Atom::from("refQuery"), + property_name: Ident::from("refQuery"), first: true, predicate: QueryPredicate::Selectors(selectors), descendants: true, diff --git a/crates/oxc_angular_compiler/src/dts.rs b/crates/oxc_angular_compiler/src/dts.rs index 1201ce729..6a8efac19 100644 --- a/crates/oxc_angular_compiler/src/dts.rs +++ b/crates/oxc_angular_compiler/src/dts.rs @@ -578,7 +578,7 @@ fn generate_input_map_type(inputs: &[R3InputMetadata]) -> String { /// Generate the output map type. /// /// Produces: `{ "clicked": "clicked"; "valueChanged": "onChange"; }` -fn generate_output_map_type(outputs: &[(oxc_span::Atom, oxc_span::Atom)]) -> String { +fn generate_output_map_type(outputs: &[(oxc_span::Ident, oxc_span::Ident)]) -> String { if outputs.is_empty() { return "{}".to_string(); } @@ -762,7 +762,7 @@ mod tests { #[test] fn test_generate_output_map_type_empty() { - let outputs: Vec<(oxc_span::Atom, oxc_span::Atom)> = vec![]; + let outputs: Vec<(oxc_span::Ident, oxc_span::Ident)> = vec![]; assert_eq!(generate_output_map_type(&outputs), "{}"); } } diff --git a/crates/oxc_angular_compiler/src/factory/compiler.rs b/crates/oxc_angular_compiler/src/factory/compiler.rs index 331697854..59acce762 100644 --- a/crates/oxc_angular_compiler/src/factory/compiler.rs +++ b/crates/oxc_angular_compiler/src/factory/compiler.rs @@ -33,7 +33,7 @@ //! ``` use oxc_allocator::{Allocator, Box, FromIn, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use super::metadata::{ FactoryTarget, R3DependencyMetadata, R3FactoryDelegateType, R3FactoryDeps, R3FactoryMetadata, @@ -104,7 +104,7 @@ pub fn compile_factory_function<'a>( factory_name: &'a str, ) -> FactoryCompileResult<'a> { let base = meta.base(); - let factory_type_param = Atom::from("__ngFactoryType__"); + let factory_type_param = Ident::from("__ngFactoryType__"); // The type to instantiate via constructor invocation. If there is no delegated factory, // meaning this type is always created by constructor invocation, then this is the @@ -261,7 +261,7 @@ pub fn compile_factory_function<'a>( let factory_fn = OutputExpression::Function(Box::new_in( FunctionExpr { - name: Some(Atom::from(factory_name)), + name: Some(Ident::from(factory_name)), params, statements: body, source_span: None, @@ -290,11 +290,11 @@ pub fn compile_factory_function<'a>( fn make_conditional_factory<'a>( allocator: &'a Allocator, body: &mut Vec<'a, OutputStatement<'a>>, - factory_type_param: &Atom<'a>, + factory_type_param: &Ident<'a>, ctor_expr: Option>, non_ctor_expr: OutputExpression<'a>, ) -> OutputExpression<'a> { - let conditional_factory_var = Atom::from("__ngConditionalFactory__"); + let conditional_factory_var = Ident::from("__ngConditionalFactory__"); // let __ngConditionalFactory__ = null; body.push(OutputStatement::DeclareVar(Box::new_in( @@ -415,8 +415,8 @@ fn compile_inherited_factory<'a>( ) -> FactoryCompileResult<'a> { // Create base factory variable name: ɵMyClass_BaseFactory let base_factory_var_name = - Atom::from_in(format!("ɵ{}_BaseFactory", base.name).as_str(), allocator); - let factory_type_param = Atom::from("__ngFactoryType__"); + Ident::from_in(format!("ɵ{}_BaseFactory", base.name).as_str(), allocator); + let factory_type_param = Ident::from("__ngFactoryType__"); // Create ɵɵgetInheritedFactory(MyClass) call let get_inherited_factory_call = { @@ -424,12 +424,12 @@ fn compile_inherited_factory<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::GET_INHERITED_FACTORY), + name: Ident::from(Identifiers::GET_INHERITED_FACTORY), optional: false, source_span: None, }, @@ -530,7 +530,7 @@ fn compile_inherited_factory<'a>( let inner_fn = OutputExpression::Function(Box::new_in( FunctionExpr { - name: Some(Atom::from(factory_name)), + name: Some(Ident::from(factory_name)), params: inner_params, statements: inner_body, source_span: None, @@ -670,12 +670,12 @@ fn create_import_call<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(name), + name: Ident::from(name), optional: false, source_span: None, }, @@ -722,12 +722,12 @@ mod tests { fn test_compile_simple_factory() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TestClass"), source_span: None }, + ReadVarExpr { name: Ident::from("TestClass"), source_span: None }, &allocator, )); let meta = R3FactoryMetadata::Constructor(R3ConstructorFactoryMetadata { - name: Atom::from("TestClass"), + name: Ident::from("TestClass"), type_expr: type_expr.clone_in(&allocator), type_decl: type_expr, type_argument_count: 0, @@ -747,12 +747,12 @@ mod tests { fn test_compile_factory_with_deps() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("MyPipe"), source_span: None }, + ReadVarExpr { name: Ident::from("MyPipe"), source_span: None }, &allocator, )); let dep_token = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("SomeService"), source_span: None }, + ReadVarExpr { name: Ident::from("SomeService"), source_span: None }, &allocator, )); @@ -760,7 +760,7 @@ mod tests { deps.push(R3DependencyMetadata::simple(dep_token)); let meta = R3FactoryMetadata::Constructor(R3ConstructorFactoryMetadata { - name: Atom::from("MyPipe"), + name: Ident::from("MyPipe"), type_expr: type_expr.clone_in(&allocator), type_decl: type_expr, type_argument_count: 0, @@ -780,12 +780,12 @@ mod tests { fn test_compile_invalid_deps_factory() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("BrokenClass"), source_span: None }, + ReadVarExpr { name: Ident::from("BrokenClass"), source_span: None }, &allocator, )); let meta = R3FactoryMetadata::Constructor(R3ConstructorFactoryMetadata { - name: Atom::from("BrokenClass"), + name: Ident::from("BrokenClass"), type_expr: type_expr.clone_in(&allocator), type_decl: type_expr, type_argument_count: 0, @@ -804,13 +804,13 @@ mod tests { fn test_compile_inherited_factory() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ChildClass"), source_span: None }, + ReadVarExpr { name: Ident::from("ChildClass"), source_span: None }, &allocator, )); // R3FactoryDeps::None indicates no constructor, use inherited factory let meta = R3FactoryMetadata::Constructor(R3ConstructorFactoryMetadata { - name: Atom::from("ChildClass"), + name: Ident::from("ChildClass"), type_expr: type_expr.clone_in(&allocator), type_decl: type_expr, type_argument_count: 0, diff --git a/crates/oxc_angular_compiler/src/factory/metadata.rs b/crates/oxc_angular_compiler/src/factory/metadata.rs index 1238e2a1f..daecd815e 100644 --- a/crates/oxc_angular_compiler/src/factory/metadata.rs +++ b/crates/oxc_angular_compiler/src/factory/metadata.rs @@ -3,7 +3,7 @@ //! Ported from Angular's `render3/r3_factory.ts`. use oxc_allocator::Vec; -use oxc_span::Atom; +use oxc_span::Ident; use crate::output::ast::OutputExpression; @@ -98,7 +98,7 @@ pub enum R3FactoryDeps<'a> { #[derive(Debug)] pub struct R3ConstructorFactoryMetadata<'a> { /// String name of the type being generated (used to name the factory function). - pub name: Atom<'a>, + pub name: Ident<'a>, /// An expression representing the interface type being constructed. pub type_expr: OutputExpression<'a>, @@ -186,7 +186,7 @@ mod tests { fn test_dependency_metadata() { let allocator = Allocator::default(); let token = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TestService"), source_span: None }, + ReadVarExpr { name: Ident::from("TestService"), source_span: None }, &allocator, )); @@ -202,7 +202,7 @@ mod tests { fn test_optional_dependency() { let allocator = Allocator::default(); let token = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("OptionalService"), source_span: None }, + ReadVarExpr { name: Ident::from("OptionalService"), source_span: None }, &allocator, )); diff --git a/crates/oxc_angular_compiler/src/hmr/dependencies.rs b/crates/oxc_angular_compiler/src/hmr/dependencies.rs index 9dd6280e8..6b297257f 100644 --- a/crates/oxc_angular_compiler/src/hmr/dependencies.rs +++ b/crates/oxc_angular_compiler/src/hmr/dependencies.rs @@ -9,7 +9,7 @@ use std::collections::HashSet; use oxc_allocator::Box; -use oxc_span::Atom; +use oxc_span::Ident; use crate::output::ast::{ ExternalExpr, OutputExpression, OutputStatement, ReadVarExpr, RecursiveOutputAstVisitor, @@ -29,16 +29,16 @@ use crate::output::ast::{ #[derive(Debug, Clone)] pub struct HmrNamespaceDependency<'a> { /// Module name of the import (e.g., `@angular/core`). - pub module_name: Atom<'a>, + pub module_name: Ident<'a>, /// Name under which to refer to the namespace inside HMR-related code. /// Must be a valid JS identifier (e.g., `i0`). - pub assigned_name: Atom<'a>, + pub assigned_name: Ident<'a>, } impl<'a> HmrNamespaceDependency<'a> { /// Create a new namespace dependency. - pub fn new(module_name: Atom<'a>, assigned_name: Atom<'a>) -> Self { + pub fn new(module_name: Ident<'a>, assigned_name: Ident<'a>) -> Self { Self { module_name, assigned_name } } } @@ -57,7 +57,7 @@ impl<'a> HmrNamespaceDependency<'a> { #[derive(Debug)] pub struct HmrLocalDependency<'a> { /// Name of the local symbol. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Runtime representation of the local (the expression to pass as argument). pub runtime_representation: OutputExpression<'a>, @@ -65,7 +65,7 @@ pub struct HmrLocalDependency<'a> { impl<'a> HmrLocalDependency<'a> { /// Create a new local dependency. - pub fn new(name: Atom<'a>, runtime_representation: OutputExpression<'a>) -> Self { + pub fn new(name: Ident<'a>, runtime_representation: OutputExpression<'a>) -> Self { Self { name, runtime_representation } } } @@ -83,10 +83,10 @@ pub struct HmrMetadata<'a> { pub component_type: OutputExpression<'a>, /// Name of the component class. - pub class_name: Atom<'a>, + pub class_name: Ident<'a>, /// File path of the component class. - pub file_path: Atom<'a>, + pub file_path: Ident<'a>, /// Namespace dependencies (e.g., `import * as i0 from '@angular/core'`). /// @@ -105,8 +105,8 @@ impl<'a> HmrMetadata<'a> { /// Create new HMR metadata. pub fn new( component_type: OutputExpression<'a>, - class_name: Atom<'a>, - file_path: Atom<'a>, + class_name: Ident<'a>, + file_path: Ident<'a>, ) -> Self { Self { component_type, @@ -118,14 +118,14 @@ impl<'a> HmrMetadata<'a> { } /// Add a namespace dependency. - pub fn add_namespace_dependency(&mut self, module_name: Atom<'a>, assigned_name: Atom<'a>) { + pub fn add_namespace_dependency(&mut self, module_name: Ident<'a>, assigned_name: Ident<'a>) { self.namespace_dependencies.push(HmrNamespaceDependency::new(module_name, assigned_name)); } /// Add a local dependency. pub fn add_local_dependency( &mut self, - name: Atom<'a>, + name: Ident<'a>, runtime_representation: OutputExpression<'a>, ) { self.local_dependencies.push(HmrLocalDependency::new(name, runtime_representation)); @@ -243,7 +243,7 @@ pub struct ExtractedHmrDependencies<'a> { #[derive(Debug)] pub struct LocalDependency<'a> { /// Name of the local symbol. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Runtime representation (the expression to pass as argument). pub runtime_representation: OutputExpression<'a>, @@ -310,8 +310,8 @@ pub fn extract_compiled_dependencies<'a>( .get_namespace_dependencies() .into_iter() .map(|(module_name, assigned_name)| HmrNamespaceDependency { - module_name: Atom::from(allocator.alloc_str(module_name)), - assigned_name: Atom::from(allocator.alloc_str(&assigned_name)), + module_name: Ident::from(allocator.alloc_str(module_name)), + assigned_name: Ident::from(allocator.alloc_str(&assigned_name)), }) .collect(); @@ -322,10 +322,10 @@ pub fn extract_compiled_dependencies<'a>( .map(|name| { // Create variable reference as runtime representation let runtime_representation = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(allocator.alloc_str(name)), source_span: None }, + ReadVarExpr { name: Ident::from(allocator.alloc_str(name)), source_span: None }, allocator, )); - LocalDependency { name: Atom::from(allocator.alloc_str(name)), runtime_representation } + LocalDependency { name: Ident::from(allocator.alloc_str(name)), runtime_representation } }) .collect(); @@ -498,7 +498,7 @@ mod tests { // Create a variable read expression let var_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("myVar"), source_span: None }, + ReadVarExpr { name: Ident::from("myVar"), source_span: None }, &allocator, )); @@ -516,8 +516,8 @@ mod tests { let external_expr = OutputExpression::External(Box::new_in( ExternalExpr { value: ExternalReference { - module_name: Some(Atom::from("@angular/core")), - name: Some(Atom::from("Component")), + module_name: Some(Ident::from("@angular/core")), + name: Some(Ident::from("Component")), }, source_span: None, }, @@ -540,8 +540,8 @@ mod tests { let expr1 = OutputExpression::External(Box::new_in( ExternalExpr { value: ExternalReference { - module_name: Some(Atom::from("@angular/core")), - name: Some(Atom::from("Component")), + module_name: Some(Ident::from("@angular/core")), + name: Some(Ident::from("Component")), }, source_span: None, }, @@ -551,8 +551,8 @@ mod tests { let expr2 = OutputExpression::External(Box::new_in( ExternalExpr { value: ExternalReference { - module_name: Some(Atom::from("@angular/core")), - name: Some(Atom::from("Injectable")), + module_name: Some(Ident::from("@angular/core")), + name: Some(Ident::from("Injectable")), }, source_span: None, }, @@ -575,8 +575,8 @@ mod tests { let expr1 = OutputExpression::External(Box::new_in( ExternalExpr { value: ExternalReference { - module_name: Some(Atom::from("@angular/core")), - name: Some(Atom::from("Component")), + module_name: Some(Ident::from("@angular/core")), + name: Some(Ident::from("Component")), }, source_span: None, }, @@ -586,8 +586,8 @@ mod tests { let expr2 = OutputExpression::External(Box::new_in( ExternalExpr { value: ExternalReference { - module_name: Some(Atom::from("./my-dep")), - name: Some(Atom::from("MyDep")), + module_name: Some(Ident::from("./my-dep")), + name: Some(Ident::from("MyDep")), }, source_span: None, }, @@ -612,7 +612,7 @@ mod tests { // Add some variable reads for name in ["MyService", "localVar", "AppComponent", "helperFn"] { let expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(name), source_span: None }, + ReadVarExpr { name: Ident::from(name), source_span: None }, &allocator, )); collector.visit_expression(&expr); @@ -643,8 +643,8 @@ mod tests { let def_expr = OutputExpression::External(Box::new_in( ExternalExpr { value: ExternalReference { - module_name: Some(Atom::from("@angular/core")), - name: Some(Atom::from("ɵɵdefineComponent")), + module_name: Some(Ident::from("@angular/core")), + name: Some(Ident::from("ɵɵdefineComponent")), }, source_span: None, }, diff --git a/crates/oxc_angular_compiler/src/hmr/initializer.rs b/crates/oxc_angular_compiler/src/hmr/initializer.rs index 5142645de..b90d9b6d5 100644 --- a/crates/oxc_angular_compiler/src/hmr/initializer.rs +++ b/crates/oxc_angular_compiler/src/hmr/initializer.rs @@ -6,7 +6,7 @@ //! Ported from Angular's `packages/compiler/src/render3/r3_hmr_compiler.ts`. use oxc_allocator::{Allocator, Box, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use super::dependencies::HmrMetadata; use crate::output::ast::{ @@ -75,7 +75,7 @@ pub fn compile_hmr_initializer<'a>( // (m) => m.default && ɵɵreplaceMetadata(...) let replace_callback = OutputExpression::ArrowFunction(Box::new_in( ArrowFunctionExpr { - params: Vec::from_iter_in([FnParam { name: Atom::from(module_name) }], allocator), + params: Vec::from_iter_in([FnParam { name: Ident::from(module_name) }], allocator), body: ArrowFunctionBody::Expression(Box::new_in( binary_op(allocator, BinaryOperator::And, default_read, replace_call), allocator, @@ -104,7 +104,7 @@ pub fn compile_hmr_initializer<'a>( let dynamic_import = OutputExpression::DynamicImport(Box::new_in( DynamicImportExpr { url: DynamicImportUrl::Expression(Box::new_in(url, allocator)), - url_comment: Some(Atom::from("@vite-ignore")), + url_comment: Some(Ident::from("@vite-ignore")), source_span: None, }, allocator, @@ -116,8 +116,8 @@ pub fn compile_hmr_initializer<'a>( // function Cmp_HmrLoad(t) { import(...).then(...); } let import_callback = OutputStatement::DeclareFunction(Box::new_in( DeclareFunctionStmt { - name: Atom::from(allocator.alloc_str(&import_callback_name)), - params: Vec::from_iter_in([FnParam { name: Atom::from(timestamp_name) }], allocator), + name: Ident::from(allocator.alloc_str(&import_callback_name)), + params: Vec::from_iter_in([FnParam { name: Ident::from(timestamp_name) }], allocator), statements: Vec::from_iter_in([expr_stmt(allocator, import_then_call)], allocator), modifiers: StmtModifier::FINAL, source_span: None, @@ -128,7 +128,7 @@ pub fn compile_hmr_initializer<'a>( // (d) => d.id === id && Cmp_HmrLoad(d.timestamp) let update_callback = OutputExpression::ArrowFunction(Box::new_in( ArrowFunctionExpr { - params: Vec::from_iter_in([FnParam { name: Atom::from(data_name) }], allocator), + params: Vec::from_iter_in([FnParam { name: Ident::from(data_name) }], allocator), body: ArrowFunctionBody::Expression(Box::new_in( binary_op( allocator, @@ -178,7 +178,7 @@ pub fn compile_hmr_initializer<'a>( // Handles the angular:invalidate event sent when HMR fails let invalidate_callback = OutputExpression::ArrowFunction(Box::new_in( ArrowFunctionExpr { - params: Vec::from_iter_in([FnParam { name: Atom::from(data_name) }], allocator), + params: Vec::from_iter_in([FnParam { name: Ident::from(data_name) }], allocator), body: ArrowFunctionBody::Expression(Box::new_in( binary_op( allocator, @@ -290,7 +290,7 @@ pub fn compile_hmr_initializer<'a>( #[derive(Debug)] pub struct HmrDefinition<'a> { /// Name of the field (e.g., "ɵcmp", "ɵfac"). - pub name: Atom<'a>, + pub name: Ident<'a>, /// Initializer expression. pub initializer: Option>, /// Additional statements after the field assignment. @@ -318,7 +318,7 @@ pub fn compile_hmr_update_callback<'a>( // Build function parameters: [className, ɵɵnamespaces, ...locals] let mut params: Vec<'a, FnParam<'a>> = Vec::new_in(allocator); params.push(FnParam { name: meta.class_name.clone() }); - params.push(FnParam { name: Atom::from(namespaces_param) }); + params.push(FnParam { name: Ident::from(namespaces_param) }); for local in &meta.local_dependencies { params.push(FnParam { name: local.name.clone() }); } @@ -372,7 +372,7 @@ pub fn compile_hmr_update_callback<'a>( let fn_name = format!("{}_UpdateMetadata", meta.class_name); OutputStatement::DeclareFunction(Box::new_in( DeclareFunctionStmt { - name: Atom::from(allocator.alloc_str(&fn_name)), + name: Ident::from(allocator.alloc_str(&fn_name)), params, statements: body, modifiers: StmtModifier::FINAL, @@ -389,7 +389,7 @@ pub fn compile_hmr_update_callback<'a>( /// Create a read variable expression. fn read_var<'a>(allocator: &'a Allocator, name: &str) -> OutputExpression<'a> { OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(allocator.alloc_str(name)), source_span: None }, + ReadVarExpr { name: Ident::from(allocator.alloc_str(name)), source_span: None }, allocator, )) } @@ -403,7 +403,7 @@ fn read_prop<'a>( OutputExpression::ReadProp(Box::new_in( ReadPropExpr { receiver: Box::new_in(receiver, allocator), - name: Atom::from(allocator.alloc_str(name)), + name: Ident::from(allocator.alloc_str(name)), optional: false, source_span: None, }, @@ -433,7 +433,7 @@ fn invoke_fn<'a>( fn literal_str<'a>(allocator: &'a Allocator, value: &str) -> OutputExpression<'a> { OutputExpression::Literal(Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from(allocator.alloc_str(value))), + value: LiteralValue::String(Ident::from(allocator.alloc_str(value))), source_span: None, }, allocator, @@ -487,7 +487,7 @@ fn var_decl<'a>( let modifiers = if is_final { StmtModifier::FINAL } else { StmtModifier::NONE }; OutputStatement::DeclareVar(Box::new_in( DeclareVarStmt { - name: Atom::from(allocator.alloc_str(name)), + name: Ident::from(allocator.alloc_str(name)), value: Some(value), modifiers, source_span: None, diff --git a/crates/oxc_angular_compiler/src/i18n/parser.rs b/crates/oxc_angular_compiler/src/i18n/parser.rs index 67bd3d334..af1a742fb 100644 --- a/crates/oxc_angular_compiler/src/i18n/parser.rs +++ b/crates/oxc_angular_compiler/src/i18n/parser.rs @@ -834,7 +834,7 @@ mod tests { use crate::ast::html::HtmlText; use crate::util::ParseSourceFile; use oxc_allocator::{Allocator, Box, Vec as AllocVec}; - use oxc_span::Atom; + use oxc_span::Ident; use std::sync::Arc; #[test] @@ -858,7 +858,7 @@ mod tests { let factory = create_i18n_message_factory(false, false); let text = HtmlText { - value: Atom::from("Hello {{name}}!"), + value: Ident::from("Hello {{name}}!"), span: Span::default(), full_start: None, tokens: AllocVec::new_in(&allocator), @@ -889,7 +889,7 @@ mod tests { let factory = create_i18n_message_factory(false, false); let text = HtmlText { - value: Atom::from("Hello World"), + value: Ident::from("Hello World"), span: Span::default(), full_start: None, tokens: AllocVec::new_in(&allocator), @@ -910,7 +910,7 @@ mod tests { let factory = create_i18n_message_factory(false, false); let text = HtmlText { - value: Atom::from("{{greeting}} {{name}}!"), + value: Ident::from("{{greeting}} {{name}}!"), span: Span::default(), full_start: None, tokens: AllocVec::new_in(&allocator), diff --git a/crates/oxc_angular_compiler/src/injectable/compiler.rs b/crates/oxc_angular_compiler/src/injectable/compiler.rs index 259d3fb0f..2b8c9c20d 100644 --- a/crates/oxc_angular_compiler/src/injectable/compiler.rs +++ b/crates/oxc_angular_compiler/src/injectable/compiler.rs @@ -12,7 +12,7 @@ //! ``` use oxc_allocator::{Allocator, Box, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use super::metadata::{InjectableProvider, ProvidedIn, R3InjectableMetadata}; use crate::factory::{ @@ -205,12 +205,12 @@ fn create_inject_call<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::RESOLVE_FORWARD_REF), + name: Ident::from(Identifiers::RESOLVE_FORWARD_REF), optional: false, source_span: None, }, @@ -239,12 +239,12 @@ fn create_inject_call<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::INJECT), + name: Ident::from(Identifiers::INJECT), optional: false, source_span: None, }, @@ -274,7 +274,7 @@ fn create_factory_delegation<'a>( OutputExpression::ReadProp(Box::new_in( ReadPropExpr { receiver: Box::new_in(type_expr.clone_in(allocator), allocator), - name: Atom::from("ɵfac"), + name: Ident::from("ɵfac"), optional: false, source_span: None, }, @@ -287,7 +287,7 @@ fn create_forward_ref_factory<'a>( allocator: &'a Allocator, type_expr: &OutputExpression<'a>, ) -> OutputExpression<'a> { - let param_name = Atom::from("t"); + let param_name = Ident::from("t"); let mut params = Vec::new_in(allocator); params.push(FnParam { name: param_name.clone() }); @@ -296,12 +296,12 @@ fn create_forward_ref_factory<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::RESOLVE_FORWARD_REF), + name: Ident::from(Identifiers::RESOLVE_FORWARD_REF), optional: false, source_span: None, }, @@ -326,7 +326,7 @@ fn create_forward_ref_factory<'a>( let fac_access = OutputExpression::ReadProp(Box::new_in( ReadPropExpr { receiver: Box::new_in(resolved_type, allocator), - name: Atom::from("ɵfac"), + name: Ident::from("ɵfac"), optional: false, source_span: None, }, @@ -405,14 +405,14 @@ fn build_definition_map<'a>( // token: MyService entries.push(LiteralMapEntry { - key: Atom::from("token"), + key: Ident::from("token"), value: metadata.r#type.clone_in(allocator), quoted: false, }); // factory: entries.push(LiteralMapEntry { - key: Atom::from("factory"), + key: Ident::from("factory"), value: factory_expr, quoted: false, }); @@ -421,10 +421,10 @@ fn build_definition_map<'a>( match &metadata.provided_in { ProvidedIn::Root => { entries.push(LiteralMapEntry { - key: Atom::from("providedIn"), + key: Ident::from("providedIn"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from("root")), + value: LiteralValue::String(Ident::from("root")), source_span: None, }, allocator, @@ -434,10 +434,10 @@ fn build_definition_map<'a>( } ProvidedIn::Platform => { entries.push(LiteralMapEntry { - key: Atom::from("providedIn"), + key: Ident::from("providedIn"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from("platform")), + value: LiteralValue::String(Ident::from("platform")), source_span: None, }, allocator, @@ -447,10 +447,10 @@ fn build_definition_map<'a>( } ProvidedIn::Any => { entries.push(LiteralMapEntry { - key: Atom::from("providedIn"), + key: Ident::from("providedIn"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from("any")), + value: LiteralValue::String(Ident::from("any")), source_span: None, }, allocator, @@ -460,7 +460,7 @@ fn build_definition_map<'a>( } ProvidedIn::Module(module_expr) => { entries.push(LiteralMapEntry { - key: Atom::from("providedIn"), + key: Ident::from("providedIn"), value: module_expr.clone_in(allocator), quoted: false, }); @@ -483,12 +483,12 @@ fn create_define_injectable_call<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::DEFINE_INJECTABLE), + name: Ident::from(Identifiers::DEFINE_INJECTABLE), optional: false, source_span: None, }, @@ -527,12 +527,12 @@ mod tests { fn test_compile_simple_injectable() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("MyService"), source_span: None }, + ReadVarExpr { name: Ident::from("MyService"), source_span: None }, &allocator, )); let metadata = R3InjectableMetadataBuilder::new() - .name(Atom::from("MyService")) + .name(Ident::from("MyService")) .r#type(type_expr) .provided_in_root() .build() @@ -552,19 +552,19 @@ mod tests { fn test_compile_injectable_with_use_value() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("CONFIG_TOKEN"), source_span: None }, + ReadVarExpr { name: Ident::from("CONFIG_TOKEN"), source_span: None }, &allocator, )); let value_expr = OutputExpression::Literal(Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from("config_value")), + value: LiteralValue::String(Ident::from("config_value")), source_span: None, }, &allocator, )); let metadata = R3InjectableMetadataBuilder::new() - .name(Atom::from("CONFIG_TOKEN")) + .name(Ident::from("CONFIG_TOKEN")) .r#type(type_expr) .use_value(value_expr) .provided_in_root() @@ -584,16 +584,16 @@ mod tests { fn test_compile_injectable_with_use_existing() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("AliasService"), source_span: None }, + ReadVarExpr { name: Ident::from("AliasService"), source_span: None }, &allocator, )); let existing_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("RealService"), source_span: None }, + ReadVarExpr { name: Ident::from("RealService"), source_span: None }, &allocator, )); let metadata = R3InjectableMetadataBuilder::new() - .name(Atom::from("AliasService")) + .name(Ident::from("AliasService")) .r#type(type_expr) .use_existing(existing_expr, false) .provided_in_root() @@ -612,12 +612,12 @@ mod tests { fn test_compile_injectable_no_provided_in() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("LocalService"), source_span: None }, + ReadVarExpr { name: Ident::from("LocalService"), source_span: None }, &allocator, )); let metadata = R3InjectableMetadataBuilder::new() - .name(Atom::from("LocalService")) + .name(Ident::from("LocalService")) .r#type(type_expr) .build() .unwrap(); @@ -648,11 +648,11 @@ mod tests { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("CipherService"), source_span: None }, + ReadVarExpr { name: Ident::from("CipherService"), source_span: None }, &allocator, )); let factory_fn = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("cipherServiceFactory"), source_span: None }, + ReadVarExpr { name: Ident::from("cipherServiceFactory"), source_span: None }, &allocator, )); @@ -660,7 +660,7 @@ mod tests { let mut deps = Vec::new_in(&allocator); deps.push(R3DependencyMetadata { token: Some(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("LogService"), source_span: None }, + ReadVarExpr { name: Ident::from("LogService"), source_span: None }, &allocator, ))), attribute_name_type: None, @@ -671,7 +671,7 @@ mod tests { }); let metadata = R3InjectableMetadataBuilder::new() - .name(Atom::from("CipherService")) + .name(Ident::from("CipherService")) .r#type(type_expr) .use_factory(factory_fn, Some(deps)) .provided_in_root() @@ -705,11 +705,11 @@ mod tests { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("BaseService"), source_span: None }, + ReadVarExpr { name: Ident::from("BaseService"), source_span: None }, &allocator, )); let class_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ConcreteService"), source_span: None }, + ReadVarExpr { name: Ident::from("ConcreteService"), source_span: None }, &allocator, )); @@ -717,7 +717,7 @@ mod tests { let mut deps = Vec::new_in(&allocator); deps.push(R3DependencyMetadata { token: Some(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("DepService"), source_span: None }, + ReadVarExpr { name: Ident::from("DepService"), source_span: None }, &allocator, ))), attribute_name_type: None, @@ -728,7 +728,7 @@ mod tests { }); let metadata = R3InjectableMetadataBuilder::new() - .name(Atom::from("BaseService")) + .name(Ident::from("BaseService")) .r#type(type_expr) .use_class(class_expr, false, Some(deps)) .provided_in_root() diff --git a/crates/oxc_angular_compiler/src/injectable/decorator.rs b/crates/oxc_angular_compiler/src/injectable/decorator.rs index de779e375..4f478c17d 100644 --- a/crates/oxc_angular_compiler/src/injectable/decorator.rs +++ b/crates/oxc_angular_compiler/src/injectable/decorator.rs @@ -8,7 +8,7 @@ use oxc_ast::ast::{ Argument, ArrayExpressionElement, Class, ClassElement, Decorator, Expression, MethodDefinitionKind, ObjectPropertyKind, PropertyKey, }; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use crate::factory::R3DependencyMetadata; use crate::output::ast::{OutputExpression, ReadVarExpr}; @@ -18,7 +18,7 @@ use crate::output::oxc_converter::convert_oxc_expression; #[derive(Debug)] pub struct InjectableMetadata<'a> { /// The name of the injectable class. - pub class_name: Atom<'a>, + pub class_name: Ident<'a>, /// Span of the class declaration. pub class_span: Span, /// Where this injectable is provided. @@ -218,7 +218,7 @@ pub fn extract_injectable_metadata<'a>( allocator: &'a Allocator, class: &'a Class<'a>, ) -> Option> { - let class_name: Atom<'a> = class.id.as_ref()?.name.clone().into(); + let class_name: Ident<'a> = class.id.as_ref()?.name.clone().into(); let class_span = class.span; // Find the @Injectable decorator @@ -230,12 +230,12 @@ pub fn extract_injectable_metadata<'a>( _ => return None, }; - // If no arguments, return basic metadata with default providedIn: 'root' + // If no arguments, return basic metadata with no providedIn if call_expr.arguments.is_empty() { return Some(InjectableMetadata { class_name, class_span, - provided_in: Some(ProvidedInValue::Root), + provided_in: None, use_class: None, use_factory: None, use_value: None, @@ -250,8 +250,8 @@ pub fn extract_injectable_metadata<'a>( _ => return None, }; - // Extract providedIn (default to 'root' if not specified) - let provided_in = extract_provided_in(allocator, config_obj).or(Some(ProvidedInValue::Root)); + // Extract providedIn (None if not specified) + let provided_in = extract_provided_in(allocator, config_obj); // Extract useClass let use_class = extract_use_class(allocator, config_obj); @@ -291,10 +291,10 @@ fn is_injectable_decorator(decorator: &Decorator<'_>) -> bool { } } -fn get_property_key_name<'a>(key: &'a PropertyKey<'a>) -> Option> { +fn get_property_key_name<'a>(key: &'a PropertyKey<'a>) -> Option> { match key { PropertyKey::StaticIdentifier(id) => Some(id.name.clone().into()), - PropertyKey::StringLiteral(s) => Some(s.value.clone()), + PropertyKey::StringLiteral(s) => Some(s.value.clone().into()), _ => None, } } @@ -543,7 +543,7 @@ fn extract_param_dependency<'a>( let mut self_ = false; let mut host = false; let mut inject_token: Option> = None; - let mut attribute_name: Option> = None; + let mut attribute_name: Option> = None; for decorator in ¶m.decorators { if let Some(name) = get_decorator_name(&decorator.expression) { @@ -564,7 +564,7 @@ fn extract_param_dependency<'a>( // @Attribute('attrName') - extract the attribute name if let Expression::CallExpression(call) = &decorator.expression { if let Some(Argument::StringLiteral(s)) = call.arguments.first() { - attribute_name = Some(s.value.clone()); + attribute_name = Some(s.value.clone().into()); } } } @@ -600,7 +600,7 @@ fn extract_param_dependency<'a>( } /// Get the name of a decorator from its expression. -fn get_decorator_name<'a>(expr: &'a Expression<'a>) -> Option> { +fn get_decorator_name<'a>(expr: &'a Expression<'a>) -> Option> { match expr { // @Optional Expression::Identifier(id) => Some(id.name.clone().into()), @@ -628,7 +628,7 @@ fn extract_param_token<'a>( // Handle TSTypeReference: SomeClass, SomeModule, etc. if let oxc_ast::ast::TSType::TSTypeReference(type_ref) = ts_type { // Get the type name - let type_name: Atom<'a> = match &type_ref.type_name { + let type_name: Ident<'a> = match &type_ref.type_name { oxc_ast::ast::TSTypeName::IdentifierReference(id) => id.name.clone().into(), oxc_ast::ast::TSTypeName::QualifiedName(_) | oxc_ast::ast::TSTypeName::ThisExpression(_) => { @@ -681,8 +681,8 @@ mod tests { assert!(metadata.is_some()); let metadata = metadata.unwrap(); assert_eq!(metadata.class_name.as_str(), "MyService"); - // Default to 'root' when not specified (Angular's default behavior) - assert!(matches!(metadata.provided_in, Some(ProvidedInValue::Root))); + // No providedIn when not specified + assert!(metadata.provided_in.is_none()); } #[test] @@ -715,7 +715,7 @@ mod tests { #[test] fn test_extract_injectable_with_empty_object() { - // @Injectable({}) should default to providedIn: 'root' + // @Injectable({}) should have no providedIn let allocator = Allocator::default(); let source = r#" @Injectable({}) @@ -726,8 +726,8 @@ mod tests { assert!(metadata.is_some()); let metadata = metadata.unwrap(); assert_eq!(metadata.class_name.as_str(), "MyService"); - // Default to 'root' when providedIn is not specified in the object - assert!(matches!(metadata.provided_in, Some(ProvidedInValue::Root))); + // No providedIn when not specified in the object + assert!(metadata.provided_in.is_none()); } #[test] diff --git a/crates/oxc_angular_compiler/src/injectable/definition.rs b/crates/oxc_angular_compiler/src/injectable/definition.rs index be5036450..2805bac60 100644 --- a/crates/oxc_angular_compiler/src/injectable/definition.rs +++ b/crates/oxc_angular_compiler/src/injectable/definition.rs @@ -205,18 +205,18 @@ mod tests { use crate::output::ast::ReadVarExpr; use crate::output::emitter::JsEmitter; use oxc_allocator::Box; - use oxc_span::Atom; + use oxc_span::Ident; #[test] fn test_generate_simple_injectable_definition() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("MyService"), source_span: None }, + ReadVarExpr { name: Ident::from("MyService"), source_span: None }, &allocator, )); let metadata = R3InjectableMetadataBuilder::new() - .name(Atom::from("MyService")) + .name(Ident::from("MyService")) .r#type(type_expr) .provided_in_root() .build() @@ -243,12 +243,12 @@ mod tests { fn test_generate_injectable_definition_no_provided_in() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("LocalService"), source_span: None }, + ReadVarExpr { name: Ident::from("LocalService"), source_span: None }, &allocator, )); let metadata = R3InjectableMetadataBuilder::new() - .name(Atom::from("LocalService")) + .name(Ident::from("LocalService")) .r#type(type_expr) .build() .unwrap(); @@ -268,12 +268,12 @@ mod tests { fn test_generate_injectable_definition_with_platform() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("PlatformService"), source_span: None }, + ReadVarExpr { name: Ident::from("PlatformService"), source_span: None }, &allocator, )); let metadata = R3InjectableMetadataBuilder::new() - .name(Atom::from("PlatformService")) + .name(Ident::from("PlatformService")) .r#type(type_expr) .provided_in_platform() .build() @@ -291,12 +291,12 @@ mod tests { fn test_generate_injectable_definition_with_any() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("AnyService"), source_span: None }, + ReadVarExpr { name: Ident::from("AnyService"), source_span: None }, &allocator, )); let metadata = R3InjectableMetadataBuilder::new() - .name(Atom::from("AnyService")) + .name(Ident::from("AnyService")) .r#type(type_expr) .provided_in_any() .build() @@ -317,12 +317,12 @@ mod tests { // This test verifies the definition is correctly structured for tree-shaking. let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TreeShakableService"), source_span: None }, + ReadVarExpr { name: Ident::from("TreeShakableService"), source_span: None }, &allocator, )); let metadata = R3InjectableMetadataBuilder::new() - .name(Atom::from("TreeShakableService")) + .name(Ident::from("TreeShakableService")) .r#type(type_expr) .provided_in_root() .build() diff --git a/crates/oxc_angular_compiler/src/injectable/metadata.rs b/crates/oxc_angular_compiler/src/injectable/metadata.rs index e0eb81c1e..79936adff 100644 --- a/crates/oxc_angular_compiler/src/injectable/metadata.rs +++ b/crates/oxc_angular_compiler/src/injectable/metadata.rs @@ -3,7 +3,7 @@ //! Ported from Angular's `injectable_compiler_2.ts`. use oxc_allocator::Vec; -use oxc_span::Atom; +use oxc_span::Ident; use crate::factory::R3DependencyMetadata; use crate::output::ast::OutputExpression; @@ -74,7 +74,7 @@ pub enum ProvidedIn<'a> { #[derive(Debug)] pub struct R3InjectableMetadata<'a> { /// Name of the injectable type. - pub name: Atom<'a>, + pub name: Ident<'a>, /// An expression representing a reference to the injectable class. pub r#type: OutputExpression<'a>, @@ -103,7 +103,7 @@ impl<'a> R3InjectableMetadata<'a> { /// Builder for R3InjectableMetadata. pub struct R3InjectableMetadataBuilder<'a> { - name: Option>, + name: Option>, r#type: Option>, type_argument_count: u32, provided_in: ProvidedIn<'a>, @@ -125,7 +125,7 @@ impl<'a> R3InjectableMetadataBuilder<'a> { } /// Set the injectable name. - pub fn name(mut self, name: Atom<'a>) -> Self { + pub fn name(mut self, name: Ident<'a>) -> Self { self.name = Some(name); self } diff --git a/crates/oxc_angular_compiler/src/injector/compiler.rs b/crates/oxc_angular_compiler/src/injector/compiler.rs index 85bc6e7d1..1fdfaae6b 100644 --- a/crates/oxc_angular_compiler/src/injector/compiler.rs +++ b/crates/oxc_angular_compiler/src/injector/compiler.rs @@ -11,7 +11,7 @@ //! ``` use oxc_allocator::{Allocator, Box, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use super::metadata::R3InjectorMetadata; use crate::output::ast::{ @@ -64,7 +64,7 @@ fn build_definition_map<'a>( // providers: [...] (only if present) if let Some(providers) = &metadata.providers { entries.push(LiteralMapEntry { - key: Atom::from("providers"), + key: Ident::from("providers"), value: providers.clone_in(allocator), quoted: false, }); @@ -87,7 +87,7 @@ fn build_definition_map<'a>( }; entries.push(LiteralMapEntry { - key: Atom::from("imports"), + key: Ident::from("imports"), value: imports_value, quoted: false, }); @@ -106,12 +106,12 @@ fn create_define_injector_call<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::DEFINE_INJECTOR), + name: Ident::from(Identifiers::DEFINE_INJECTOR), optional: false, source_span: None, }, @@ -150,12 +150,12 @@ mod tests { fn test_compile_simple_injector() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("MyModule"), source_span: None }, + ReadVarExpr { name: Ident::from("MyModule"), source_span: None }, &allocator, )); let metadata = R3InjectorMetadataBuilder::new(&allocator) - .name(Atom::from("MyModule")) + .name(Ident::from("MyModule")) .r#type(type_expr) .build() .unwrap(); @@ -172,17 +172,17 @@ mod tests { fn test_compile_injector_with_providers() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ProviderModule"), source_span: None }, + ReadVarExpr { name: Ident::from("ProviderModule"), source_span: None }, &allocator, )); let providers_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("PROVIDERS"), source_span: None }, + ReadVarExpr { name: Ident::from("PROVIDERS"), source_span: None }, &allocator, )); let metadata = R3InjectorMetadataBuilder::new(&allocator) - .name(Atom::from("ProviderModule")) + .name(Ident::from("ProviderModule")) .r#type(type_expr) .providers(providers_expr) .build() @@ -201,21 +201,21 @@ mod tests { fn test_compile_injector_with_imports() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ImportModule"), source_span: None }, + ReadVarExpr { name: Ident::from("ImportModule"), source_span: None }, &allocator, )); let import1 = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("CommonModule"), source_span: None }, + ReadVarExpr { name: Ident::from("CommonModule"), source_span: None }, &allocator, )); let import2 = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("FormsModule"), source_span: None }, + ReadVarExpr { name: Ident::from("FormsModule"), source_span: None }, &allocator, )); let metadata = R3InjectorMetadataBuilder::new(&allocator) - .name(Atom::from("ImportModule")) + .name(Ident::from("ImportModule")) .r#type(type_expr) .add_import(import1) .add_import(import2) @@ -236,12 +236,12 @@ mod tests { fn test_compile_injector_empty_statements() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("EmptyModule"), source_span: None }, + ReadVarExpr { name: Ident::from("EmptyModule"), source_span: None }, &allocator, )); let metadata = R3InjectorMetadataBuilder::new(&allocator) - .name(Atom::from("EmptyModule")) + .name(Ident::from("EmptyModule")) .r#type(type_expr) .build() .unwrap(); diff --git a/crates/oxc_angular_compiler/src/injector/metadata.rs b/crates/oxc_angular_compiler/src/injector/metadata.rs index 461252eda..66926f318 100644 --- a/crates/oxc_angular_compiler/src/injector/metadata.rs +++ b/crates/oxc_angular_compiler/src/injector/metadata.rs @@ -3,7 +3,7 @@ //! Ported from Angular's `render3/r3_injector_compiler.ts`. use oxc_allocator::Vec; -use oxc_span::Atom; +use oxc_span::Ident; use crate::output::ast::OutputExpression; @@ -14,7 +14,7 @@ use crate::output::ast::OutputExpression; #[derive(Debug)] pub struct R3InjectorMetadata<'a> { /// Name of the injector type. - pub name: Atom<'a>, + pub name: Ident<'a>, /// An expression representing a reference to the injector class. pub r#type: OutputExpression<'a>, @@ -46,7 +46,7 @@ impl<'a> R3InjectorMetadata<'a> { /// Builder for R3InjectorMetadata. pub struct R3InjectorMetadataBuilder<'a> { - name: Option>, + name: Option>, r#type: Option>, providers: Option>, imports: Vec<'a, OutputExpression<'a>>, @@ -66,7 +66,7 @@ impl<'a> R3InjectorMetadataBuilder<'a> { } /// Set the injector name. - pub fn name(mut self, name: Atom<'a>) -> Self { + pub fn name(mut self, name: Ident<'a>) -> Self { self.name = Some(name); self } diff --git a/crates/oxc_angular_compiler/src/ir/expression.rs b/crates/oxc_angular_compiler/src/ir/expression.rs index b7a6bc106..6f9f06ff9 100644 --- a/crates/oxc_angular_compiler/src/ir/expression.rs +++ b/crates/oxc_angular_compiler/src/ir/expression.rs @@ -6,7 +6,7 @@ //! Ported from Angular's `template/pipeline/ir/src/expression.ts`. use oxc_allocator::{Box, Vec}; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use super::enums::ExpressionKind; use super::ops::{SlotId, XrefId}; @@ -793,7 +793,7 @@ pub struct IrParenthesizedExpr<'a> { #[derive(Debug)] pub struct LexicalReadExpr<'a> { /// Variable name to read. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Source span. pub source_span: Option, } @@ -884,7 +884,7 @@ pub struct ReadVariableExpr<'a> { /// Variable XrefId. pub xref: XrefId, /// Resolved variable name. - pub name: Option>, + pub name: Option>, /// Source span. pub source_span: Option, } @@ -899,7 +899,7 @@ pub struct ResolvedPropertyReadExpr<'a> { /// The resolved receiver expression (e.g., ReadVariable for a loop variable). pub receiver: Box<'a, IrExpression<'a>>, /// Property name to read. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Source span. pub source_span: Option, } @@ -961,7 +961,7 @@ pub struct ResolvedSafePropertyReadExpr<'a> { /// The resolved receiver expression (e.g., ReadVariable for a loop variable). pub receiver: Box<'a, IrExpression<'a>>, /// Property name to read. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Source span. pub source_span: Option, } @@ -985,7 +985,7 @@ pub struct ResolvedTemplateLiteralExpr<'a> { #[derive(Debug, Clone)] pub struct IrTemplateLiteralElement<'a> { /// The text content. - pub text: Atom<'a>, + pub text: Ident<'a>, /// Source span. pub source_span: Option, } @@ -1007,7 +1007,7 @@ pub struct DerivedLiteralArrayExpr<'a> { #[derive(Debug)] pub struct DerivedLiteralMapExpr<'a> { /// Map keys (string keys from the original literal map). - pub keys: Vec<'a, Atom<'a>>, + pub keys: Vec<'a, Ident<'a>>, /// Map values - can be Ast (constants) or PureFunctionParameter (refs). pub values: Vec<'a, IrExpression<'a>>, /// Whether each key is quoted. @@ -1031,7 +1031,7 @@ pub struct IrLiteralArrayExpr<'a> { #[derive(Debug)] pub struct IrLiteralMapExpr<'a> { /// Map keys (string keys from the original literal map). - pub keys: Vec<'a, Atom<'a>>, + pub keys: Vec<'a, Ident<'a>>, /// Map values as IR expressions. pub values: Vec<'a, IrExpression<'a>>, /// Whether each key is quoted. @@ -1072,7 +1072,7 @@ pub struct PipeBindingExpr<'a> { /// Target slot. pub target_slot: SlotHandle, /// Pipe name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Pipe arguments. pub args: Vec<'a, IrExpression<'a>>, /// Variable offset. @@ -1089,7 +1089,7 @@ pub struct PipeBindingVariadicExpr<'a> { /// Target slot. pub target_slot: SlotHandle, /// Pipe name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Arguments as an array expression. pub args: Box<'a, IrExpression<'a>>, /// Number of arguments. @@ -1106,7 +1106,7 @@ pub struct SafePropertyReadExpr<'a> { /// Receiver expression. pub receiver: Box<'a, IrExpression<'a>>, /// Property name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Source span. pub source_span: Option, } @@ -1159,7 +1159,7 @@ pub struct AssignTemporaryExpr<'a> { /// Temporary variable XrefId. pub xref: XrefId, /// Resolved variable name. - pub name: Option>, + pub name: Option>, /// Source span. pub source_span: Option, } @@ -1170,7 +1170,7 @@ pub struct ReadTemporaryExpr<'a> { /// Temporary variable XrefId. pub xref: XrefId, /// Resolved variable name. - pub name: Option>, + pub name: Option>, /// Source span. pub source_span: Option, } @@ -1197,7 +1197,7 @@ pub struct ConditionalCaseExpr<'a> { /// Target slot. pub target_slot: SlotHandle, /// Alias variable name. - pub alias: Option>, + pub alias: Option>, /// Source span. pub source_span: Option, } @@ -1261,13 +1261,13 @@ pub struct StoreLetExpr<'a> { #[derive(Debug)] pub struct Interpolation<'a> { /// Static string parts. - pub strings: Vec<'a, Atom<'a>>, + pub strings: Vec<'a, Ident<'a>>, /// Dynamic expression parts. pub expressions: Vec<'a, IrExpression<'a>>, /// I18n placeholder names for each expression. /// Used by convert_i18n_bindings phase to create I18nExpression ops. /// Empty if not in an i18n context. - pub i18n_placeholders: Vec<'a, Atom<'a>>, + pub i18n_placeholders: Vec<'a, Ident<'a>>, /// Source span. pub source_span: Option, } @@ -1279,7 +1279,7 @@ impl<'a> Interpolation<'a> { } /// Returns the constant string value if this is a pure interpolation. - pub fn const_value(&self) -> Option<&Atom<'a>> { + pub fn const_value(&self) -> Option<&Ident<'a>> { if self.is_const_string() && self.strings.len() == 1 { Some(&self.strings[0]) } else { diff --git a/crates/oxc_angular_compiler/src/ir/ops.rs b/crates/oxc_angular_compiler/src/ir/ops.rs index a672cb6e5..7cd5017f8 100644 --- a/crates/oxc_angular_compiler/src/ir/ops.rs +++ b/crates/oxc_angular_compiler/src/ir/ops.rs @@ -10,7 +10,7 @@ use std::ptr::NonNull; use oxc_allocator::{Box, Vec}; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use super::enums::{ AnimationBindingKind, AnimationKind, BindingKind, DeferOpModifierKind, DeferTriggerKind, @@ -58,20 +58,20 @@ pub enum I18nSlotHandle { #[derive(Debug, Clone, PartialEq, Eq)] pub struct I18nPlaceholder<'a> { /// The placeholder name for the opening tag (e.g., "START_TAG_DIV"). - pub start_name: Atom<'a>, + pub start_name: Ident<'a>, /// The placeholder name for the closing tag (e.g., "CLOSE_TAG_DIV"). /// `None` for self-closing/void elements. - pub close_name: Option>, + pub close_name: Option>, } impl<'a> I18nPlaceholder<'a> { /// Creates a new I18nPlaceholder. - pub fn new(start_name: Atom<'a>, close_name: Option>) -> Self { + pub fn new(start_name: Ident<'a>, close_name: Option>) -> Self { Self { start_name, close_name } } /// Creates a placeholder for a self-closing/void element. - pub fn self_closing(start_name: Atom<'a>) -> Self { + pub fn self_closing(start_name: Ident<'a>) -> Self { Self { start_name, close_name: None } } } @@ -658,13 +658,13 @@ pub struct ElementStartOp<'a> { /// Cross-reference ID. pub xref: XrefId, /// Element tag name. - pub tag: Atom<'a>, + pub tag: Ident<'a>, /// Assigned slot. pub slot: Option, /// Namespace. pub namespace: Namespace, /// Attribute namespace (e.g., "xlink" for SVG). - pub attribute_namespace: Option>, + pub attribute_namespace: Option>, /// Local references attached to this element. pub local_refs: Vec<'a, LocalRef<'a>>, /// Index into the consts array for local refs. @@ -686,13 +686,13 @@ pub struct ElementOp<'a> { /// Cross-reference ID. pub xref: XrefId, /// Element tag name. - pub tag: Atom<'a>, + pub tag: Ident<'a>, /// Assigned slot. pub slot: Option, /// Namespace. pub namespace: Namespace, /// Attribute namespace. - pub attribute_namespace: Option>, + pub attribute_namespace: Option>, /// Local references. pub local_refs: Vec<'a, LocalRef<'a>>, /// Index into the consts array for local refs. @@ -728,15 +728,15 @@ pub struct TemplateOp<'a> { pub slot: Option, /// HTML tag name for this template element. /// Used for content projection matching (e.g., `` vs `
`). - pub tag: Option>, + pub tag: Option>, /// Namespace. pub namespace: Namespace, /// Template kind. pub template_kind: TemplateKind, /// Function name suffix. - pub fn_name_suffix: Option>, + pub fn_name_suffix: Option>, /// Block of the template. - pub block: Option>, + pub block: Option>, /// Decl count for structural directive compatibility. pub decl_count: Option, /// The number of binding variable slots used by this template. @@ -847,9 +847,9 @@ pub struct ConditionalOp<'a> { /// Template kind. pub template_kind: TemplateKind, /// Function name suffix. - pub fn_name_suffix: Atom<'a>, + pub fn_name_suffix: Ident<'a>, /// HTML tag name (for content projection). - pub tag: Option>, + pub tag: Option>, /// The number of declaration slots used by this conditional. /// Set by allocate_slots phase. pub decls: Option, @@ -888,9 +888,9 @@ pub struct ConditionalBranchCreateOp<'a> { /// Template kind. pub template_kind: TemplateKind, /// Function name suffix. - pub fn_name_suffix: Atom<'a>, + pub fn_name_suffix: Ident<'a>, /// HTML tag name (for content projection). - pub tag: Option>, + pub tag: Option>, /// The number of declaration slots used by this branch. /// Set by allocate_slots phase. pub decls: Option, @@ -931,11 +931,11 @@ pub struct TextOp<'a> { /// Assigned slot. pub slot: Option, /// Static text value. - pub initial_value: Atom<'a>, + pub initial_value: Ident<'a>, /// I18n placeholder. - pub i18n_placeholder: Option>, + pub i18n_placeholder: Option>, /// ICU placeholder (for text inside ICU expressions). - pub icu_placeholder: Option>, + pub icu_placeholder: Option>, } /// Declare an event listener. @@ -948,25 +948,25 @@ pub struct ListenerOp<'a> { /// Slot to target. pub target_slot: SlotId, /// Tag name of the element on which this listener is placed. Null for host bindings. - pub tag: Option>, + pub tag: Option>, /// Whether this listener is from a host binding. pub host_listener: bool, /// Event name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Handler expression (the expression to execute on event). pub handler_expression: Option>>, /// Handler operations (legacy, for complex handlers). pub handler_ops: Vec<'a, UpdateOp<'a>>, /// Function name. - pub handler_fn_name: Option>, + pub handler_fn_name: Option>, /// Consume functions. - pub consume_fn_name: Option>, + pub consume_fn_name: Option>, /// Whether this is an animation listener. pub is_animation_listener: bool, /// Animation phase. pub animation_phase: Option, /// Event target (window, document, body). - pub event_target: Option>, + pub event_target: Option>, /// Whether this listener uses $event. pub consumes_dollar_event: bool, } @@ -981,7 +981,7 @@ pub struct PipeOp<'a> { /// Assigned slot. pub slot: Option, /// Pipe name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Number of arguments. pub num_args: u32, } @@ -1033,7 +1033,7 @@ pub struct DeferOp<'a> { /// Corresponds to `ownResolverFn` in Angular TS. pub own_resolver_fn: Option>, /// SSR unique ID. - pub ssr_unique_id: Option>, + pub ssr_unique_id: Option>, /// Defer block flags (e.g., HasHydrateTriggers = 1). /// Corresponds to `flags` in Angular TS's DeferOp. pub flags: Option, @@ -1060,7 +1060,7 @@ pub struct DeferOnOp<'a> { /// -1 means starting from placeholder, 0 means same view as defer owner. pub target_slot_view_steps: Option, /// Target name for local ref targeting. - pub target_name: Option>, + pub target_name: Option>, /// Timer delay. pub delay: Option, /// Viewport options (for viewport trigger). @@ -1101,18 +1101,18 @@ pub struct I18nMessageOp<'a> { pub i18n_block: Option, /// Message placeholder for ICU sub-messages. /// Only set for ICU placeholder messages (extracted from parent). - pub message_placeholder: Option>, + pub message_placeholder: Option>, /// Message ID. - pub message_id: Option>, + pub message_id: Option>, /// Custom message ID. - pub custom_id: Option>, + pub custom_id: Option>, /// Message meaning. - pub meaning: Option>, + pub meaning: Option>, /// Message description. - pub description: Option>, + pub description: Option>, /// The serialized message string for goog.getMsg and $localize. /// Contains the message text with placeholder markers like "{$interpolation}". - pub message_string: Option>, + pub message_string: Option>, /// Whether message needs postprocessing (has params with multiple values). pub needs_postprocessing: bool, /// Sub-messages. @@ -1156,7 +1156,7 @@ pub struct ProjectionOp<'a> { /// I18n placeholder data (start_name and close_name for ng-content). pub i18n_placeholder: Option>, /// Selector attribute. - pub selector: Option>, + pub selector: Option>, /// Fallback template. pub fallback: Option, /// I18n placeholder data for fallback view. @@ -1181,7 +1181,7 @@ pub struct RepeaterCreateOp<'a> { /// Track function. pub track: Box<'a, IrExpression<'a>>, /// Track function name. - pub track_fn_name: Option>, + pub track_fn_name: Option>, /// Some kinds of expressions (e.g. safe reads or nullish coalescing) require additional ops /// in order to work. This list keeps track of those ops, if they're necessary. /// Set by track_fn_optimization phase when the track expression cannot be optimized. @@ -1206,12 +1206,12 @@ pub struct RepeaterCreateOp<'a> { /// Var names for template. pub var_names: RepeaterVarNames<'a>, /// HTML tag name (for content projection). - pub tag: Option>, + pub tag: Option>, /// Const index for attributes (for content projection). /// Set by const_collection phase. pub attributes: Option, /// HTML tag name for empty view (for content projection). - pub empty_tag: Option>, + pub empty_tag: Option>, /// Const index for empty view attributes (for content projection). /// Set by const_collection phase. pub empty_attributes: Option, @@ -1225,20 +1225,20 @@ pub struct RepeaterCreateOp<'a> { #[derive(Debug)] pub struct RepeaterVarNames<'a> { /// Alias for the item ($implicit). - pub item: Option>, + pub item: Option>, /// Alias for $count. - pub count: Option>, + pub count: Option>, /// Aliases for $index (can be multiple, e.g. `let i = $index, j = $index`). /// Angular stores these in a `Set`. - pub index: Vec<'a, Atom<'a>>, + pub index: Vec<'a, Ident<'a>>, /// Alias for $first. - pub first: Option>, + pub first: Option>, /// Alias for $last. - pub last: Option>, + pub last: Option>, /// Alias for $even. - pub even: Option>, + pub even: Option>, /// Alias for $odd. - pub odd: Option>, + pub odd: Option>, } /// Initialize a @let slot. @@ -1251,7 +1251,7 @@ pub struct DeclareLetOp<'a> { /// Assigned slot. pub slot: Option, /// Variable name. - pub name: Atom<'a>, + pub name: Ident<'a>, } /// Start an i18n block. @@ -1325,7 +1325,7 @@ pub struct IcuStartOp<'a> { /// Message instance ID for metadata lookup. pub message: Option, /// ICU placeholder. - pub icu_placeholder: Option>, + pub icu_placeholder: Option>, } /// End an ICU expression. @@ -1352,20 +1352,20 @@ pub struct I18nContextOp<'a> { /// Maps placeholder names to lists of param values. pub params: oxc_allocator::HashMap< 'a, - Atom<'a>, + Ident<'a>, oxc_allocator::Vec<'a, super::i18n_params::I18nParamValue>, >, /// Post-processing params map for ICU expressions. /// These are processed after the main params during message generation. pub postprocessing_params: oxc_allocator::HashMap< 'a, - Atom<'a>, + Ident<'a>, oxc_allocator::Vec<'a, super::i18n_params::I18nParamValue>, >, /// ICU placeholder literals map. /// Maps ICU placeholder names to their formatted string values. /// These are string literals like "Hello ${�0�}!" generated from IcuPlaceholderOp. - pub icu_placeholder_literals: oxc_allocator::HashMap<'a, Atom<'a>, Atom<'a>>, + pub icu_placeholder_literals: oxc_allocator::HashMap<'a, Ident<'a>, Ident<'a>>, /// Message instance ID reference (for metadata lookup). /// /// Stores the i18n message's instance_id (not an XrefId) to look up metadata @@ -1395,7 +1395,7 @@ pub struct I18nAttributesOp<'a> { #[derive(Debug)] pub struct I18nAttributeConfig<'a> { /// Attribute name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// I18n message. pub message: XrefId, } @@ -1410,7 +1410,7 @@ pub struct VariableOp<'a> { /// Variable kind. pub kind: SemanticVariableKind, /// Variable name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Initializer expression. pub initializer: Box<'a, IrExpression<'a>>, /// Variable flags. @@ -1434,9 +1434,9 @@ pub struct ExtractedAttributeOp<'a> { /// Binding kind. pub binding_kind: BindingKind, /// Namespace. - pub namespace: Option>, + pub namespace: Option>, /// Attribute name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Attribute value. pub value: Option>>, /// Security context. @@ -1453,7 +1453,7 @@ pub struct ExtractedAttributeOp<'a> { /// i18n context. pub i18n_context: Option, /// Trusted value function for security-sensitive constant attributes. - pub trusted_value_fn: Option>, + pub trusted_value_fn: Option>, } /// Source location for debugging. @@ -1464,7 +1464,7 @@ pub struct SourceLocationOp<'a> { /// Target element. pub target: XrefId, /// Template URL. - pub template_url: Atom<'a>, + pub template_url: Ident<'a>, /// Line number. pub line: u32, /// Column number. @@ -1507,7 +1507,7 @@ pub struct InterpolateTextOp<'a> { /// Interpolation expression. pub interpolation: Box<'a, IrExpression<'a>>, /// I18n placeholder. - pub i18n_placeholder: Option>, + pub i18n_placeholder: Option>, } /// Bind to an element property. @@ -1518,7 +1518,7 @@ pub struct PropertyOp<'a> { /// Target element. pub target: XrefId, /// Property name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Expression. pub expression: Box<'a, IrExpression<'a>>, /// Whether this is a host binding. @@ -1526,7 +1526,7 @@ pub struct PropertyOp<'a> { /// Security context. pub security_context: SecurityContext, /// Sanitizer function. - pub sanitizer: Option>, + pub sanitizer: Option>, /// Whether property should be updated structurally. pub is_structural: bool, /// I18n context. @@ -1547,11 +1547,11 @@ pub struct StylePropOp<'a> { /// Target element. pub target: XrefId, /// Style name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Expression. pub expression: Box<'a, IrExpression<'a>>, /// Unit suffix. - pub unit: Option>, + pub unit: Option>, } /// Bind to a class property. @@ -1562,7 +1562,7 @@ pub struct ClassPropOp<'a> { /// Target element. pub target: XrefId, /// Class name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Expression. pub expression: Box<'a, IrExpression<'a>>, } @@ -1608,15 +1608,15 @@ pub struct AttributeOp<'a> { /// Target element. pub target: XrefId, /// Attribute name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Expression. pub expression: Box<'a, IrExpression<'a>>, /// Namespace. - pub namespace: Option>, + pub namespace: Option>, /// Security context. pub security_context: SecurityContext, /// Sanitizer function. - pub sanitizer: Option>, + pub sanitizer: Option>, /// I18n context. pub i18n_context: Option, /// I18n message instance ID. @@ -1645,7 +1645,7 @@ pub struct DomPropertyOp<'a> { /// Target element. pub target: XrefId, /// Property name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Expression. pub expression: Box<'a, IrExpression<'a>>, /// Whether this is a host binding. @@ -1653,7 +1653,7 @@ pub struct DomPropertyOp<'a> { /// Security context. pub security_context: SecurityContext, /// Sanitizer function. - pub sanitizer: Option>, + pub sanitizer: Option>, /// Binding kind (for animation handling). pub binding_kind: BindingKind, } @@ -1679,13 +1679,13 @@ pub struct TwoWayPropertyOp<'a> { /// Target element. pub target: XrefId, /// Property name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Expression. pub expression: Box<'a, IrExpression<'a>>, /// Security context. pub security_context: SecurityContext, /// Sanitizer function. - pub sanitizer: Option>, + pub sanitizer: Option>, } /// Two-way listener (CREATE op). @@ -1698,13 +1698,13 @@ pub struct TwoWayListenerOp<'a> { /// Target slot. pub target_slot: SlotId, /// Tag name of the element on which this listener is placed. - pub tag: Option>, + pub tag: Option>, /// Event name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Handler operations. pub handler_ops: Vec<'a, UpdateOp<'a>>, /// Function name. - pub handler_fn_name: Option>, + pub handler_fn_name: Option>, } /// Store @let value. @@ -1717,7 +1717,7 @@ pub struct StoreLetOp<'a> { /// Target slot. pub target_slot: SlotId, /// Name that the user set when declaring the `@let`. - pub declared_name: Atom<'a>, + pub declared_name: Ident<'a>, /// Value expression. pub value: Box<'a, IrExpression<'a>>, } @@ -1766,10 +1766,10 @@ pub struct I18nExpressionOp<'a> { /// Expression usage. pub usage: I18nExpressionFor, /// Attribute name (for attribute bindings). - pub name: Atom<'a>, + pub name: Ident<'a>, /// I18n placeholder name associated with this expression. /// This can be None if the expression is part of an ICU placeholder. - pub i18n_placeholder: Option>, + pub i18n_placeholder: Option>, /// Reference to ICU placeholder op if this expression is part of an ICU. pub icu_placeholder: Option, } @@ -1798,10 +1798,10 @@ pub struct IcuPlaceholderOp<'a> { /// Cross-reference ID. pub xref: XrefId, /// Placeholder name in the ICU expression. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Static string segments to be combined with expression placeholders. /// Works like interpolation: strings.len() == expression_placeholders.len() + 1 - pub strings: oxc_allocator::Vec<'a, Atom<'a>>, + pub strings: oxc_allocator::Vec<'a, Ident<'a>>, /// Expression placeholders collected from I18nExpression ops. /// These are combined with strings to form the full placeholder value. pub expression_placeholders: oxc_allocator::Vec<'a, super::i18n_params::I18nParamValue>, @@ -1817,11 +1817,11 @@ pub struct BindingOp<'a> { /// Binding kind. pub kind: BindingKind, /// Binding name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Expression. pub expression: Box<'a, IrExpression<'a>>, /// Unit suffix. - pub unit: Option>, + pub unit: Option>, /// Security context. pub security_context: SecurityContext, /// I18n message instance ID. @@ -1851,13 +1851,13 @@ pub struct AnimationOp<'a> { /// Target element. pub target: XrefId, /// Animation name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Animation kind (enter or leave). pub animation_kind: AnimationKind, /// Handler operations (contains the expression as a return statement). pub handler_ops: Vec<'a, UpdateOp<'a>>, /// Function name for the handler. - pub handler_fn_name: Option>, + pub handler_fn_name: Option>, /// I18n message instance ID. /// /// Stores the i18n message's instance_id for dedup, not an XrefId. @@ -1865,7 +1865,7 @@ pub struct AnimationOp<'a> { /// Security context. pub security_context: SecurityContext, /// Sanitizer function. - pub sanitizer: Option>, + pub sanitizer: Option>, } /// Animation string binding (CREATE op). @@ -1876,7 +1876,7 @@ pub struct AnimationStringOp<'a> { /// Target element. pub target: XrefId, /// Animation name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Animation kind (enter or leave). pub animation_kind: AnimationKind, /// Expression. @@ -1891,7 +1891,7 @@ pub struct AnimationBindingOp<'a> { /// Target element. pub target: XrefId, /// Animation name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Expression. pub expression: Box<'a, IrExpression<'a>>, /// Binding kind. @@ -1908,15 +1908,15 @@ pub struct AnimationListenerOp<'a> { /// Target slot. pub target_slot: SlotId, /// Tag name of the element on which this listener is placed. Null for host bindings. - pub tag: Option>, + pub tag: Option>, /// Whether this listener is from a host binding. pub host_listener: bool, /// Event name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Handler operations. pub handler_ops: Vec<'a, UpdateOp<'a>>, /// Function name. - pub handler_fn_name: Option>, + pub handler_fn_name: Option>, /// Animation phase. pub phase: AnimationKind, /// Whether this listener uses $event. @@ -1933,7 +1933,7 @@ pub struct UpdateVariableOp<'a> { /// Variable kind. pub kind: SemanticVariableKind, /// Variable name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Initializer expression. pub initializer: Box<'a, IrExpression<'a>>, /// Variable flags. @@ -1955,7 +1955,7 @@ pub struct ControlOp<'a> { /// Target element. pub target: XrefId, /// Property name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Expression. pub expression: Box<'a, IrExpression<'a>>, /// Security context. @@ -1984,7 +1984,7 @@ pub struct StatementOp<'a> { #[derive(Debug)] pub struct LocalRef<'a> { /// Reference name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Target directive/component. - pub target: Atom<'a>, + pub target: Ident<'a>, } diff --git a/crates/oxc_angular_compiler/src/linker/mod.rs b/crates/oxc_angular_compiler/src/linker/mod.rs index 7e2865739..3c63ff805 100644 --- a/crates/oxc_angular_compiler/src/linker/mod.rs +++ b/crates/oxc_angular_compiler/src/linker/mod.rs @@ -43,6 +43,18 @@ use oxc_span::{GetSpan, SourceType}; use crate::optimizer::Edit; use crate::pipeline::selector::{R3SelectorElement, parse_selector_to_r3_selector}; +/// Check if an object property key needs quoting because it contains unsafe characters. +/// +/// Matches Angular's `UNSAFE_OBJECT_KEY_NAME_REGEXP = /[-.]/` from `render3/view/util.ts`. +fn needs_object_key_quoting(key: &str) -> bool { + key.contains('.') || key.contains('-') +} + +/// Quote a property key if it contains unsafe characters (dots or hyphens). +fn quote_key(key: &str) -> String { + if needs_object_key_quoting(key) { format!("\"{key}\"") } else { key.to_string() } +} + /// Partial declaration function names to link. const DECLARE_FACTORY: &str = "\u{0275}\u{0275}ngDeclareFactory"; const DECLARE_INJECTABLE: &str = "\u{0275}\u{0275}ngDeclareInjectable"; @@ -599,7 +611,7 @@ fn get_bool_property(obj: &ObjectExpression<'_>, name: &str) -> Option { && matches!(&prop.key, PropertyKey::StaticIdentifier(ident) if ident.name == name) && let Expression::BooleanLiteral(lit) = &prop.value { - return Some(lit.value); + return Some(lit.value.into()); } } None @@ -1209,6 +1221,7 @@ fn link_class_metadata_async( /// Declaration format (`ɵɵngDeclareDirective`): /// - `propertyName: "publicName"` (simple) /// - `propertyName: ["publicName", "classPropertyName"]` (aliased) +/// - `propertyName: ["publicName", "classPropertyName", transformFn]` (aliased with transform) /// - `propertyName: { classPropertyName: "...", publicName: "...", isRequired: bool, /// isSignal: bool, transformFunction: expr }` (Angular 16+ object format) /// @@ -1234,36 +1247,37 @@ fn convert_inputs_to_definition_format(inputs_obj: &ObjectExpression<'_>, source } }; + let quoted_key = quote_key(&key); + match &p.value { // Simple string: propertyName: "publicName" → keep as is Expression::StringLiteral(lit) => { - entries.push(format!("{key}: \"{}\"", lit.value)); + entries.push(format!("{quoted_key}: \"{}\"", lit.value.as_str())); } // Array: check if it's declaration format [publicName, classPropertyName] // and convert to definition format [InputFlags, publicName, classPropertyName] Expression::ArrayExpression(arr) => { - if arr.elements.len() == 2 { - // Check if first element is a string (declaration format) - let first_is_string = matches!( - arr.elements.first(), - Some(ArrayExpressionElement::StringLiteral(_)) - ); - if first_is_string { - // Declaration format: ["publicName", "classPropertyName"] - // Convert to: [0, "publicName", "classPropertyName"] - let arr_source = - &source[arr.span.start as usize + 1..arr.span.end as usize - 1]; - entries.push(format!("{key}: [0, {arr_source}]")); - } else { - // Already in definition format or unknown, keep as is - let val = - &source[p.value.span().start as usize..p.value.span().end as usize]; - entries.push(format!("{key}: {val}")); - } - } else { - // 3+ elements likely already in definition format, keep as is - let val = &source[p.value.span().start as usize..p.value.span().end as usize]; - entries.push(format!("{key}: {val}")); + let val = &source[p.value.span().start as usize..p.value.span().end as usize]; + let first_is_string = + matches!(arr.elements.first(), Some(ArrayExpressionElement::StringLiteral(_))); + + if !first_is_string { + // Already in definition format or unknown, keep as is. + entries.push(format!("{quoted_key}: {val}")); + continue; + } + + let arr_source = &source[arr.span.start as usize + 1..arr.span.end as usize - 1]; + match arr.elements.len() { + // Declaration format: ["publicName", "classPropertyName"] + // Convert to: [0, "publicName", "classPropertyName"] + 2 => entries.push(format!("{quoted_key}: [0, {arr_source}]")), + // Since Angular 16.1, support multi-directive inputs, so we need to convert to the definition format. + // Declaration format: ["publicName", "classPropertyName", transformFn] + // Convert to: [2, "publicName", "classPropertyName", transformFn] + 3 => entries.push(format!("{quoted_key}: [2, {arr_source}]")), + // 4+ elements likely already in definition format, keep as is. + _ => entries.push(format!("{quoted_key}: {val}")), } } // Object: Angular 16+ format with classPropertyName, publicName, isRequired, etc. @@ -1290,20 +1304,21 @@ fn convert_inputs_to_definition_format(inputs_obj: &ObjectExpression<'_>, source if flags == 0 && transform.is_none() && public_name == declared_name { // Simple case: no flags, no transform, names match - entries.push(format!("{key}: \"{public_name}\"")); + entries.push(format!("{quoted_key}: \"{public_name}\"")); } else if let Some(transform_fn) = transform { entries.push(format!( - "{key}: [{flags}, \"{public_name}\", \"{declared_name}\", {transform_fn}]" + "{quoted_key}: [{flags}, \"{public_name}\", \"{declared_name}\", {transform_fn}]" )); } else { - entries - .push(format!("{key}: [{flags}, \"{public_name}\", \"{declared_name}\"]")); + entries.push(format!( + "{quoted_key}: [{flags}, \"{public_name}\", \"{declared_name}\"]" + )); } } // Unknown format, keep as is _ => { let val = &source[p.value.span().start as usize..p.value.span().end as usize]; - entries.push(format!("{key}: {val}")); + entries.push(format!("{quoted_key}: {val}")); } } } @@ -2424,6 +2439,26 @@ RxFor.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.1 ); } + #[test] + fn test_link_directive_input_array_with_transform() { + let allocator = Allocator::default(); + let code = r#" +import * as i0 from "@angular/core"; +class MyDirective { +} +MyDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "20.0.0", ngImport: i0, type: MyDirective, selector: "[myDir]", inputs: { push: ["cdkConnectedOverlayPush", "push", i0.booleanAttribute] } }); +"#; + let result = link(&allocator, code, "test.mjs"); + assert!(result.linked); + assert!( + result + .code + .contains(r#"push: [2, "cdkConnectedOverlayPush", "push", i0.booleanAttribute]"#), + "Expected transform input array to be converted with InputFlags.HasDecoratorInputTransform. Got: {}", + result.code + ); + } + #[test] fn test_link_component_basic() { let allocator = Allocator::default(); diff --git a/crates/oxc_angular_compiler/src/ng_module/compiler.rs b/crates/oxc_angular_compiler/src/ng_module/compiler.rs index 84ceb1896..eef56a507 100644 --- a/crates/oxc_angular_compiler/src/ng_module/compiler.rs +++ b/crates/oxc_angular_compiler/src/ng_module/compiler.rs @@ -14,7 +14,7 @@ //! ``` use oxc_allocator::{Allocator, Box, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use super::metadata::{R3NgModuleMetadata, R3Reference}; use crate::output::ast::{ @@ -82,7 +82,7 @@ fn build_definition_map<'a>( // type: ModuleClass entries.push(LiteralMapEntry { - key: Atom::from("type"), + key: Ident::from("type"), value: metadata.r#type.value.clone_in(allocator), quoted: false, }); @@ -92,7 +92,7 @@ fn build_definition_map<'a>( let bootstrap_array = create_reference_array(allocator, &metadata.bootstrap, metadata.contains_forward_decls); entries.push(LiteralMapEntry { - key: Atom::from("bootstrap"), + key: Ident::from("bootstrap"), value: bootstrap_array, quoted: false, }); @@ -108,7 +108,7 @@ fn build_definition_map<'a>( metadata.contains_forward_decls, ); entries.push(LiteralMapEntry { - key: Atom::from("declarations"), + key: Ident::from("declarations"), value: declarations_array, quoted: false, }); @@ -122,7 +122,7 @@ fn build_definition_map<'a>( metadata.contains_forward_decls, ); entries.push(LiteralMapEntry { - key: Atom::from("imports"), + key: Ident::from("imports"), value: imports_array, quoted: false, }); @@ -136,7 +136,7 @@ fn build_definition_map<'a>( metadata.contains_forward_decls, ); entries.push(LiteralMapEntry { - key: Atom::from("exports"), + key: Ident::from("exports"), value: exports_array, quoted: false, }); @@ -148,7 +148,7 @@ fn build_definition_map<'a>( let schemas_array = create_reference_array(allocator, &metadata.schemas, metadata.contains_forward_decls); entries.push(LiteralMapEntry { - key: Atom::from("schemas"), + key: Ident::from("schemas"), value: schemas_array, quoted: false, }); @@ -157,7 +157,7 @@ fn build_definition_map<'a>( // id: 'unique-module-id' if let Some(id) = &metadata.id { entries.push(LiteralMapEntry { - key: Atom::from("id"), + key: Ident::from("id"), value: id.clone_in(allocator), quoted: false, }); @@ -211,12 +211,12 @@ fn create_define_ng_module_call<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::DEFINE_NG_MODULE), + name: Ident::from(Identifiers::DEFINE_NG_MODULE), optional: false, source_span: None, }, @@ -277,7 +277,7 @@ fn create_set_scope_side_effect<'a>( metadata.contains_forward_decls, ); scope_entries.push(LiteralMapEntry { - key: Atom::from("declarations"), + key: Ident::from("declarations"), value: decls, quoted: false, }); @@ -287,7 +287,7 @@ fn create_set_scope_side_effect<'a>( let imports = create_reference_array(allocator, &metadata.imports, metadata.contains_forward_decls); scope_entries.push(LiteralMapEntry { - key: Atom::from("imports"), + key: Ident::from("imports"), value: imports, quoted: false, }); @@ -297,7 +297,7 @@ fn create_set_scope_side_effect<'a>( let exports = create_reference_array(allocator, &metadata.exports, metadata.contains_forward_decls); scope_entries.push(LiteralMapEntry { - key: Atom::from("exports"), + key: Ident::from("exports"), value: exports, quoted: false, }); @@ -313,12 +313,12 @@ fn create_set_scope_side_effect<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::SET_NG_MODULE_SCOPE), + name: Ident::from(Identifiers::SET_NG_MODULE_SCOPE), optional: false, source_span: None, }, @@ -342,7 +342,7 @@ fn create_set_scope_side_effect<'a>( // Create: (typeof ngJitMode === "undefined" || ngJitMode) let typeof_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("typeof ngJitMode"), source_span: None }, + ReadVarExpr { name: Ident::from("typeof ngJitMode"), source_span: None }, allocator, )); @@ -353,7 +353,7 @@ fn create_set_scope_side_effect<'a>( rhs: Box::new_in( OutputExpression::Literal(Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from("undefined")), + value: LiteralValue::String(Ident::from("undefined")), source_span: None, }, allocator, @@ -366,7 +366,7 @@ fn create_set_scope_side_effect<'a>( )); let ng_jit_mode = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ngJitMode"), source_span: None }, + ReadVarExpr { name: Ident::from("ngJitMode"), source_span: None }, allocator, )); @@ -431,12 +431,12 @@ fn create_register_ng_module_type<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::REGISTER_NG_MODULE_TYPE), + name: Ident::from(Identifiers::REGISTER_NG_MODULE_TYPE), optional: false, source_span: None, }, @@ -474,7 +474,7 @@ mod tests { fn test_compile_simple_ng_module() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("AppModule"), source_span: None }, + ReadVarExpr { name: Ident::from("AppModule"), source_span: None }, &allocator, )); @@ -496,11 +496,11 @@ mod tests { fn test_compile_ng_module_with_declarations() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("MyModule"), source_span: None }, + ReadVarExpr { name: Ident::from("MyModule"), source_span: None }, &allocator, )); let component_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("MyComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("MyComponent"), source_span: None }, &allocator, )); @@ -524,15 +524,15 @@ mod tests { fn test_compile_ng_module_with_imports_exports() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("SharedModule"), source_span: None }, + ReadVarExpr { name: Ident::from("SharedModule"), source_span: None }, &allocator, )); let import_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("CommonModule"), source_span: None }, + ReadVarExpr { name: Ident::from("CommonModule"), source_span: None }, &allocator, )); let export_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("SharedComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("SharedComponent"), source_span: None }, &allocator, )); @@ -558,11 +558,11 @@ mod tests { fn test_compile_ng_module_with_side_effect_scope() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("JitModule"), source_span: None }, + ReadVarExpr { name: Ident::from("JitModule"), source_span: None }, &allocator, )); let decl_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("JitComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("JitComponent"), source_span: None }, &allocator, )); @@ -583,11 +583,11 @@ mod tests { fn test_compile_ng_module_with_forward_decls() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ForwardModule"), source_span: None }, + ReadVarExpr { name: Ident::from("ForwardModule"), source_span: None }, &allocator, )); let decl_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ForwardComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("ForwardComponent"), source_span: None }, &allocator, )); diff --git a/crates/oxc_angular_compiler/src/ng_module/decorator.rs b/crates/oxc_angular_compiler/src/ng_module/decorator.rs index 0672b7513..63e0fa564 100644 --- a/crates/oxc_angular_compiler/src/ng_module/decorator.rs +++ b/crates/oxc_angular_compiler/src/ng_module/decorator.rs @@ -8,7 +8,7 @@ use oxc_ast::ast::{ Argument, ArrayExpressionElement, Class, ClassElement, Decorator, Expression, MethodDefinitionKind, ObjectPropertyKind, PropertyKey, }; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use crate::factory::R3DependencyMetadata; use crate::output::ast::{OutputExpression, ReadVarExpr}; @@ -21,16 +21,16 @@ use crate::output::oxc_converter::convert_oxc_expression; #[derive(Debug)] pub struct NgModuleMetadata<'a> { /// The name of the NgModule class. - pub class_name: Atom<'a>, + pub class_name: Ident<'a>, /// Span of the class declaration. pub class_span: Span, /// Declared components, directives, and pipes as class names. - pub declarations: Vec<'a, Atom<'a>>, + pub declarations: Vec<'a, Ident<'a>>, /// Imported modules as class names (for ɵmod scope resolution). - pub imports: Vec<'a, Atom<'a>>, + pub imports: Vec<'a, Ident<'a>>, /// Raw imports array expression (for ɵinj provider resolution). /// This preserves call expressions like `StoreModule.forRoot(...)` and spread elements @@ -38,19 +38,19 @@ pub struct NgModuleMetadata<'a> { pub raw_imports_expr: Option>, /// Exported declarations and modules as class names. - pub exports: Vec<'a, Atom<'a>>, + pub exports: Vec<'a, Ident<'a>>, /// Providers expression as OutputExpression. pub providers: Option>, /// Bootstrap components as class names. - pub bootstrap: Vec<'a, Atom<'a>>, + pub bootstrap: Vec<'a, Ident<'a>>, /// Schema identifiers (e.g., "CUSTOM_ELEMENTS_SCHEMA"). - pub schemas: Vec<'a, Atom<'a>>, + pub schemas: Vec<'a, Ident<'a>>, /// Module ID for registration. - pub id: Option>, + pub id: Option>, /// Whether any declarations/imports/exports contain forward references. pub contains_forward_decls: bool, @@ -63,7 +63,7 @@ pub struct NgModuleMetadata<'a> { impl<'a> NgModuleMetadata<'a> { /// Create a new NgModuleMetadata with defaults. - pub fn new(allocator: &'a Allocator, class_name: Atom<'a>, class_span: Span) -> Self { + pub fn new(allocator: &'a Allocator, class_name: Ident<'a>, class_span: Span) -> Self { Self { class_name, class_span, @@ -182,7 +182,7 @@ pub fn extract_ng_module_metadata<'a>( class: &'a Class<'a>, ) -> Option> { // Get the class name - let class_name: Atom<'a> = class.id.as_ref()?.name.clone().into(); + let class_name: Ident<'a> = class.id.as_ref()?.name.clone().into(); let class_span = class.span; // Find the @NgModule decorator @@ -305,20 +305,20 @@ fn is_ng_module_call(callee: &Expression<'_>) -> bool { } /// Get the name of a property key as a string. -fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { +fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { match key { PropertyKey::StaticIdentifier(id) => Some(id.name.clone().into()), - PropertyKey::StringLiteral(lit) => Some(lit.value.clone()), + PropertyKey::StringLiteral(lit) => Some(lit.value.clone().into()), _ => None, } } /// Extract a string value from an expression. -fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { +fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { match expr { - Expression::StringLiteral(lit) => Some(lit.value.clone()), + Expression::StringLiteral(lit) => Some(lit.value.clone().into()), Expression::TemplateLiteral(tpl) if tpl.expressions.is_empty() => { - tpl.quasis.first().and_then(|q| q.value.cooked.clone()) + tpl.quasis.first().and_then(|q| q.value.cooked.clone().map(Into::into)) } _ => None, } @@ -329,7 +329,7 @@ fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { fn extract_reference_array<'a>( allocator: &'a Allocator, expr: &Expression<'a>, -) -> (Vec<'a, Atom<'a>>, bool) { +) -> (Vec<'a, Ident<'a>>, bool) { let mut result = Vec::new_in(allocator); let mut has_forward_refs = false; @@ -377,7 +377,7 @@ fn extract_reference_array<'a>( fn extract_identifier_array<'a>( allocator: &'a Allocator, expr: &Expression<'a>, -) -> Vec<'a, Atom<'a>> { +) -> Vec<'a, Ident<'a>> { let mut result = Vec::new_in(allocator); let Expression::ArrayExpression(arr) = expr else { @@ -444,7 +444,7 @@ fn extract_param_dependency<'a>( let mut skip_self = false; let mut self_ = false; let mut host = false; - let mut attribute_name: Option> = None; + let mut attribute_name: Option> = None; for decorator in ¶m.decorators { if let Some(name) = get_decorator_name(&decorator.expression) { @@ -457,7 +457,7 @@ fn extract_param_dependency<'a>( // @Attribute('attrName') - extract the attribute name if let Expression::CallExpression(call) = &decorator.expression { if let Some(Argument::StringLiteral(s)) = call.arguments.first() { - attribute_name = Some(s.value.clone()); + attribute_name = Some(s.value.clone().into()); } } } @@ -491,7 +491,7 @@ fn extract_param_dependency<'a>( } /// Get the name of a decorator from its expression. -fn get_decorator_name<'a>(expr: &'a Expression<'a>) -> Option> { +fn get_decorator_name<'a>(expr: &'a Expression<'a>) -> Option> { match expr { // @Optional Expression::Identifier(id) => Some(id.name.clone().into()), @@ -519,7 +519,7 @@ fn extract_param_token<'a>( // Handle TSTypeReference: SomeClass, SomeModule, etc. if let oxc_ast::ast::TSType::TSTypeReference(type_ref) = ts_type { // Get the type name - let type_name: Atom<'a> = match &type_ref.type_name { + let type_name: Ident<'a> = match &type_ref.type_name { oxc_ast::ast::TSTypeName::IdentifierReference(id) => id.name.clone().into(), oxc_ast::ast::TSTypeName::QualifiedName(_) | oxc_ast::ast::TSTypeName::ThisExpression(_) => { diff --git a/crates/oxc_angular_compiler/src/ng_module/definition.rs b/crates/oxc_angular_compiler/src/ng_module/definition.rs index 7f98c5e55..2cdc5aaaf 100644 --- a/crates/oxc_angular_compiler/src/ng_module/definition.rs +++ b/crates/oxc_angular_compiler/src/ng_module/definition.rs @@ -335,13 +335,13 @@ mod tests { use crate::ng_module::metadata::{R3NgModuleMetadataBuilder, R3Reference, R3SelectorScopeMode}; use crate::output::ast::ReadVarExpr; use oxc_allocator::Box; - use oxc_span::Atom; + use oxc_span::Ident; #[test] fn test_generate_simple_ng_module_definition() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("AppModule"), source_span: None }, + ReadVarExpr { name: Ident::from("AppModule"), source_span: None }, &allocator, )); @@ -368,11 +368,11 @@ mod tests { fn test_generate_ng_module_with_declarations() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("MyModule"), source_span: None }, + ReadVarExpr { name: Ident::from("MyModule"), source_span: None }, &allocator, )); let component_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("MyComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("MyComponent"), source_span: None }, &allocator, )); @@ -397,15 +397,15 @@ mod tests { fn test_generate_ng_module_with_imports_exports() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("SharedModule"), source_span: None }, + ReadVarExpr { name: Ident::from("SharedModule"), source_span: None }, &allocator, )); let import_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("CommonModule"), source_span: None }, + ReadVarExpr { name: Ident::from("CommonModule"), source_span: None }, &allocator, )); let export_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("SharedComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("SharedComponent"), source_span: None }, &allocator, )); @@ -433,11 +433,11 @@ mod tests { fn test_generate_ng_module_with_bootstrap() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("RootModule"), source_span: None }, + ReadVarExpr { name: Ident::from("RootModule"), source_span: None }, &allocator, )); let bootstrap_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("AppComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("AppComponent"), source_span: None }, &allocator, )); @@ -461,11 +461,11 @@ mod tests { fn test_generate_ng_module_with_side_effect_scope() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("JitModule"), source_span: None }, + ReadVarExpr { name: Ident::from("JitModule"), source_span: None }, &allocator, )); let decl_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("JitComponent"), source_span: None }, + ReadVarExpr { name: Ident::from("JitComponent"), source_span: None }, &allocator, )); @@ -487,7 +487,7 @@ mod tests { fn test_emit_ng_module_definition() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TestModule"), source_span: None }, + ReadVarExpr { name: Ident::from("TestModule"), source_span: None }, &allocator, )); @@ -618,7 +618,7 @@ mod tests { // This enables tree-shaking via the @__PURE__ annotation. let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TreeShakableModule"), source_span: None }, + ReadVarExpr { name: Ident::from("TreeShakableModule"), source_span: None }, &allocator, )); diff --git a/crates/oxc_angular_compiler/src/output/ast.rs b/crates/oxc_angular_compiler/src/output/ast.rs index 5395e54a0..1ac69d59e 100644 --- a/crates/oxc_angular_compiler/src/output/ast.rs +++ b/crates/oxc_angular_compiler/src/output/ast.rs @@ -6,7 +6,7 @@ //! Ported from Angular's `output/output_ast.ts`. use oxc_allocator::{Box, Vec}; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use crate::ir::expression::IrExpression; @@ -293,7 +293,7 @@ pub struct MapType<'a> { #[derive(Debug)] pub struct TransplantedType<'a> { /// String representation of the external type. - pub type_repr: Atom<'a>, + pub type_repr: Ident<'a>, /// Type modifiers. pub modifiers: TypeModifier, } @@ -483,7 +483,7 @@ pub enum LiteralValue<'a> { /// Number literal. Number(f64), /// String literal. - String(Atom<'a>), + String(Ident<'a>), } /// Array literal expression. @@ -499,7 +499,7 @@ pub struct LiteralArrayExpr<'a> { #[derive(Debug)] pub struct LiteralMapEntry<'a> { /// Property key. - pub key: Atom<'a>, + pub key: Ident<'a>, /// Property value. pub value: OutputExpression<'a>, /// Whether the key is quoted. @@ -519,9 +519,9 @@ pub struct LiteralMapExpr<'a> { #[derive(Debug)] pub struct RegularExpressionLiteralExpr<'a> { /// Regex body. - pub body: Atom<'a>, + pub body: Ident<'a>, /// Regex flags. - pub flags: Option>, + pub flags: Option>, /// Source span. pub source_span: Option, } @@ -530,9 +530,9 @@ pub struct RegularExpressionLiteralExpr<'a> { #[derive(Debug)] pub struct TemplateLiteralElement<'a> { /// Cooked text. - pub text: Atom<'a>, + pub text: Ident<'a>, /// Raw text. - pub raw_text: Atom<'a>, + pub raw_text: Ident<'a>, /// Source span. pub source_span: Option, } @@ -563,7 +563,7 @@ pub struct TaggedTemplateLiteralExpr<'a> { #[derive(Debug)] pub struct ReadVarExpr<'a> { /// Variable name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Source span. pub source_span: Option, } @@ -574,7 +574,7 @@ pub struct ReadPropExpr<'a> { /// Receiver expression. pub receiver: Box<'a, OutputExpression<'a>>, /// Property name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Whether this is an optional chain access (`?.`). pub optional: bool, /// Source span. @@ -682,14 +682,14 @@ pub struct CommaExpr<'a> { #[derive(Debug)] pub struct FnParam<'a> { /// Parameter name. - pub name: Atom<'a>, + pub name: Ident<'a>, } /// Function expression. #[derive(Debug)] pub struct FunctionExpr<'a> { /// Function name (optional). - pub name: Option>, + pub name: Option>, /// Parameters. pub params: Vec<'a, FnParam<'a>>, /// Function body. @@ -750,7 +750,7 @@ pub struct DynamicImportExpr<'a> { /// URL to import. pub url: DynamicImportUrl<'a>, /// URL comment for bundlers. - pub url_comment: Option>, + pub url_comment: Option>, /// Source span. pub source_span: Option, } @@ -759,7 +759,7 @@ pub struct DynamicImportExpr<'a> { #[derive(Debug)] pub enum DynamicImportUrl<'a> { /// Static string URL. - String(Atom<'a>), + String(Ident<'a>), /// Dynamic expression URL. Expression(Box<'a, OutputExpression<'a>>), } @@ -768,9 +768,9 @@ pub enum DynamicImportUrl<'a> { #[derive(Debug)] pub struct ExternalReference<'a> { /// Module name. - pub module_name: Option>, + pub module_name: Option>, /// Export name. - pub name: Option>, + pub name: Option>, } /// External expression (reference to external module). @@ -786,15 +786,15 @@ pub struct ExternalExpr<'a> { #[derive(Debug)] pub struct LocalizedStringExpr<'a> { /// Message description. - pub description: Option>, + pub description: Option>, /// Message meaning. - pub meaning: Option>, + pub meaning: Option>, /// Custom message ID. - pub custom_id: Option>, + pub custom_id: Option>, /// Message parts. - pub message_parts: Vec<'a, Atom<'a>>, + pub message_parts: Vec<'a, Ident<'a>>, /// Placeholder names. - pub placeholder_names: Vec<'a, Atom<'a>>, + pub placeholder_names: Vec<'a, Ident<'a>>, /// Interpolated expressions. pub expressions: Vec<'a, OutputExpression<'a>>, /// Source span. @@ -805,7 +805,7 @@ pub struct LocalizedStringExpr<'a> { #[derive(Debug)] pub struct WrappedNodeExpr<'a> { /// Node identifier/reference. - pub node_id: Atom<'a>, + pub node_id: Ident<'a>, /// Source span. pub source_span: Option, } @@ -856,7 +856,7 @@ pub enum OutputStatement<'a> { #[derive(Debug)] pub struct DeclareVarStmt<'a> { /// Variable name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Initial value. pub value: Option>, /// Statement modifiers. @@ -873,18 +873,18 @@ pub enum LeadingComment<'a> { /// JSDoc comment. JSDoc(JsDocComment<'a>), /// Single-line comment. - SingleLine(Atom<'a>), + SingleLine(Ident<'a>), /// Multi-line comment. - MultiLine(Atom<'a>), + MultiLine(Ident<'a>), } /// JSDoc comment for Closure Compiler compatibility. #[derive(Debug, Clone)] pub struct JsDocComment<'a> { /// Description tag (@desc). - pub description: Option>, + pub description: Option>, /// Meaning tag (@meaning). - pub meaning: Option>, + pub meaning: Option>, /// Suppress warnings (@suppress {msgDescriptions}). pub suppress_msg_descriptions: bool, } @@ -893,7 +893,7 @@ pub struct JsDocComment<'a> { #[derive(Debug)] pub struct DeclareFunctionStmt<'a> { /// Function name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Parameters. pub params: Vec<'a, FnParam<'a>>, /// Function body. @@ -1588,7 +1588,7 @@ impl OutputAstBuilder { /// Create a string literal. pub fn string<'a>( allocator: &'a oxc_allocator::Allocator, - value: Atom<'a>, + value: Ident<'a>, ) -> OutputExpression<'a> { OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::String(value), source_span: None }, @@ -1599,7 +1599,7 @@ impl OutputAstBuilder { /// Create a variable read. pub fn variable<'a>( allocator: &'a oxc_allocator::Allocator, - name: Atom<'a>, + name: Ident<'a>, ) -> OutputExpression<'a> { OutputExpression::ReadVar(Box::new_in(ReadVarExpr { name, source_span: None }, allocator)) } diff --git a/crates/oxc_angular_compiler/src/output/emitter.rs b/crates/oxc_angular_compiler/src/output/emitter.rs index 9eb735d4e..350b2ec4b 100644 --- a/crates/oxc_angular_compiler/src/output/emitter.rs +++ b/crates/oxc_angular_compiler/src/output/emitter.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use oxc_diagnostics::OxcDiagnostic; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use super::ast::{ ArrowFunctionBody, BinaryOperator, DeclareVarStmt, DynamicImportUrl, FnParam, LeadingComment, @@ -1276,7 +1276,7 @@ fn push_unicode_escape(buf: &mut String, code: u32) { } /// Escape an identifier for use as a property key. -fn escape_identifier(input: &Atom<'_>, escape_dollar: bool, always_quote: bool) -> String { +fn escape_identifier(input: &Ident<'_>, escape_dollar: bool, always_quote: bool) -> String { // Check if the identifier is a valid JavaScript identifier fn is_legal_identifier(s: &str) -> bool { let mut chars = s.chars(); @@ -1393,7 +1393,7 @@ mod tests { use super::*; use crate::output::ast::{LiteralExpr, LiteralValue, ReadVarExpr}; use oxc_allocator::{Allocator, Box}; - use oxc_span::Atom; + use oxc_span::Ident; #[test] fn test_emit_literal_null() { @@ -1478,7 +1478,7 @@ mod tests { let emitter = JsEmitter::new(); let alloc = Allocator::default(); let expr = OutputExpression::Literal(Box::new_in( - LiteralExpr { value: LiteralValue::String(Atom::from("hello")), source_span: None }, + LiteralExpr { value: LiteralValue::String(Ident::from("hello")), source_span: None }, &alloc, )); // Uses double quotes to match Angular's output style @@ -1490,7 +1490,7 @@ mod tests { let emitter = JsEmitter::new(); let alloc = Allocator::default(); let expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("myVar"), source_span: None }, + ReadVarExpr { name: Ident::from("myVar"), source_span: None }, &alloc, )); assert_eq!(emitter.emit_expression(&expr), "myVar"); @@ -1764,18 +1764,18 @@ mod tests { #[test] fn test_emit_jsdoc_comment() { use super::super::ast::{DeclareVarStmt, JsDocComment, LeadingComment, StmtModifier}; - use oxc_span::Atom; + use oxc_span::Ident; let emitter = JsEmitter::new(); // Create a statement with a JSDoc comment let stmt = DeclareVarStmt { - name: Atom::from("MSG_HELLO"), + name: Ident::from("MSG_HELLO"), value: None, modifiers: StmtModifier::FINAL, leading_comment: Some(LeadingComment::JSDoc(JsDocComment { - description: Some(Atom::from("Hello world")), - meaning: Some(Atom::from("greeting")), + description: Some(Ident::from("Hello world")), + meaning: Some(Ident::from("greeting")), suppress_msg_descriptions: false, })), source_span: None, @@ -1792,13 +1792,13 @@ mod tests { #[test] fn test_emit_jsdoc_with_suppress() { use super::super::ast::{DeclareVarStmt, JsDocComment, LeadingComment, StmtModifier}; - use oxc_span::Atom; + use oxc_span::Ident; let emitter = JsEmitter::new(); // Create a statement with @suppress let stmt = DeclareVarStmt { - name: Atom::from("MSG_HELLO"), + name: Ident::from("MSG_HELLO"), value: None, modifiers: StmtModifier::FINAL, leading_comment: Some(LeadingComment::JSDoc(JsDocComment { @@ -1820,15 +1820,15 @@ mod tests { #[test] fn test_emit_single_line_comment() { use super::super::ast::{DeclareVarStmt, LeadingComment, StmtModifier}; - use oxc_span::Atom; + use oxc_span::Ident; let emitter = JsEmitter::new(); let stmt = DeclareVarStmt { - name: Atom::from("x"), + name: Ident::from("x"), value: None, modifiers: StmtModifier::NONE, - leading_comment: Some(LeadingComment::SingleLine(Atom::from("test comment"))), + leading_comment: Some(LeadingComment::SingleLine(Ident::from("test comment"))), source_span: None, }; @@ -1843,15 +1843,15 @@ mod tests { #[test] fn test_emit_multi_line_comment() { use super::super::ast::{DeclareVarStmt, LeadingComment, StmtModifier}; - use oxc_span::Atom; + use oxc_span::Ident; let emitter = JsEmitter::new(); let stmt = DeclareVarStmt { - name: Atom::from("x"), + name: Ident::from("x"), value: None, modifiers: StmtModifier::NONE, - leading_comment: Some(LeadingComment::MultiLine(Atom::from("multi\nline"))), + leading_comment: Some(LeadingComment::MultiLine(Ident::from("multi\nline"))), source_span: None, }; @@ -1867,16 +1867,16 @@ mod tests { #[test] fn test_emit_multi_line_comment_with_asterisk() { use super::super::ast::{DeclareVarStmt, LeadingComment, StmtModifier}; - use oxc_span::Atom; + use oxc_span::Ident; let emitter = JsEmitter::new(); // Test license-style comment with asterisks let stmt = DeclareVarStmt { - name: Atom::from("x"), + name: Ident::from("x"), value: None, modifiers: StmtModifier::NONE, - leading_comment: Some(LeadingComment::MultiLine(Atom::from( + leading_comment: Some(LeadingComment::MultiLine(Ident::from( "\n* @license\n* Copyright Google LLC\n", ))), source_span: None, @@ -1896,14 +1896,14 @@ mod tests { fn test_emit_conditional_expression() { use super::super::ast::{ConditionalExpr, LiteralExpr, LiteralValue, ReadVarExpr}; use oxc_allocator::{Allocator, Box}; - use oxc_span::Atom; + use oxc_span::Ident; let emitter = JsEmitter::new(); let alloc = Allocator::default(); // Build: (condition? true: false) let condition = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("condition"), source_span: None }, + ReadVarExpr { name: Ident::from("condition"), source_span: None }, &alloc, )); let true_case = OutputExpression::Literal(Box::new_in( @@ -1936,7 +1936,7 @@ mod tests { BinaryOperatorExpr, ConditionalExpr, LiteralExpr, LiteralValue, ReadVarExpr, }; use oxc_allocator::{Allocator, Box}; - use oxc_span::Atom; + use oxc_span::Ident; let emitter = JsEmitter::new(); let alloc = Allocator::default(); @@ -1948,7 +1948,7 @@ mod tests { operator: super::super::ast::BinaryOperator::Identical, lhs: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("tmp"), source_span: None }, + ReadVarExpr { name: Ident::from("tmp"), source_span: None }, &alloc, )), &alloc, @@ -1956,7 +1956,7 @@ mod tests { rhs: Box::new_in( OutputExpression::Literal(Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from("year")), + value: LiteralValue::String(Ident::from("year")), source_span: None, }, &alloc, @@ -1996,7 +1996,7 @@ mod tests { operator: super::super::ast::BinaryOperator::Identical, lhs: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("tmp"), source_span: None }, + ReadVarExpr { name: Ident::from("tmp"), source_span: None }, &alloc, )), &alloc, @@ -2004,7 +2004,7 @@ mod tests { rhs: Box::new_in( OutputExpression::Literal(Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from("month")), + value: LiteralValue::String(Ident::from("month")), source_span: None, }, &alloc, @@ -2046,7 +2046,7 @@ mod tests { // Build: [...arr, 1, 2] let arr_var = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("arr"), source_span: None }, + ReadVarExpr { name: Ident::from("arr"), source_span: None }, &alloc, )); let spread_expr = OutputExpression::SpreadElement(Box::new_in( @@ -2085,11 +2085,11 @@ mod tests { // Build: [...a, ...b] let a_var = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("a"), source_span: None }, + ReadVarExpr { name: Ident::from("a"), source_span: None }, &alloc, )); let b_var = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("b"), source_span: None }, + ReadVarExpr { name: Ident::from("b"), source_span: None }, &alloc, )); let spread_a = OutputExpression::SpreadElement(Box::new_in( @@ -2122,22 +2122,22 @@ mod tests { fn test_emit_nullish_coalescing_with_logical_and_on_left() { use super::super::ast::BinaryOperatorExpr; use oxc_allocator::{Allocator, Box}; - use oxc_span::Atom; + use oxc_span::Ident; let emitter = JsEmitter::new(); let alloc = Allocator::default(); // Build: (a && b) ?? c let a = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("a"), source_span: None }, + ReadVarExpr { name: Ident::from("a"), source_span: None }, &alloc, )); let b = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("b"), source_span: None }, + ReadVarExpr { name: Ident::from("b"), source_span: None }, &alloc, )); let c = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("c"), source_span: None }, + ReadVarExpr { name: Ident::from("c"), source_span: None }, &alloc, )); @@ -2172,22 +2172,22 @@ mod tests { fn test_emit_nullish_coalescing_with_logical_or_on_right() { use super::super::ast::BinaryOperatorExpr; use oxc_allocator::{Allocator, Box}; - use oxc_span::Atom; + use oxc_span::Ident; let emitter = JsEmitter::new(); let alloc = Allocator::default(); // Build: a ?? (b || c) let a = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("a"), source_span: None }, + ReadVarExpr { name: Ident::from("a"), source_span: None }, &alloc, )); let b = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("b"), source_span: None }, + ReadVarExpr { name: Ident::from("b"), source_span: None }, &alloc, )); let c = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("c"), source_span: None }, + ReadVarExpr { name: Ident::from("c"), source_span: None }, &alloc, )); @@ -2222,22 +2222,22 @@ mod tests { fn test_emit_logical_and_with_nullish_coalescing_on_left() { use super::super::ast::BinaryOperatorExpr; use oxc_allocator::{Allocator, Box}; - use oxc_span::Atom; + use oxc_span::Ident; let emitter = JsEmitter::new(); let alloc = Allocator::default(); // Build: (a ?? b) && c let a = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("a"), source_span: None }, + ReadVarExpr { name: Ident::from("a"), source_span: None }, &alloc, )); let b = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("b"), source_span: None }, + ReadVarExpr { name: Ident::from("b"), source_span: None }, &alloc, )); let c = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("c"), source_span: None }, + ReadVarExpr { name: Ident::from("c"), source_span: None }, &alloc, )); @@ -2272,26 +2272,26 @@ mod tests { fn test_emit_nullish_coalescing_with_conditional_on_left() { use super::super::ast::{BinaryOperatorExpr, ConditionalExpr}; use oxc_allocator::{Allocator, Box}; - use oxc_span::Atom; + use oxc_span::Ident; let emitter = JsEmitter::new(); let alloc = Allocator::default(); // Build: (a ? b : c) ?? d let a = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("a"), source_span: None }, + ReadVarExpr { name: Ident::from("a"), source_span: None }, &alloc, )); let b = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("b"), source_span: None }, + ReadVarExpr { name: Ident::from("b"), source_span: None }, &alloc, )); let c = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("c"), source_span: None }, + ReadVarExpr { name: Ident::from("c"), source_span: None }, &alloc, )); let d = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("d"), source_span: None }, + ReadVarExpr { name: Ident::from("d"), source_span: None }, &alloc, )); @@ -2337,7 +2337,7 @@ mod tests { // Build: (x) =>(x + 1) let x_var = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("x"), source_span: None }, + ReadVarExpr { name: Ident::from("x"), source_span: None }, &alloc, )); let one = OutputExpression::Literal(Box::new_in( @@ -2355,7 +2355,7 @@ mod tests { )); let mut params = oxc_allocator::Vec::new_in(&alloc); - params.push(FnParam { name: Atom::from("x") }); + params.push(FnParam { name: Ident::from("x") }); let expr = OutputExpression::ArrowFunction(Box::new_in( ArrowFunctionExpr { @@ -2382,11 +2382,11 @@ mod tests { // Build: (x, y) =>(x + y) let x_var = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("x"), source_span: None }, + ReadVarExpr { name: Ident::from("x"), source_span: None }, &alloc, )); let y_var = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("y"), source_span: None }, + ReadVarExpr { name: Ident::from("y"), source_span: None }, &alloc, )); let body = OutputExpression::BinaryOperator(Box::new_in( @@ -2400,8 +2400,8 @@ mod tests { )); let mut params = oxc_allocator::Vec::new_in(&alloc); - params.push(FnParam { name: Atom::from("x") }); - params.push(FnParam { name: Atom::from("y") }); + params.push(FnParam { name: Ident::from("x") }); + params.push(FnParam { name: Ident::from("y") }); let expr = OutputExpression::ArrowFunction(Box::new_in( ArrowFunctionExpr { @@ -2455,7 +2455,7 @@ mod tests { // Simple $localize with a single message part and no expressions let mut message_parts = oxc_allocator::Vec::new_in(&alloc); - message_parts.push(Atom::from("Hello")); + message_parts.push(Ident::from("Hello")); let placeholder_names = oxc_allocator::Vec::new_in(&alloc); let expressions = oxc_allocator::Vec::new_in(&alloc); @@ -2487,15 +2487,15 @@ mod tests { // $localize with interpolation: "Hello {$name}!" let mut message_parts = oxc_allocator::Vec::new_in(&alloc); - message_parts.push(Atom::from("Hello ")); - message_parts.push(Atom::from("!")); + message_parts.push(Ident::from("Hello ")); + message_parts.push(Ident::from("!")); let mut placeholder_names = oxc_allocator::Vec::new_in(&alloc); - placeholder_names.push(Atom::from("name")); + placeholder_names.push(Ident::from("name")); let mut expressions = oxc_allocator::Vec::new_in(&alloc); expressions.push(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("name"), source_span: None }, + ReadVarExpr { name: Ident::from("name"), source_span: None }, &alloc, ))); @@ -2596,14 +2596,14 @@ mod tests { fn test_emit_empty_declare_function_body() { use super::super::ast::{DeclareFunctionStmt, StmtModifier}; use oxc_allocator::{Allocator, Box}; - use oxc_span::Atom; + use oxc_span::Ident; let emitter = JsEmitter::new(); let alloc = Allocator::default(); let stmt = OutputStatement::DeclareFunction(Box::new_in( DeclareFunctionStmt { - name: Atom::from("foo"), + name: Ident::from("foo"), params: oxc_allocator::Vec::new_in(&alloc), statements: oxc_allocator::Vec::new_in(&alloc), modifiers: StmtModifier::NONE, @@ -2634,7 +2634,7 @@ mod tests { super::super::ast::InvokeFunctionExpr { fn_expr: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("signal"), source_span: None }, + ReadVarExpr { name: Ident::from("signal"), source_span: None }, &alloc, )), &alloc, @@ -2656,7 +2656,7 @@ mod tests { let mut entries = oxc_allocator::Vec::new_in(&alloc); entries.push(LiteralMapEntry { - key: Atom::from("showMenu"), + key: Ident::from("showMenu"), value: signal_call, quoted: false, }); @@ -2699,7 +2699,7 @@ mod tests { super::super::ast::InvokeFunctionExpr { fn_expr: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("signal"), source_span: None }, + ReadVarExpr { name: Ident::from("signal"), source_span: None }, &alloc, )), &alloc, @@ -2721,7 +2721,7 @@ mod tests { let mut entries = oxc_allocator::Vec::new_in(&alloc); entries.push(LiteralMapEntry { - key: Atom::from("showMenu"), + key: Ident::from("showMenu"), value: signal_call, quoted: false, }); diff --git a/crates/oxc_angular_compiler/src/output/oxc_converter.rs b/crates/oxc_angular_compiler/src/output/oxc_converter.rs index 649d73d2f..cc6fd85af 100644 --- a/crates/oxc_angular_compiler/src/output/oxc_converter.rs +++ b/crates/oxc_angular_compiler/src/output/oxc_converter.rs @@ -16,7 +16,7 @@ use oxc_ast::ast::{ Argument, ArrayExpressionElement, BindingPattern, Expression, ObjectPropertyKind, PropertyKey, UnaryOperator as OxcUnaryOperator, }; -use oxc_span::Atom; +use oxc_span::Ident; use super::ast::{ ArrowFunctionBody, ArrowFunctionExpr, BinaryOperator, BinaryOperatorExpr, CommaExpr, @@ -49,7 +49,7 @@ pub fn convert_oxc_expression<'a>( match expr { // Literals Expression::BooleanLiteral(lit) => Some(OutputExpression::Literal(Box::new_in( - LiteralExpr { value: LiteralValue::Boolean(lit.value), source_span: None }, + LiteralExpr { value: LiteralValue::Boolean(lit.value.into()), source_span: None }, allocator, ))), @@ -59,12 +59,15 @@ pub fn convert_oxc_expression<'a>( ))), Expression::NumericLiteral(lit) => Some(OutputExpression::Literal(Box::new_in( - LiteralExpr { value: LiteralValue::Number(lit.value), source_span: None }, + LiteralExpr { value: LiteralValue::Number(lit.value.into()), source_span: None }, allocator, ))), Expression::StringLiteral(lit) => Some(OutputExpression::Literal(Box::new_in( - LiteralExpr { value: LiteralValue::String(lit.value.clone()), source_span: None }, + LiteralExpr { + value: LiteralValue::String(lit.value.clone().into()), + source_span: None, + }, allocator, ))), @@ -203,7 +206,7 @@ pub fn convert_oxc_expression<'a>( // This expression Expression::ThisExpression(_) => Some(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("this"), source_span: None }, + ReadVarExpr { name: Ident::from("this"), source_span: None }, allocator, ))), @@ -288,9 +291,9 @@ fn convert_object_expression<'a>( // Get the property key let (key, quoted) = match &p.key { PropertyKey::StaticIdentifier(id) => (id.name.clone().into(), false), - PropertyKey::StringLiteral(lit) => (lit.value.clone(), true), + PropertyKey::StringLiteral(lit) => (lit.value.clone().into(), true), PropertyKey::NumericLiteral(lit) => { - (Atom::from(allocator.alloc_str(&lit.value.to_string())), true) + (Ident::from(allocator.alloc_str(&lit.value.to_string())), true) } PropertyKey::PrivateIdentifier(_) => return None, // Private fields not supported _ => { @@ -532,7 +535,11 @@ fn convert_template_literal<'a>( .map_or_else(|| quasi.value.raw.clone(), |cooked| cooked.clone()); let raw_text = quasi.value.raw.clone(); - elements.push(TemplateLiteralElement { text, raw_text, source_span: None }); + elements.push(TemplateLiteralElement { + text: text.into(), + raw_text: raw_text.into(), + source_span: None, + }); } // Convert expressions diff --git a/crates/oxc_angular_compiler/src/parser/expression/lexer.rs b/crates/oxc_angular_compiler/src/parser/expression/lexer.rs index 8e24a0298..58a3ce233 100644 --- a/crates/oxc_angular_compiler/src/parser/expression/lexer.rs +++ b/crates/oxc_angular_compiler/src/parser/expression/lexer.rs @@ -5,7 +5,7 @@ //! Ported from Angular's `expression_parser/lexer.ts`. use oxc_allocator::{Allocator, FromIn}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::util::chars; @@ -72,7 +72,7 @@ pub struct Token<'a> { /// The numeric value (for Number tokens). pub num_value: f64, /// The string value (for String/Identifier tokens). - pub str_value: Atom<'a>, + pub str_value: Ident<'a>, /// The string token kind (for String/Template tokens). pub str_kind: StringTokenKind, } @@ -84,7 +84,7 @@ impl<'a> Token<'a> { index: u32, end: u32, num_value: f64, - str_value: Atom<'a>, + str_value: Ident<'a>, str_kind: StringTokenKind, ) -> Self { Self { token_type, index, end, num_value, str_value, str_kind } @@ -97,7 +97,7 @@ impl<'a> Token<'a> { index, end, code as u32 as f64, - Atom::from_in(String::from(code), allocator), + Ident::from_in(String::from(code), allocator), StringTokenKind::Plain, ) } @@ -109,7 +109,7 @@ impl<'a> Token<'a> { index, end, 0.0, - Atom::from_in(text, allocator), + Ident::from_in(text, allocator), StringTokenKind::Plain, ) } @@ -121,7 +121,7 @@ impl<'a> Token<'a> { index, end, 0.0, - Atom::from_in(text, allocator), + Ident::from_in(text, allocator), StringTokenKind::Plain, ) } @@ -133,7 +133,7 @@ impl<'a> Token<'a> { index, end, 0.0, - Atom::from_in(text, allocator), + Ident::from_in(text, allocator), StringTokenKind::Plain, ) } @@ -145,7 +145,7 @@ impl<'a> Token<'a> { index, end, 0.0, - Atom::from_in(text, allocator), + Ident::from_in(text, allocator), StringTokenKind::Plain, ) } @@ -157,7 +157,7 @@ impl<'a> Token<'a> { index, end, 0.0, - Atom::from_in(text, allocator), + Ident::from_in(text, allocator), StringTokenKind::Plain, ) } @@ -169,7 +169,7 @@ impl<'a> Token<'a> { index, end, value, - Atom::from_in("", allocator), + Ident::from_in("", allocator), StringTokenKind::Plain, ) } @@ -181,7 +181,7 @@ impl<'a> Token<'a> { index, end, 0.0, - Atom::from_in(message, allocator), + Ident::from_in(message, allocator), StringTokenKind::Plain, ) } @@ -198,7 +198,7 @@ impl<'a> Token<'a> { index, end, 0.0, - Atom::from_in(text, allocator), + Ident::from_in(text, allocator), StringTokenKind::TemplateLiteralEnd, ) } @@ -210,7 +210,7 @@ impl<'a> Token<'a> { index, end, 0.0, - Atom::from_in(text, allocator), + Ident::from_in(text, allocator), StringTokenKind::TemplateLiteralPart, ) } @@ -222,7 +222,7 @@ impl<'a> Token<'a> { index, end, 0.0, - Atom::from_in(text, allocator), + Ident::from_in(text, allocator), StringTokenKind::TemplateLiteralPart, ) } @@ -234,7 +234,7 @@ impl<'a> Token<'a> { index, end, 0.0, - Atom::from_in(text, allocator), + Ident::from_in(text, allocator), StringTokenKind::TemplateLiteralEnd, ) } @@ -246,7 +246,7 @@ impl<'a> Token<'a> { index, end, 0.0, - Atom::from_in(text, allocator), + Ident::from_in(text, allocator), StringTokenKind::Plain, ) } @@ -258,7 +258,7 @@ impl<'a> Token<'a> { index, end, 0.0, - Atom::from_in(text, allocator), + Ident::from_in(text, allocator), StringTokenKind::Plain, ) } diff --git a/crates/oxc_angular_compiler/src/parser/expression/mod.rs b/crates/oxc_angular_compiler/src/parser/expression/mod.rs index 1cb19d9d4..3aa5f8f84 100644 --- a/crates/oxc_angular_compiler/src/parser/expression/mod.rs +++ b/crates/oxc_angular_compiler/src/parser/expression/mod.rs @@ -223,7 +223,7 @@ impl<'a> BindingParser<'a> { ) -> TemplateBindingParseResult<'a> { let parser = Parser::with_offset(self.allocator, template_value, value_span.start); let key_identifier = TemplateBindingIdentifier { - source: oxc_span::Atom::from(template_key), + source: oxc_span::Ident::from(template_key), span: AbsoluteSourceSpan::new(key_span.start, key_span.end), }; parser.parse_template_bindings(key_identifier) diff --git a/crates/oxc_angular_compiler/src/parser/expression/parser.rs b/crates/oxc_angular_compiler/src/parser/expression/parser.rs index 4fc6fbb6a..f605e88b2 100644 --- a/crates/oxc_angular_compiler/src/parser/expression/parser.rs +++ b/crates/oxc_angular_compiler/src/parser/expression/parser.rs @@ -5,7 +5,7 @@ //! Ported from Angular's `expression_parser/parser.ts`. use oxc_allocator::{Allocator, Box, FromIn, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::ast::expression::{ ASTWithSource, AbsoluteSourceSpan, AngularExpression, ArrowFunction, ArrowFunctionParameter, @@ -314,7 +314,7 @@ impl<'a> Parser<'a> { let source_span = span.to_absolute(absolute_offset); let value = match input { - Some(s) => LiteralValue::String(Atom::from_in(s, self.allocator)), + Some(s) => LiteralValue::String(Ident::from_in(s, self.allocator)), None => LiteralValue::Null, }; @@ -323,8 +323,8 @@ impl<'a> Parser<'a> { ASTWithSource { ast, - source: input.map(|s| Atom::from_in(s, self.allocator)), - location: Atom::from_in(location, self.allocator), + source: input.map(|s| Ident::from_in(s, self.allocator)), + location: Ident::from_in(location, self.allocator), absolute_offset, } } @@ -512,8 +512,8 @@ impl<'a> Parser<'a> { } else { Some(ASTWithSource { ast: value, - source: Some(Atom::from_in(value_source, self.allocator)), - location: Atom::from_in("", self.allocator), + source: Some(Ident::from_in(value_source, self.allocator)), + location: Ident::from_in("", self.allocator), absolute_offset: self.absolute_offset + start, }) }; @@ -597,7 +597,7 @@ impl<'a> Parser<'a> { end: u32, ) -> TemplateBindingIdentifier<'a> { TemplateBindingIdentifier { - source: Atom::from_in(source, self.allocator), + source: Ident::from_in(source, self.allocator), span: AbsoluteSourceSpan::new(start, end), } } @@ -747,8 +747,8 @@ impl<'a> Parser<'a> { let value_source = &self.source[expr_start as usize..value_end as usize]; let ast_with_source = ASTWithSource { ast: value, - source: Some(Atom::from_in(value_source, self.allocator)), - location: Atom::from_in("", self.allocator), + source: Some(Ident::from_in(value_source, self.allocator)), + location: Ident::from_in("", self.allocator), absolute_offset: self.absolute_offset + expr_start, }; @@ -996,7 +996,7 @@ impl<'a> Parser<'a> { { // Add text before the interpolation let text = &self.source[current_pos..abs_start]; - strings.push(Atom::from_in(text, self.allocator)); + strings.push(Ident::from_in(text, self.allocator)); let expr_text = &self.source[expr_start..expr_start + end_idx]; @@ -1016,13 +1016,13 @@ impl<'a> Parser<'a> { // Treat the {{ and everything after as literal text // by adding all remaining text to strings and exiting let text = &self.source[current_pos..]; - strings.push(Atom::from_in(text, self.allocator)); + strings.push(Ident::from_in(text, self.allocator)); break; } } else { // No more interpolations - add remaining text and exit let text = &self.source[current_pos..]; - strings.push(Atom::from_in(text, self.allocator)); + strings.push(Ident::from_in(text, self.allocator)); break; } } @@ -1031,7 +1031,7 @@ impl<'a> Parser<'a> { // all input including the last "}}"), we still need to add the trailing string. // This ensures strings.len() == expressions.len() + 1 for proper interleaving. if !expressions.is_empty() && strings.len() == expressions.len() { - strings.push(Atom::from_in("", self.allocator)); + strings.push(Ident::from_in("", self.allocator)); } if expressions.is_empty() { @@ -1861,7 +1861,7 @@ impl<'a> Parser<'a> { let span = ParseSpan::new(start, end); let source_span = span.to_absolute(self.absolute_offset); let name_span = source_span; - let empty_name = Atom::from_in("", self.allocator); + let empty_name = Ident::from_in("", self.allocator); if safe { let read = SafePropertyRead { span, @@ -1990,7 +1990,7 @@ impl<'a> Parser<'a> { let span = ParseSpan::new(start, end); let source_span = span.to_absolute(self.absolute_offset); let name_span = source_span; // Empty name span at end - let empty_name = Atom::from_in("", self.allocator); + let empty_name = Ident::from_in("", self.allocator); if safe { let read = @@ -2160,7 +2160,7 @@ impl<'a> Parser<'a> { &mut self, receiver: AngularExpression<'a>, start: u32, - name: Atom<'a>, + name: Ident<'a>, name_start: u32, name_end: u32, safe: bool, @@ -2889,7 +2889,7 @@ impl<'a> Parser<'a> { )); self.advance(); let key = LiteralMapKey::Property(LiteralMapPropertyKey { - key: Atom::from_in("", self.allocator), + key: Ident::from_in("", self.allocator), quoted: false, is_shorthand_initialized: false, }); @@ -2944,7 +2944,7 @@ impl<'a> Parser<'a> { self.error("Missing expected identifier, keyword, or string"); let key = LiteralMapKey::Property(LiteralMapPropertyKey { - key: Atom::from_in("", self.allocator), + key: Ident::from_in("", self.allocator), quoted: false, is_shorthand_initialized: false, }); @@ -3014,13 +3014,13 @@ impl<'a> Parser<'a> { // The fullSpanEnd tracks whitespace after the pipe character let full_span_end = self.peek().map(|t| t.index).unwrap_or(self.source.len() as u32); - (Atom::from_in("", self.allocator), full_span_end, Some(full_span_end)) + (Ident::from_in("", self.allocator), full_span_end, Some(full_span_end)) } } else { // End of input - create empty pipe name self.error("Unexpected end of input, expected identifier or keyword"); let end_pos = self.source.len() as u32; - (Atom::from_in("", self.allocator), end_pos, Some(end_pos)) + (Ident::from_in("", self.allocator), end_pos, Some(end_pos)) }; // Parse pipe arguments @@ -3251,7 +3251,7 @@ mod tests { let allocator = Allocator::default(); let parser = Parser::new(&allocator, "condition"); let key = TemplateBindingIdentifier { - source: Atom::from("ngIf"), + source: Ident::from("ngIf"), span: AbsoluteSourceSpan::new(0, 4), }; let result = parser.parse_template_bindings(key); @@ -3275,7 +3275,7 @@ mod tests { let allocator = Allocator::default(); let parser = Parser::new(&allocator, "let item of items"); let key = TemplateBindingIdentifier { - source: Atom::from("ngFor"), + source: Ident::from("ngFor"), span: AbsoluteSourceSpan::new(0, 5), }; let result = parser.parse_template_bindings(key); @@ -3317,7 +3317,7 @@ mod tests { let allocator = Allocator::default(); let parser = Parser::new(&allocator, "let item of items; let i = index"); let key = TemplateBindingIdentifier { - source: Atom::from("ngFor"), + source: Ident::from("ngFor"), span: AbsoluteSourceSpan::new(0, 5), }; let result = parser.parse_template_bindings(key); @@ -3367,7 +3367,7 @@ mod tests { let allocator = Allocator::default(); let parser = Parser::new(&allocator, "let item of items; trackBy: trackByFn"); let key = TemplateBindingIdentifier { - source: Atom::from("ngFor"), + source: Ident::from("ngFor"), span: AbsoluteSourceSpan::new(0, 5), }; let result = parser.parse_template_bindings(key); @@ -3391,7 +3391,7 @@ mod tests { let allocator = Allocator::default(); let parser = Parser::new(&allocator, "obs$ | async as result"); let key = TemplateBindingIdentifier { - source: Atom::from("ngIf"), + source: Ident::from("ngIf"), span: AbsoluteSourceSpan::new(0, 4), }; let result = parser.parse_template_bindings(key); @@ -3437,7 +3437,7 @@ mod tests { let allocator = Allocator::default(); let parser = Parser::new(&allocator, "let item of items; index as i"); let key = TemplateBindingIdentifier { - source: Atom::from("ngFor"), + source: Ident::from("ngFor"), span: AbsoluteSourceSpan::new(0, 5), }; let result = parser.parse_template_bindings(key); diff --git a/crates/oxc_angular_compiler/src/parser/html/parser.rs b/crates/oxc_angular_compiler/src/parser/html/parser.rs index df1fb36f7..3fa2845c9 100644 --- a/crates/oxc_angular_compiler/src/parser/html/parser.rs +++ b/crates/oxc_angular_compiler/src/parser/html/parser.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use oxc_allocator::{Allocator, Box, FromIn, Vec}; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use crate::ast::html::{ BlockType, HtmlAttribute, HtmlBlock, HtmlBlockParameter, HtmlComment, HtmlDirective, @@ -202,7 +202,7 @@ impl<'a> HtmlParser<'a> { &mut self.blocks[idx], HtmlBlock { block_type: BlockType::If, - name: Atom::from(""), + name: Ident::from(""), parameters: Vec::new_in(self.allocator), children: Vec::new_in(self.allocator), span: Span::new(0, 0), @@ -223,7 +223,7 @@ impl<'a> HtmlParser<'a> { let element = std::mem::replace( &mut self.elements[idx], HtmlElement { - name: Atom::from(""), + name: Ident::from(""), component_prefix: None, component_tag_name: None, attrs: Vec::new_in(self.allocator), @@ -299,7 +299,7 @@ impl<'a> HtmlParser<'a> { &mut self.blocks[idx], HtmlBlock { block_type: BlockType::If, - name: Atom::from(""), + name: Ident::from(""), parameters: Vec::new_in(self.allocator), children: Vec::new_in(self.allocator), span: Span::new(0, 0), @@ -352,7 +352,7 @@ impl<'a> HtmlParser<'a> { let element = std::mem::replace( &mut self.elements[idx], HtmlElement { - name: Atom::from(""), + name: Ident::from(""), component_prefix: None, component_tag_name: None, attrs: Vec::new_in(self.allocator), @@ -374,7 +374,7 @@ impl<'a> HtmlParser<'a> { &mut self.blocks[idx], HtmlBlock { block_type: BlockType::If, - name: Atom::from(""), + name: Ident::from(""), parameters: Vec::new_in(self.allocator), children: Vec::new_in(self.allocator), span: Span::new(0, 0), @@ -395,7 +395,7 @@ impl<'a> HtmlParser<'a> { let mut element = std::mem::replace( &mut self.elements[match_elem_idx], HtmlElement { - name: Atom::from(""), + name: Ident::from(""), component_prefix: None, component_tag_name: None, attrs: Vec::new_in(self.allocator), @@ -626,9 +626,9 @@ impl<'a> HtmlParser<'a> { let is_void = is_void_element(&tag_name); let element = HtmlElement { - name: Atom::from_in(tag_name.clone(), self.allocator), - component_prefix: component_prefix.map(|p| Atom::from_in(p, self.allocator)), - component_tag_name: component_tag_name.map(|t| Atom::from_in(t, self.allocator)), + name: Ident::from_in(tag_name.clone(), self.allocator), + component_prefix: component_prefix.map(|p| Ident::from_in(p, self.allocator)), + component_tag_name: component_tag_name.map(|t| Ident::from_in(t, self.allocator)), attrs, directives, children: Vec::new_in(self.allocator), @@ -881,7 +881,7 @@ impl<'a> HtmlParser<'a> { for (token_type, parts, tok_span) in tokens { let mut arena_parts = Vec::new_in(self.allocator); for part in parts { - arena_parts.push(Atom::from_in(part, self.allocator)); + arena_parts.push(Ident::from_in(part, self.allocator)); } arena_tokens.push(InterpolatedToken { token_type, @@ -893,8 +893,8 @@ impl<'a> HtmlParser<'a> { }); let attr = HtmlAttribute { - name: Atom::from_in(name, self.allocator), - value: Atom::from_in(value, self.allocator), + name: Ident::from_in(name, self.allocator), + value: Ident::from_in(value, self.allocator), span, name_span, value_span, @@ -1064,14 +1064,14 @@ impl<'a> HtmlParser<'a> { for (token_type, parts, span) in tokens { let mut arena_parts = Vec::new_in(self.allocator); for part in parts { - arena_parts.push(Atom::from_in(part, self.allocator)); + arena_parts.push(Ident::from_in(part, self.allocator)); } arena_tokens.push(InterpolatedToken { token_type, parts: arena_parts, span }); } let span = self.make_span(start, end); let text_node = HtmlText { - value: Atom::from_in(text, self.allocator), + value: Ident::from_in(text, self.allocator), span, full_start, tokens: arena_tokens, @@ -1107,7 +1107,7 @@ impl<'a> HtmlParser<'a> { // Create element with no end span (incomplete) // Note: is_self_closing is false because this is an incomplete tag, not explicitly self-closing let element = HtmlElement { - name: Atom::from_in(tag_name.clone(), self.allocator), + name: Ident::from_in(tag_name.clone(), self.allocator), component_prefix: None, component_tag_name: None, attrs: Vec::new_in(self.allocator), @@ -1155,7 +1155,7 @@ impl<'a> HtmlParser<'a> { }; let span = self.make_span(start, end); - let comment = HtmlComment { value: Atom::from_in(value, self.allocator), span }; + let comment = HtmlComment { value: Ident::from_in(value, self.allocator), span }; Some(HtmlNode::Comment(Box::new_in(comment, self.allocator))) } @@ -1195,11 +1195,11 @@ impl<'a> HtmlParser<'a> { // CDATA content becomes a text node with a single text token let mut tokens = Vec::new_in(self.allocator); let mut parts = Vec::new_in(self.allocator); - parts.push(Atom::from_in(value.clone(), self.allocator)); + parts.push(Ident::from_in(value.clone(), self.allocator)); tokens.push(InterpolatedToken { token_type: InterpolatedTokenType::Text, parts, span }); // CDATA tokens don't have leading trivia stripped let text = HtmlText { - value: Atom::from_in(value, self.allocator), + value: Ident::from_in(value, self.allocator), span, full_start: None, tokens, @@ -1263,7 +1263,7 @@ impl<'a> HtmlParser<'a> { let parse_result = expr_parser.parse_binding(value_str, value_span); let let_decl = HtmlLetDeclaration { - name: Atom::from_in(name, self.allocator), + name: Ident::from_in(name, self.allocator), value: parse_result.ast, span, name_span, @@ -1338,8 +1338,8 @@ impl<'a> HtmlParser<'a> { let span = self.make_span(start, end); let expansion = HtmlExpansion { - switch_value: Atom::from_in(switch_value, self.allocator), - expansion_type: Atom::from_in(expansion_type, self.allocator), + switch_value: Ident::from_in(switch_value, self.allocator), + expansion_type: Ident::from_in(expansion_type, self.allocator), cases, span, switch_value_span, @@ -1419,7 +1419,7 @@ impl<'a> HtmlParser<'a> { let element = std::mem::replace( &mut self.elements[idx], HtmlElement { - name: Atom::from(""), + name: Ident::from(""), component_prefix: None, component_tag_name: None, attrs: Vec::new_in(self.allocator), @@ -1439,7 +1439,7 @@ impl<'a> HtmlParser<'a> { &mut self.blocks[idx], HtmlBlock { block_type: BlockType::If, - name: Atom::from(""), + name: Ident::from(""), parameters: Vec::new_in(self.allocator), children: Vec::new_in(self.allocator), span: Span::new(0, 0), @@ -1482,7 +1482,7 @@ impl<'a> HtmlParser<'a> { let expansion_span = self.make_span(exp_start, exp_end); Some(HtmlExpansionCase { - value: Atom::from_in(value, self.allocator), + value: Ident::from_in(value, self.allocator), expansion, span, value_span, @@ -1532,7 +1532,7 @@ impl<'a> HtmlParser<'a> { let param_end = param_token.end; let param_span = self.make_span(param_start, param_end); parameters.push(HtmlBlockParameter { - expression: Atom::from_in(¶m_text, self.allocator), + expression: Ident::from_in(¶m_text, self.allocator), span: param_span, }); } else { @@ -1554,7 +1554,7 @@ impl<'a> HtmlParser<'a> { let block = HtmlBlock { block_type, - name: Atom::from_in(name, self.allocator), + name: Ident::from_in(name, self.allocator), parameters, children: Vec::new_in(self.allocator), span, @@ -1661,8 +1661,8 @@ impl<'a> HtmlParser<'a> { let attr_name_span = self.make_span(attr_name_start, attr_name_end); attrs.push(HtmlAttribute { - name: Atom::from_in(attr_name, self.allocator), - value: Atom::from_in(attr_value, self.allocator), + name: Ident::from_in(attr_name, self.allocator), + value: Ident::from_in(attr_value, self.allocator), span: attr_span, name_span: attr_name_span, value_span, @@ -1687,7 +1687,7 @@ impl<'a> HtmlParser<'a> { let span = self.make_span(directive_start, directive_end); Some(HtmlDirective { - name: Atom::from_in(name, self.allocator), + name: Ident::from_in(name, self.allocator), attrs, span, name_span, diff --git a/crates/oxc_angular_compiler/src/parser/html/whitespace.rs b/crates/oxc_angular_compiler/src/parser/html/whitespace.rs index 43621a50b..c9978b686 100644 --- a/crates/oxc_angular_compiler/src/parser/html/whitespace.rs +++ b/crates/oxc_angular_compiler/src/parser/html/whitespace.rs @@ -10,7 +10,7 @@ //! Ported from Angular's `ml_parser/html_whitespaces.ts`. use oxc_allocator::{Allocator, Box, FromIn, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::ast::html::{ HtmlAttribute, HtmlBlock, HtmlBlockParameter, HtmlComment, HtmlComponent, HtmlDirective, @@ -473,7 +473,7 @@ impl<'a> WhitespaceVisitor<'a> { // Process tokens let tokens = self.process_tokens(&text.tokens, context); - let value_atom = Atom::from_in(value.as_str(), self.allocator); + let value_atom = Ident::from_in(value.as_str(), self.allocator); Some(HtmlNode::Text(Box::new_in( HtmlText { value: value_atom, @@ -514,7 +514,7 @@ impl<'a> WhitespaceVisitor<'a> { } let mut parts = Vec::with_capacity_in(1, self.allocator); - parts.push(Atom::from_in(processed.as_str(), self.allocator)); + parts.push(Ident::from_in(processed.as_str(), self.allocator)); result.push(InterpolatedToken { token_type: token.token_type, diff --git a/crates/oxc_angular_compiler/src/pipe/compiler.rs b/crates/oxc_angular_compiler/src/pipe/compiler.rs index 82b545740..e0fc34ba3 100644 --- a/crates/oxc_angular_compiler/src/pipe/compiler.rs +++ b/crates/oxc_angular_compiler/src/pipe/compiler.rs @@ -3,7 +3,7 @@ //! Ported from Angular's `render3/r3_pipe_compiler.ts`. use oxc_allocator::{Allocator, Box, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use super::metadata::R3PipeMetadata; use crate::output::ast::{ @@ -65,7 +65,7 @@ fn build_definition_map<'a>( // name: literal(metadata.pipeName ?? metadata.name) let pipe_name = metadata.pipe_name.clone().unwrap_or_else(|| metadata.name.clone()); entries.push(LiteralMapEntry { - key: Atom::from("name"), + key: Ident::from("name"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::String(pipe_name), source_span: None }, allocator, @@ -75,14 +75,14 @@ fn build_definition_map<'a>( // type: metadata.type.value entries.push(LiteralMapEntry { - key: Atom::from("type"), + key: Ident::from("type"), value: metadata.r#type.clone_in(allocator), quoted: false, }); // pure: literal(metadata.pure) entries.push(LiteralMapEntry { - key: Atom::from("pure"), + key: Ident::from("pure"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::Boolean(metadata.pure), source_span: None }, allocator, @@ -93,7 +93,7 @@ fn build_definition_map<'a>( // standalone: only included if false (Angular's runtime defaults standalone to true) if !metadata.is_standalone { entries.push(LiteralMapEntry { - key: Atom::from("standalone"), + key: Ident::from("standalone"), value: OutputExpression::Literal(Box::new_in( LiteralExpr { value: LiteralValue::Boolean(false), source_span: None }, allocator, @@ -115,12 +115,12 @@ fn create_define_pipe_call<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::DEFINE_PIPE), + name: Ident::from(Identifiers::DEFINE_PIPE), optional: false, source_span: None, }, @@ -157,15 +157,15 @@ mod tests { #[test] fn test_compile_pure_pipe() { let allocator = Allocator::default(); - let name = Atom::from("TestPipe"); + let name = Ident::from("TestPipe"); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TestPipe"), source_span: None }, + ReadVarExpr { name: Ident::from("TestPipe"), source_span: None }, &allocator, )); let metadata = R3PipeMetadata { name: name.clone(), - pipe_name: Some(Atom::from("test")), + pipe_name: Some(Ident::from("test")), r#type: type_expr, type_argument_count: 0, deps: None, @@ -187,9 +187,9 @@ mod tests { #[test] fn test_compile_impure_pipe() { let allocator = Allocator::default(); - let name = Atom::from("ImpurePipe"); + let name = Ident::from("ImpurePipe"); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ImpurePipe"), source_span: None }, + ReadVarExpr { name: Ident::from("ImpurePipe"), source_span: None }, &allocator, )); @@ -214,16 +214,16 @@ mod tests { #[test] fn test_compile_standalone_pipe() { let allocator = Allocator::default(); - let name = Atom::from("StandalonePipe"); + let name = Ident::from("StandalonePipe"); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("StandalonePipe"), source_span: None }, + ReadVarExpr { name: Ident::from("StandalonePipe"), source_span: None }, &allocator, )); // Standalone = true means DON'T include standalone in output (it's the default) let metadata = R3PipeMetadata { name: name.clone(), - pipe_name: Some(Atom::from("standalone")), + pipe_name: Some(Ident::from("standalone")), r#type: type_expr, type_argument_count: 0, deps: None, @@ -242,16 +242,16 @@ mod tests { #[test] fn test_compile_non_standalone_pipe() { let allocator = Allocator::default(); - let name = Atom::from("NonStandalonePipe"); + let name = Ident::from("NonStandalonePipe"); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("NonStandalonePipe"), source_span: None }, + ReadVarExpr { name: Ident::from("NonStandalonePipe"), source_span: None }, &allocator, )); // Non-standalone pipes should include standalone: false let metadata = R3PipeMetadata { name: name.clone(), - pipe_name: Some(Atom::from("legacy")), + pipe_name: Some(Ident::from("legacy")), r#type: type_expr, type_argument_count: 0, deps: None, diff --git a/crates/oxc_angular_compiler/src/pipe/decorator.rs b/crates/oxc_angular_compiler/src/pipe/decorator.rs index 769970cb5..43340a70f 100644 --- a/crates/oxc_angular_compiler/src/pipe/decorator.rs +++ b/crates/oxc_angular_compiler/src/pipe/decorator.rs @@ -8,7 +8,7 @@ use oxc_ast::ast::{ Argument, Class, ClassElement, Decorator, Expression, MethodDefinitionKind, ObjectPropertyKind, PropertyKey, }; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use super::metadata::R3PipeMetadata; use crate::factory::R3DependencyMetadata; @@ -21,13 +21,13 @@ use crate::output::ast::{OutputExpression, ReadVarExpr}; #[derive(Debug)] pub struct PipeMetadata<'a> { /// The name of the pipe class. - pub class_name: Atom<'a>, + pub class_name: Ident<'a>, /// Span of the class declaration. pub class_span: Span, /// The pipe name used in templates (from `@Pipe({name: '...'})`). - pub pipe_name: Option>, + pub pipe_name: Option>, /// Whether the pipe is pure (default: true). /// Pure pipes only transform when inputs change. @@ -46,7 +46,7 @@ impl<'a> PipeMetadata<'a> { /// Create a new PipeMetadata with defaults. pub fn new( _allocator: &'a Allocator, - class_name: Atom<'a>, + class_name: Ident<'a>, class_span: Span, implicit_standalone: bool, ) -> Self { @@ -109,7 +109,7 @@ pub fn extract_pipe_metadata<'a>( implicit_standalone: bool, ) -> Option> { // Get the class name - let class_name: Atom<'a> = class.id.as_ref()?.name.clone().into(); + let class_name: Ident<'a> = class.id.as_ref()?.name.clone().into(); let class_span = class.span; // Find the @Pipe decorator @@ -200,20 +200,20 @@ fn is_pipe_call(callee: &Expression<'_>) -> bool { } /// Get the name of a property key as a string. -fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { +fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option> { match key { PropertyKey::StaticIdentifier(id) => Some(id.name.clone().into()), - PropertyKey::StringLiteral(lit) => Some(lit.value.clone()), + PropertyKey::StringLiteral(lit) => Some(lit.value.clone().into()), _ => None, } } /// Extract a string value from an expression. -fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { +fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { match expr { - Expression::StringLiteral(lit) => Some(lit.value.clone()), + Expression::StringLiteral(lit) => Some(lit.value.clone().into()), Expression::TemplateLiteral(tpl) if tpl.expressions.is_empty() => { - tpl.quasis.first().and_then(|q| q.value.cooked.clone()) + tpl.quasis.first().and_then(|q| q.value.cooked.clone().map(Into::into)) } _ => None, } @@ -222,7 +222,7 @@ fn extract_string_value<'a>(expr: &Expression<'a>) -> Option> { /// Extract a boolean value from an expression. fn extract_boolean_value(expr: &Expression<'_>) -> Option { match expr { - Expression::BooleanLiteral(lit) => Some(lit.value), + Expression::BooleanLiteral(lit) => Some(lit.value.into()), _ => None, } } @@ -267,7 +267,7 @@ fn extract_param_dependency<'a>( let mut skip_self = false; let mut self_ = false; let mut host = false; - let mut attribute_name: Option> = None; + let mut attribute_name: Option> = None; for decorator in ¶m.decorators { if let Some(name) = get_decorator_name(&decorator.expression) { @@ -280,7 +280,7 @@ fn extract_param_dependency<'a>( // @Attribute('attrName') - extract the attribute name if let Expression::CallExpression(call) = &decorator.expression { if let Some(Argument::StringLiteral(s)) = call.arguments.first() { - attribute_name = Some(s.value.clone()); + attribute_name = Some(s.value.clone().into()); } } } @@ -314,7 +314,7 @@ fn extract_param_dependency<'a>( } /// Get the name of a decorator from its expression. -fn get_decorator_name<'a>(expr: &'a Expression<'a>) -> Option> { +fn get_decorator_name<'a>(expr: &'a Expression<'a>) -> Option> { match expr { // @Optional Expression::Identifier(id) => Some(id.name.clone().into()), @@ -342,7 +342,7 @@ fn extract_param_token<'a>( // Handle TSTypeReference: SomeClass, SomeModule, etc. if let oxc_ast::ast::TSType::TSTypeReference(type_ref) = ts_type { // Get the type name - let type_name: Atom<'a> = match &type_ref.type_name { + let type_name: Ident<'a> = match &type_ref.type_name { oxc_ast::ast::TSTypeName::IdentifierReference(id) => id.name.clone().into(), oxc_ast::ast::TSTypeName::QualifiedName(_) | oxc_ast::ast::TSTypeName::ThisExpression(_) => { diff --git a/crates/oxc_angular_compiler/src/pipe/definition.rs b/crates/oxc_angular_compiler/src/pipe/definition.rs index bd7474398..dbf18d984 100644 --- a/crates/oxc_angular_compiler/src/pipe/definition.rs +++ b/crates/oxc_angular_compiler/src/pipe/definition.rs @@ -209,18 +209,18 @@ mod tests { use crate::output::emitter::JsEmitter; use crate::pipe::metadata::R3PipeMetadataBuilder; use oxc_allocator::Box; - use oxc_span::Atom; + use oxc_span::Ident; #[test] fn test_generate_pure_pipe_definition() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("MyPipe"), source_span: None }, + ReadVarExpr { name: Ident::from("MyPipe"), source_span: None }, &allocator, )); - let metadata = R3PipeMetadataBuilder::new(Atom::from("MyPipe"), type_expr) - .pipe_name(Atom::from("myPipe")) + let metadata = R3PipeMetadataBuilder::new(Ident::from("MyPipe"), type_expr) + .pipe_name(Ident::from("myPipe")) .pure(true) .is_standalone(true) .build(); @@ -248,12 +248,12 @@ mod tests { fn test_generate_impure_pipe_definition() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ImpurePipe"), source_span: None }, + ReadVarExpr { name: Ident::from("ImpurePipe"), source_span: None }, &allocator, )); - let metadata = R3PipeMetadataBuilder::new(Atom::from("ImpurePipe"), type_expr) - .pipe_name(Atom::from("impure")) + let metadata = R3PipeMetadataBuilder::new(Ident::from("ImpurePipe"), type_expr) + .pipe_name(Ident::from("impure")) .pure(false) .is_standalone(true) .build(); @@ -272,12 +272,12 @@ mod tests { fn test_generate_non_standalone_pipe_definition() { let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("LegacyPipe"), source_span: None }, + ReadVarExpr { name: Ident::from("LegacyPipe"), source_span: None }, &allocator, )); - let metadata = R3PipeMetadataBuilder::new(Atom::from("LegacyPipe"), type_expr) - .pipe_name(Atom::from("legacy")) + let metadata = R3PipeMetadataBuilder::new(Ident::from("LegacyPipe"), type_expr) + .pipe_name(Ident::from("legacy")) .pure(true) .is_standalone(false) .build(); @@ -298,12 +298,12 @@ mod tests { // This enables tree-shaking via the @__PURE__ annotation. let allocator = Allocator::default(); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TreeShakablePipe"), source_span: None }, + ReadVarExpr { name: Ident::from("TreeShakablePipe"), source_span: None }, &allocator, )); - let metadata = R3PipeMetadataBuilder::new(Atom::from("TreeShakablePipe"), type_expr) - .pipe_name(Atom::from("treeshakable")) + let metadata = R3PipeMetadataBuilder::new(Ident::from("TreeShakablePipe"), type_expr) + .pipe_name(Ident::from("treeshakable")) .pure(true) .is_standalone(true) .build(); diff --git a/crates/oxc_angular_compiler/src/pipe/metadata.rs b/crates/oxc_angular_compiler/src/pipe/metadata.rs index c36d0b2fb..641997b61 100644 --- a/crates/oxc_angular_compiler/src/pipe/metadata.rs +++ b/crates/oxc_angular_compiler/src/pipe/metadata.rs @@ -3,7 +3,7 @@ //! Ported from Angular's `render3/r3_pipe_compiler.ts`. use oxc_allocator::{Box, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::output::ast::OutputExpression; @@ -13,11 +13,11 @@ use crate::output::ast::OutputExpression; #[derive(Debug)] pub struct R3PipeMetadata<'a> { /// The TypeScript class name of the pipe. - pub name: Atom<'a>, + pub name: Ident<'a>, /// The actual pipe name used in templates (from `@Pipe({name: '...'})`). /// If None, the class name is used. - pub pipe_name: Option>, + pub pipe_name: Option>, /// Reference to the pipe class itself. pub r#type: OutputExpression<'a>, @@ -91,8 +91,8 @@ impl<'a> R3DependencyMetadata<'a> { /// Builder for creating pipe metadata. pub struct R3PipeMetadataBuilder<'a> { - name: Atom<'a>, - pipe_name: Option>, + name: Ident<'a>, + pipe_name: Option>, r#type: OutputExpression<'a>, type_argument_count: u32, deps: Option>>, @@ -102,7 +102,7 @@ pub struct R3PipeMetadataBuilder<'a> { impl<'a> R3PipeMetadataBuilder<'a> { /// Creates a new builder with required fields. - pub fn new(name: Atom<'a>, r#type: OutputExpression<'a>) -> Self { + pub fn new(name: Ident<'a>, r#type: OutputExpression<'a>) -> Self { Self { name, pipe_name: None, @@ -115,7 +115,7 @@ impl<'a> R3PipeMetadataBuilder<'a> { } /// Sets the pipe name (as used in templates). - pub fn pipe_name(mut self, name: Atom<'a>) -> Self { + pub fn pipe_name(mut self, name: Ident<'a>) -> Self { self.pipe_name = Some(name); self } @@ -167,14 +167,14 @@ mod tests { #[test] fn test_pipe_metadata_builder() { let allocator = Allocator::default(); - let name = Atom::from("TestPipe"); + let name = Ident::from("TestPipe"); let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("TestPipe"), source_span: None }, + ReadVarExpr { name: Ident::from("TestPipe"), source_span: None }, &allocator, )); let metadata = R3PipeMetadataBuilder::new(name.clone(), type_expr) - .pipe_name(Atom::from("test")) + .pipe_name(Ident::from("test")) .pure(true) .is_standalone(true) .build(); @@ -191,7 +191,7 @@ mod tests { let token = Box::new_in( OutputExpression::Literal(Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from("MyService")), + value: LiteralValue::String(Ident::from("MyService")), source_span: None, }, &allocator, diff --git a/crates/oxc_angular_compiler/src/pipeline/compilation.rs b/crates/oxc_angular_compiler/src/pipeline/compilation.rs index 99b3e3354..ae8113bbe 100644 --- a/crates/oxc_angular_compiler/src/pipeline/compilation.rs +++ b/crates/oxc_angular_compiler/src/pipeline/compilation.rs @@ -9,7 +9,7 @@ use indexmap::IndexMap; use oxc_allocator::{Allocator, Box, Vec}; use oxc_diagnostics::OxcDiagnostic; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use rustc_hash::{FxBuildHasher, FxHashMap}; use crate::AngularVersion; @@ -86,18 +86,18 @@ pub enum DeferMetadata<'a> { #[derive(Debug)] pub struct I18nMessageMetadata<'a> { /// Message ID (computed). - pub message_id: Option>, + pub message_id: Option>, /// Custom ID supplied by the author. - pub custom_id: Option>, + pub custom_id: Option>, /// Message meaning for disambiguation. - pub meaning: Option>, + pub meaning: Option>, /// Message description for translators. - pub description: Option>, + pub description: Option>, /// Legacy message IDs. - pub legacy_ids: Vec<'a, Atom<'a>>, + pub legacy_ids: Vec<'a, Ident<'a>>, /// The serialized message string for goog.getMsg and $localize. /// Contains the message text with placeholder markers like "{$interpolation}". - pub message_string: Option>, + pub message_string: Option>, } /// A complete compilation job for a single component template. @@ -108,7 +108,7 @@ pub struct ComponentCompilationJob<'a> { /// The allocator for this compilation. pub allocator: &'a Allocator, /// Name of the component being compiled. - pub component_name: Atom<'a>, + pub component_name: Ident<'a>, /// Constant pool for deduplication. pub pool: ConstantPool<'a>, /// Expression store for managing expressions by reference. @@ -156,7 +156,7 @@ pub struct ComponentCompilationJob<'a> { /// /// Used to generate unique, file-based variable names for i18n translations. /// The path is sanitized to create a valid identifier suffix. - pub relative_context_file_path: Option>, + pub relative_context_file_path: Option>, /// Relocation entries. pub relocation_entries: Vec<'a, RelocationEntry>, /// Whether to attach debug source locations. @@ -165,7 +165,7 @@ pub struct ComponentCompilationJob<'a> { pub enable_debug_locations: bool, /// Relative path to the template file for source location debugging. /// Required when `enable_debug_locations` is true. - pub relative_template_path: Option>, + pub relative_template_path: Option>, /// Template source text for computing line/column from byte offsets. /// Required when `enable_debug_locations` is true. pub template_source: Option<&'a str>, @@ -196,7 +196,7 @@ pub struct ComponentCompilationJob<'a> { impl<'a> ComponentCompilationJob<'a> { /// Creates a new compilation job. - pub fn new(allocator: &'a Allocator, component_name: Atom<'a>) -> Self { + pub fn new(allocator: &'a Allocator, component_name: Ident<'a>) -> Self { Self::with_pool_starting_index(allocator, component_name, 0) } @@ -210,7 +210,7 @@ impl<'a> ComponentCompilationJob<'a> { /// be created with `pool_starting_index: 3` to start with _c3. pub fn with_pool_starting_index( allocator: &'a Allocator, - component_name: Atom<'a>, + component_name: Ident<'a>, pool_starting_index: u32, ) -> Self { let root_xref = XrefId::new(0); @@ -408,7 +408,7 @@ pub struct ViewCompilationUnit<'a> { /// Number of variable slots needed. pub vars: Option, /// Generated function name. - pub fn_name: Option>, + pub fn_name: Option>, /// Declaration count for template compatibility. pub decl_count: Option, /// First child element/template xref. @@ -464,10 +464,10 @@ pub const CTX_REF: &str = "CTX_REF_MARKER"; #[derive(Debug)] pub struct ContextVariable<'a> { /// Variable name (the user-defined identifier). - pub name: Atom<'a>, + pub name: Ident<'a>, /// Variable value (the context property name, e.g., "$implicit"). /// If this equals `CTX_REF`, the variable represents the entire context object. - pub value: Atom<'a>, + pub value: Ident<'a>, /// Cross-reference to the variable's origin. pub xref: XrefId, } @@ -481,7 +481,7 @@ pub struct ContextVariable<'a> { #[derive(Debug)] pub struct AliasVariable<'a> { /// The user-visible identifier for this alias. - pub identifier: Atom<'a>, + pub identifier: Ident<'a>, /// The expression that computes this alias's value. /// This expression is cloned and inlined at every usage site. pub expression: crate::ir::expression::IrExpression<'a>, @@ -491,7 +491,7 @@ pub struct AliasVariable<'a> { #[derive(Debug)] pub enum ConstValue<'a> { /// A string constant. - String(Atom<'a>), + String(Ident<'a>), /// An array of constants (attribute arrays). Array(Vec<'a, ConstValue<'a>>), /// A number constant. @@ -535,9 +535,9 @@ impl<'a> ConstValue<'a> { #[derive(Debug)] pub struct ExternalRef<'a> { /// Module name (e.g., "@angular/core"). - pub module_name: Atom<'a>, + pub module_name: Ident<'a>, /// Export name. - pub name: Atom<'a>, + pub name: Ident<'a>, } /// A relocation entry for defer blocks. @@ -566,7 +566,7 @@ pub struct CompilationResult<'a> { /// A compiled template function. pub struct TemplateFn<'a> { /// Function name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Number of creation slots. pub creation_slots: u32, /// Number of variable slots. @@ -599,9 +599,9 @@ pub struct HostBindingCompilationJob<'a> { /// The allocator for this compilation. pub allocator: &'a Allocator, /// Name of the component/directive being compiled. - pub component_name: Atom<'a>, + pub component_name: Ident<'a>, /// The CSS selector for the component/directive. - pub component_selector: Atom<'a>, + pub component_selector: Ident<'a>, /// Constant pool for deduplication. pub pool: ConstantPool<'a>, /// Expression store for managing expressions by reference. @@ -615,7 +615,7 @@ pub struct HostBindingCompilationJob<'a> { /// Template compilation mode (always DomOnly for host bindings). pub mode: TemplateCompilationMode, /// Function name suffix. - pub fn_suffix: Atom<'a>, + pub fn_suffix: Ident<'a>, /// Diagnostics collected during compilation. pub diagnostics: std::vec::Vec, /// Angular version for version-gated instruction emission. @@ -626,8 +626,8 @@ impl<'a> HostBindingCompilationJob<'a> { /// Creates a new host binding compilation job. pub fn new( allocator: &'a Allocator, - component_name: Atom<'a>, - component_selector: Atom<'a>, + component_name: Ident<'a>, + component_selector: Ident<'a>, ) -> Self { Self::with_pool_starting_index(allocator, component_name, component_selector, 0) } @@ -646,8 +646,8 @@ impl<'a> HostBindingCompilationJob<'a> { /// compilation share the same ConstantPool instance. pub fn with_pool_starting_index( allocator: &'a Allocator, - component_name: Atom<'a>, - component_selector: Atom<'a>, + component_name: Ident<'a>, + component_selector: Ident<'a>, pool_starting_index: u32, ) -> Self { let root_xref = XrefId::new(0); @@ -663,7 +663,7 @@ impl<'a> HostBindingCompilationJob<'a> { next_xref_id: 1, // 0 is reserved for root compatibility_mode: CompatibilityMode::TemplateDefinitionBuilder, mode: TemplateCompilationMode::DomOnly, // Host bindings always use DomOnly - fn_suffix: Atom::from("HostBindings"), + fn_suffix: Ident::from("HostBindings"), diagnostics: std::vec::Vec::new(), angular_version: None, } @@ -731,7 +731,7 @@ pub struct HostBindingCompilationUnit<'a> { /// Number of variable slots needed. pub vars: Option, /// Generated function name. - pub fn_name: Option>, + pub fn_name: Option>, } impl<'a> HostBindingCompilationUnit<'a> { @@ -760,14 +760,14 @@ pub enum FnStatement<'a> { /// A runtime instruction call. Instruction(Instruction<'a>), /// A variable declaration. - VarDecl(Atom<'a>), + VarDecl(Ident<'a>), } /// A runtime instruction call. #[derive(Debug)] pub struct Instruction<'a> { /// Instruction name (e.g., "ɵɵelement"). - pub name: Atom<'a>, + pub name: Ident<'a>, /// Arguments to the instruction. pub args: Vec<'a, InstructionArg<'a>>, } @@ -782,5 +782,5 @@ pub enum InstructionArg<'a> { /// A slot reference. Slot(u32), /// An expression string. - Expression(Atom<'a>), + Expression(Ident<'a>), } diff --git a/crates/oxc_angular_compiler/src/pipeline/constant_pool.rs b/crates/oxc_angular_compiler/src/pipeline/constant_pool.rs index c6cb2ed2b..a44b43e36 100644 --- a/crates/oxc_angular_compiler/src/pipeline/constant_pool.rs +++ b/crates/oxc_angular_compiler/src/pipeline/constant_pool.rs @@ -6,7 +6,7 @@ //! Ported from Angular's `constant_pool.ts`. use oxc_allocator::{Allocator, Box, Vec}; -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; use crate::ir::expression::IrExpression; @@ -108,7 +108,7 @@ impl<'a> ConstantPool<'a> { let index = self.values.len() as u32; let name = self.generate_name("_c"); - let atom = Atom::from(self.allocator.alloc_str(value)); + let atom = Ident::from(self.allocator.alloc_str(value)); self.values.push(PooledConstant { name: name.clone(), @@ -168,10 +168,10 @@ impl<'a> ConstantPool<'a> { } /// Generates a unique name for a pooled constant. - fn generate_name(&mut self, prefix: &str) -> Atom<'a> { + fn generate_name(&mut self, prefix: &str) -> Ident<'a> { let name = format!("{}{}", prefix, self.next_name_index); self.next_name_index += 1; - Atom::from(self.allocator.alloc_str(&name)) + Ident::from(self.allocator.alloc_str(&name)) } /// Gets the constant at the given index. @@ -193,8 +193,8 @@ impl<'a> ConstantPool<'a> { let index = self.values.len() as u32; let name = self.generate_name("_c"); - let body_atom = Atom::from(self.allocator.alloc_str(body)); - let flags_atom = flags.map(|f| Atom::from(self.allocator.alloc_str(f))); + let body_atom = Ident::from(self.allocator.alloc_str(body)); + let flags_atom = flags.map(|f| Ident::from(self.allocator.alloc_str(f))); self.values.push(PooledConstant { name: name.clone(), @@ -230,7 +230,7 @@ impl<'a> ConstantPool<'a> { num_args: u32, body_key: &str, body_expr: IrExpression<'a>, - ) -> (u32, Atom<'a>) { + ) -> (u32, Ident<'a>) { // Create a key for deduplication based on arg count and body let key = format!("pf:{}:{}", num_args, body_key); @@ -247,7 +247,7 @@ impl<'a> ConstantPool<'a> { // Generate parameter names: a0, a1, a2, ... let mut params = Vec::with_capacity_in(num_args as usize, self.allocator); for i in 0..num_args { - params.push(Atom::from(self.allocator.alloc_str(&format!("a{}", i)))); + params.push(Ident::from(self.allocator.alloc_str(&format!("a{}", i)))); } // Store the actual body expression @@ -404,7 +404,7 @@ impl<'a> ConstantPool<'a> { /// * `base` - The base name to make unique /// * `always_include_suffix` - If `true`, always append a numeric suffix. /// If `false`, only append suffix if the name has been used before. - pub fn unique_name(&mut self, base: &str, always_include_suffix: bool) -> Atom<'a> { + pub fn unique_name(&mut self, base: &str, always_include_suffix: bool) -> Ident<'a> { // Get the per-base-name count for deduplication within this component let count = self.claimed_names.get(base).copied().unwrap_or(0); @@ -424,7 +424,7 @@ impl<'a> ConstantPool<'a> { // Increment the per-base-name counter self.claimed_names.insert(base.to_string(), count + 1); - Atom::from(self.allocator.alloc_str(&name)) + Ident::from(self.allocator.alloc_str(&name)) } /// Gets or creates a constant literal and returns a reference expression. @@ -513,7 +513,7 @@ pub struct PooledValue { #[derive(Debug)] pub struct PooledConstant<'a> { /// Generated variable name. - pub name: Atom<'a>, + pub name: Ident<'a>, /// Value reference. pub value: PooledValue, /// The constant's kind and value. @@ -524,7 +524,7 @@ pub struct PooledConstant<'a> { #[derive(Debug)] pub enum PooledConstantKind<'a> { /// A string literal. - String(Atom<'a>), + String(Ident<'a>), /// A number literal. Number(f64), /// A boolean literal. @@ -534,7 +534,7 @@ pub enum PooledConstantKind<'a> { /// A placeholder for an array (content not yet known). ArrayPlaceholder, /// An object literal. - Object(Vec<'a, (Atom<'a>, PooledConstantKind<'a>)>), + Object(Vec<'a, (Ident<'a>, PooledConstantKind<'a>)>), /// An external reference. External(ExternalReference<'a>), /// A pure function wrapper. @@ -550,18 +550,18 @@ pub enum PooledConstantKind<'a> { #[derive(Debug)] pub struct RegexConstant<'a> { /// The regex pattern body. - pub body: Atom<'a>, + pub body: Ident<'a>, /// The regex flags. - pub flags: Option>, + pub flags: Option>, } /// An external reference (import). #[derive(Debug)] pub struct ExternalReference<'a> { /// Module to import from. - pub module: Atom<'a>, + pub module: Ident<'a>, /// Name to import. - pub name: Atom<'a>, + pub name: Ident<'a>, } /// A pure function definition. @@ -574,7 +574,7 @@ pub struct ExternalReference<'a> { #[derive(Debug)] pub struct PureFunctionDef<'a> { /// Parameter names (a0, a1, a2, ...). - pub params: Vec<'a, Atom<'a>>, + pub params: Vec<'a, Ident<'a>>, /// Body expression - the actual IR expression to emit. /// /// This expression may contain `PureFunctionParameterExpr` nodes that diff --git a/crates/oxc_angular_compiler/src/pipeline/conversion.rs b/crates/oxc_angular_compiler/src/pipeline/conversion.rs index 917573944..f8f66175c 100644 --- a/crates/oxc_angular_compiler/src/pipeline/conversion.rs +++ b/crates/oxc_angular_compiler/src/pipeline/conversion.rs @@ -7,7 +7,7 @@ //! and `template/pipeline/src/conversion.ts`. use oxc_allocator::{Allocator, Box, Vec}; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use crate::ast::expression::{ AbsoluteSourceSpan, AngularExpression, BinaryOperator as AstBinaryOperator, LiteralMapKey, @@ -199,10 +199,10 @@ pub fn convert_unary_operator(op: AstUnaryOperator) -> OutputUnaryOperator { /// - `${` to prevent interpolation syntax /// - Backslashes to preserve escape sequences /// - Carriage returns and line feeds to their escape sequences -fn cooked_to_raw_text<'a>(allocator: &'a Allocator, cooked: &str) -> Atom<'a> { +fn cooked_to_raw_text<'a>(allocator: &'a Allocator, cooked: &str) -> Ident<'a> { // Fast path: if no escaping needed, return as-is if !cooked.contains(['`', '$', '\\', '\r', '\n']) { - return Atom::from(allocator.alloc_str(cooked)); + return Ident::from(allocator.alloc_str(cooked)); } // Escape special characters @@ -225,7 +225,7 @@ fn cooked_to_raw_text<'a>(allocator: &'a Allocator, cooked: &str) -> Atom<'a> { _ => raw.push(c), } } - Atom::from(allocator.alloc_str(&raw)) + Ident::from(allocator.alloc_str(&raw)) } // ============================================================================ diff --git a/crates/oxc_angular_compiler/src/pipeline/emit.rs b/crates/oxc_angular_compiler/src/pipeline/emit.rs index 0a15550be..cf1f22b7c 100644 --- a/crates/oxc_angular_compiler/src/pipeline/emit.rs +++ b/crates/oxc_angular_compiler/src/pipeline/emit.rs @@ -15,7 +15,7 @@ use crate::output::ast::{ }; use crate::r3::{Identifiers, get_interpolate_instruction, get_pipe_bind_instruction}; use oxc_allocator::Box; -use oxc_span::Atom; +use oxc_span::Ident; use crate::ir::ops::XrefId; @@ -138,8 +138,8 @@ fn emit_view<'a>(allocator: &'a Allocator, view: &ViewCompilationUnit<'a>) -> Fu // - rf: Render flags (1 = create, 2 = update) // - ctx: Component context (this) let mut params: OxcVec<'a, FnParam<'a>> = OxcVec::new_in(allocator); - params.push(FnParam { name: Atom::from("rf") }); - params.push(FnParam { name: Atom::from("ctx") }); + params.push(FnParam { name: Ident::from("rf") }); + params.push(FnParam { name: Ident::from("ctx") }); FunctionExpr { name: fn_name, params, statements: body, source_span: None } } @@ -186,7 +186,7 @@ fn maybe_generate_rf_block<'a>( operator: BinaryOperator::BitwiseAnd, lhs: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("rf"), source_span: None }, + ReadVarExpr { name: Ident::from("rf"), source_span: None }, allocator, )), allocator, @@ -250,8 +250,8 @@ pub fn emit_host_binding_function<'a>( // Create function parameters: (rf, ctx) let mut params: OxcVec<'a, FnParam<'a>> = OxcVec::new_in(allocator); - params.push(FnParam { name: Atom::from("rf") }); - params.push(FnParam { name: Atom::from("ctx") }); + params.push(FnParam { name: Ident::from("rf") }); + params.push(FnParam { name: Ident::from("ctx") }); Some(FunctionExpr { name: unit.fn_name.clone(), params, statements: body, source_span: None }) } @@ -369,7 +369,7 @@ pub fn emit_additional_pool_constants<'a>( fn convert_pure_function_body<'a>( allocator: &'a Allocator, expr: &IrExpression<'a>, - params: &[Atom<'a>], + params: &[Ident<'a>], ) -> OutputExpression<'a> { use crate::ir::expression::*; use crate::output::ast::*; @@ -381,7 +381,7 @@ fn convert_pure_function_body<'a>( params[pfp.index as usize].clone() } else { // Fallback if index out of range - Atom::from(allocator.alloc_str(&format!("a{}", pfp.index))) + Ident::from(allocator.alloc_str(&format!("a{}", pfp.index))) }; OutputExpression::ReadVar(Box::new_in( ReadVarExpr { name: param_name, source_span: None }, @@ -394,7 +394,7 @@ fn convert_pure_function_body<'a>( if lexical.name.as_str() == "ctx" { // Direct reference to context parameter - emit as just `ctx` OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ctx"), source_span: None }, + ReadVarExpr { name: Ident::from("ctx"), source_span: None }, allocator, )) } else { @@ -403,7 +403,7 @@ fn convert_pure_function_body<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ctx"), source_span: None }, + ReadVarExpr { name: Ident::from("ctx"), source_span: None }, allocator, )), allocator, @@ -419,13 +419,13 @@ fn convert_pure_function_body<'a>( // Context reference becomes ctx IrExpression::Context(_) => OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ctx"), source_span: None }, + ReadVarExpr { name: Ident::from("ctx"), source_span: None }, allocator, )), // TrackContext reference becomes this (for track functions) IrExpression::TrackContext(_) => OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("this"), source_span: None }, + ReadVarExpr { name: Ident::from("this"), source_span: None }, allocator, )), @@ -434,7 +434,7 @@ fn convert_pure_function_body<'a>( let var_name = var .name .clone() - .unwrap_or_else(|| Atom::from(allocator.alloc_str(&format!("_v{}", var.xref.0)))); + .unwrap_or_else(|| Ident::from(allocator.alloc_str(&format!("_v{}", var.xref.0)))); OutputExpression::ReadVar(Box::new_in( ReadVarExpr { name: var_name, source_span: None }, allocator, @@ -443,7 +443,7 @@ fn convert_pure_function_body<'a>( // Read temporary variable IrExpression::ReadTemporary(tmp) => { - let var_name = tmp.name.clone().unwrap_or_else(|| Atom::from("_tmp")); + let var_name = tmp.name.clone().unwrap_or_else(|| Ident::from("_tmp")); OutputExpression::ReadVar(Box::new_in( ReadVarExpr { name: var_name, source_span: None }, allocator, @@ -452,7 +452,7 @@ fn convert_pure_function_body<'a>( // Assign temporary variable: _tmp = expr IrExpression::AssignTemporary(assign) => { - let var_name = assign.name.clone().unwrap_or_else(|| Atom::from("_tmp")); + let var_name = assign.name.clone().unwrap_or_else(|| Ident::from("_tmp")); let value = convert_pure_function_body(allocator, &assign.expr, params); OutputExpression::BinaryOperator(Box::new_in( BinaryOperatorExpr { @@ -533,8 +533,8 @@ fn convert_pure_function_body<'a>( OutputExpression::External(Box::new_in( ExternalExpr { value: ExternalReference { - module_name: Some(Atom::from("@angular/core")), - name: Some(Atom::from(allocator.alloc_str(instruction))), + module_name: Some(Ident::from("@angular/core")), + name: Some(Ident::from(allocator.alloc_str(instruction))), }, source_span: None, }, @@ -600,8 +600,8 @@ fn convert_pure_function_body<'a>( OutputExpression::External(Box::new_in( ExternalExpr { value: ExternalReference { - module_name: Some(Atom::from("@angular/core")), - name: Some(Atom::from(fn_name)), + module_name: Some(Ident::from("@angular/core")), + name: Some(Ident::from(fn_name)), }, source_span: None, }, @@ -643,8 +643,8 @@ fn convert_pure_function_body<'a>( OutputExpression::External(Box::new_in( ExternalExpr { value: ExternalReference { - module_name: Some(Atom::from("@angular/core")), - name: Some(Atom::from(Identifiers::PIPE_BIND_V)), + module_name: Some(Ident::from("@angular/core")), + name: Some(Ident::from(Identifiers::PIPE_BIND_V)), }, source_span: None, }, @@ -1005,8 +1005,8 @@ fn convert_pure_function_body<'a>( OutputExpression::External(Box::new_in( ExternalExpr { value: ExternalReference { - module_name: Some(Atom::from("@angular/core")), - name: Some(Atom::from(Identifiers::TWO_WAY_BINDING_SET)), + module_name: Some(Ident::from("@angular/core")), + name: Some(Ident::from(Identifiers::TWO_WAY_BINDING_SET)), }, source_span: None, }, @@ -1038,8 +1038,8 @@ fn convert_pure_function_body<'a>( OutputExpression::External(Box::new_in( ExternalExpr { value: ExternalReference { - module_name: Some(Atom::from("@angular/core")), - name: Some(Atom::from(Identifiers::READ_CONTEXT_LET)), + module_name: Some(Ident::from("@angular/core")), + name: Some(Ident::from(Identifiers::READ_CONTEXT_LET)), }, source_span: None, }, @@ -1066,8 +1066,8 @@ fn convert_pure_function_body<'a>( OutputExpression::External(Box::new_in( ExternalExpr { value: ExternalReference { - module_name: Some(Atom::from("@angular/core")), - name: Some(Atom::from(Identifiers::STORE_LET)), + module_name: Some(Ident::from("@angular/core")), + name: Some(Ident::from(Identifiers::STORE_LET)), }, source_span: None, }, @@ -1315,7 +1315,7 @@ fn convert_pure_function_body<'a>( fn convert_ast_for_pure_function_body<'a>( allocator: &'a Allocator, ast: &crate::ast::expression::AngularExpression<'a>, - params: &[Atom<'a>], + params: &[Ident<'a>], ) -> OutputExpression<'a> { use crate::ast::expression::{AngularExpression, LiteralMapKey}; use crate::output::ast::{LiteralArrayExpr, LiteralMapEntry, LiteralMapExpr}; @@ -1378,7 +1378,7 @@ fn convert_ast_for_pure_function_body<'a>( receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( crate::output::ast::ReadVarExpr { - name: Atom::from("ctx"), + name: Ident::from("ctx"), source_span: None, }, allocator, @@ -1629,7 +1629,7 @@ mod tests { #[test] fn test_empty_template() { let allocator = Allocator::default(); - let mut job = ComponentCompilationJob::new(&allocator, Atom::from("TestComponent")); + let mut job = ComponentCompilationJob::new(&allocator, Ident::from("TestComponent")); let result = compile_template(&mut job); diff --git a/crates/oxc_angular_compiler/src/pipeline/ingest.rs b/crates/oxc_angular_compiler/src/pipeline/ingest.rs index 2c956eeae..00723b51d 100644 --- a/crates/oxc_angular_compiler/src/pipeline/ingest.rs +++ b/crates/oxc_angular_compiler/src/pipeline/ingest.rs @@ -16,7 +16,7 @@ use oxc_allocator::{Allocator, Box, Vec}; use oxc_diagnostics::OxcDiagnostic; -use oxc_span::Atom; +use oxc_span::Ident; use super::compilation::{ CTX_REF, ComponentCompilationJob, DeferBlockDepsEmitMode, DeferMetadata, @@ -67,7 +67,7 @@ pub struct IngestOptions<'a> { /// Relative path to the context file for i18n suffix generation. /// /// Used to generate unique, file-based variable names for i18n translations. - pub relative_context_file_path: Option>, + pub relative_context_file_path: Option>, /// Whether to use external message IDs in i18n variable names. /// @@ -83,7 +83,7 @@ pub struct IngestOptions<'a> { pub defer_block_deps_emit_mode: DeferBlockDepsEmitMode, /// Relative path to the template file for source location debugging. - pub relative_template_path: Option>, + pub relative_template_path: Option>, /// Whether to enable debug source locations. /// @@ -635,7 +635,7 @@ fn convert_interpolation_to_ir<'a>( fn convert_interpolation_to_ir_with_i18n_placeholders<'a>( job: &mut ComponentCompilationJob<'a>, expr: AngularExpression<'a>, - i18n_placeholders: Vec<'a, Atom<'a>>, + i18n_placeholders: Vec<'a, Ident<'a>>, ) -> Box<'a, IrExpression<'a>> { let allocator = job.allocator; @@ -678,7 +678,7 @@ fn convert_interpolation_to_ir_with_i18n_placeholders<'a>( /// `ingest_component_with_options` directly. pub fn ingest_component<'a>( allocator: &'a Allocator, - component_name: Atom<'a>, + component_name: Ident<'a>, template: Vec<'a, R3Node<'a>>, ) -> ComponentCompilationJob<'a> { ingest_component_with_options(allocator, component_name, template, IngestOptions::default()) @@ -703,7 +703,7 @@ pub fn ingest_component<'a>( /// A `ComponentCompilationJob` ready for transformation phases. pub fn ingest_component_with_options<'a>( allocator: &'a Allocator, - component_name: Atom<'a>, + component_name: Ident<'a>, template: Vec<'a, R3Node<'a>>, options: IngestOptions<'a>, ) -> ComponentCompilationJob<'a> { @@ -814,7 +814,7 @@ fn ingest_text<'a>( job: &mut ComponentCompilationJob<'a>, view_xref: XrefId, text: R3Text<'a>, - icu_placeholder: Option>, + icu_placeholder: Option>, ) { let xref = job.allocate_xref_id(); @@ -840,7 +840,7 @@ fn ingest_bound_text<'a>( job: &mut ComponentCompilationJob<'a>, view_xref: XrefId, bound_text: R3BoundText<'a>, - icu_placeholder: Option>, + icu_placeholder: Option>, ) { let allocator = job.allocator; let xref = job.allocate_xref_id(); @@ -850,7 +850,7 @@ fn ingest_bound_text<'a>( base: CreateOpBase { source_span: Some(bound_text.source_span), ..Default::default() }, xref, slot: None, - initial_value: Atom::from(""), + initial_value: Ident::from(""), i18n_placeholder: None, icu_placeholder, }); @@ -861,7 +861,7 @@ fn ingest_bound_text<'a>( // Extract i18n placeholders from the bound text's i18n metadata // Ported from Angular's ingestBoundText (ingest.ts lines 485-495) - let i18n_placeholders: Vec<'_, Atom<'_>> = match &bound_text.i18n { + let i18n_placeholders: Vec<'_, Ident<'_>> = match &bound_text.i18n { Some(I18nMeta::Node(I18nNode::Container(container))) => { let mut placeholders = Vec::new_in(allocator); for child in container.children.iter() { @@ -947,7 +947,7 @@ fn ingest_icu<'a>(job: &mut ComponentCompilationJob<'a>, view_xref: XrefId, icu: } // Process vars (bound text expressions) - // In Rust, vars is typed as HashMap so no runtime check needed + // In Rust, vars is typed as HashMap so no runtime check needed for (placeholder_name, bound_text) in icu.vars { ingest_bound_text(job, view_xref, bound_text, Some(placeholder_name)); } @@ -1033,8 +1033,8 @@ fn ingest_element<'a>( let namespace = namespace_for_key(namespace_key); // Allocate the stripped element name in the arena - let tag: Atom<'a> = if namespace_key.is_some() { - Atom::from(allocator.alloc_str(element_name)) + let tag: Ident<'a> = if namespace_key.is_some() { + Ident::from(allocator.alloc_str(element_name)) } else { element.name.clone() }; @@ -1373,8 +1373,8 @@ fn ingest_static_attributes_with_i18n<'a>( // Split namespace from attribute name (e.g., `:xmlns:xlink` → namespace="xmlns", name="xlink") let (ns, local_name) = split_ns_name(name.as_str()); - let namespace = ns.map(|n| Atom::from(n)); - let local_name = Atom::from(local_name); + let namespace = ns.map(|n| Ident::from(n)); + let local_name = Ident::from(local_name); let extracted = ExtractedAttributeOp { base: CreateOpBase::default(), @@ -1415,8 +1415,8 @@ fn ingest_single_static_attribute<'a>( job: &mut ComponentCompilationJob<'a>, view_xref: XrefId, element_xref: XrefId, - name: Atom<'a>, - value: Atom<'a>, + name: Ident<'a>, + value: Ident<'a>, is_structural_template_attribute: bool, ) { use crate::output::ast::{LiteralExpr, LiteralValue, OutputExpression}; @@ -1453,8 +1453,8 @@ fn ingest_single_static_attribute<'a>( // For regular (non-structural) attributes, create ExtractedAttributeOp directly // Split namespace from attribute name (e.g., `:xmlns:xlink` → namespace="xmlns", name="xlink") let (ns, local_name) = split_ns_name(name.as_str()); - let namespace = ns.map(|n| Atom::from(n)); - let local_name = Atom::from(local_name); + let namespace = ns.map(|n| Ident::from(n)); + let local_name = Ident::from(local_name); let extracted = ExtractedAttributeOp { base: CreateOpBase::default(), @@ -1481,7 +1481,7 @@ fn ingest_bindings_owned<'a>( job: &mut ComponentCompilationJob<'a>, view_xref: XrefId, element_xref: XrefId, - tag: Option>, + tag: Option>, inputs: Vec<'a, R3BoundAttribute<'a>>, outputs: Vec<'a, R3BoundEvent<'a>>, ) { @@ -1620,7 +1620,7 @@ fn ingest_listener_owned<'a>( job: &mut ComponentCompilationJob<'a>, view_xref: XrefId, element_xref: XrefId, - tag: Option>, + tag: Option>, output: R3BoundEvent<'a>, ) { let allocator = job.allocator; @@ -1677,7 +1677,7 @@ fn ingest_listener_owned<'a>( // Create $event reference let event_ref = IrExpression::LexicalRead(Box::new_in( - LexicalReadExpr { name: Atom::from("$event"), source_span: None }, + LexicalReadExpr { name: Ident::from("$event"), source_span: None }, allocator, )); @@ -1816,7 +1816,7 @@ fn ingest_listener_owned<'a>( /// Checks if a template is an explicit `` (as opposed to a structural directive). /// Ported from Angular's `isPlainTemplate` in `ingest.ts`. -fn is_plain_template(tag_name: &Option) -> bool { +fn is_plain_template(tag_name: &Option) -> bool { tag_name.as_ref().map_or(false, |tag| tag.as_str() == NG_TEMPLATE_TAG_NAME) } @@ -1961,12 +1961,12 @@ fn ingest_template<'a>( // `tagName` for HTML. The sanitizeIdentifier function later replaces non-word chars with `_`. let fn_name_suffix = tag_name_without_namespace.map(|stripped_tag| { let suffix = prefix_with_namespace(stripped_tag, namespace); - Atom::from(allocator.alloc_str(&suffix)) + Ident::from(allocator.alloc_str(&suffix)) }); // Build the tag atom from the stripped tag name (without namespace prefix). // TypeScript passes `tagNameWithoutNamespace` to createTemplateOp (line 367). - let tag = tag_name_without_namespace.map(|s| Atom::from(allocator.alloc_str(s))); + let tag = tag_name_without_namespace.map(|s| Ident::from(allocator.alloc_str(s))); // Convert references to local refs - needed for template op creation let local_refs = ingest_references_owned(allocator, references); @@ -2177,7 +2177,7 @@ fn ingest_template<'a>( // NOTE: This happens AFTER children are ingested, matching Angular's order. for variable in variables { let context_value = if variable.value.is_empty() { - Atom::from("$implicit") + Ident::from("$implicit") } else { variable.value.clone() }; @@ -2354,7 +2354,7 @@ fn ingest_if_block<'a>( if let Some(view) = job.view_mut(branch_view_xref) { view.context_variables.push(ContextVariable { name: alias.name.clone(), - value: Atom::from(CTX_REF), + value: Ident::from(CTX_REF), xref: branch_view_xref, }); } @@ -2383,7 +2383,7 @@ fn ingest_if_block<'a>( // fn_name_suffix is hardcoded to "Conditional" without namespace prefix // This matches Angular's ingestIfBlock which passes 'Conditional' directly - let fn_name_suffix = Atom::from("Conditional"); + let fn_name_suffix = Ident::from("Conditional"); // Create the appropriate CREATE op // Namespace is always HTML for control flow blocks, matching Angular's hardcoded ir.Namespace.HTML @@ -2500,13 +2500,13 @@ fn ingest_for_block<'a>( // Create unique names for $index and $count that are suffixed with the view xref // to disambiguate nested @for loops. This matches Angular's TemplateDefinitionBuilder pattern. - let index_name: Atom<'a> = { + let index_name: Ident<'a> = { let s = allocator.alloc_str(&format!("ɵ$index_{}", body_xref.0)); - Atom::from(s) + Ident::from(s) }; - let count_name: Atom<'a> = { + let count_name: Ident<'a> = { let s = allocator.alloc_str(&format!("ɵ$count_{}", body_xref.0)); - Atom::from(s) + Ident::from(s) }; // Collect context variables and aliases for the body view @@ -2516,7 +2516,7 @@ fn ingest_for_block<'a>( // Add the item variable (maps to $implicit in the context) context_variables.push(ContextVariable { name: for_block.item.name.clone(), - value: Atom::from("$implicit"), + value: Ident::from("$implicit"), xref: body_xref, }); @@ -2556,7 +2556,7 @@ fn ingest_for_block<'a>( // This is the implicit $index variable (name and value are both $index) // Add both $index and the unique indexed name to context context_variables.push(ContextVariable { - name: Atom::from("$index"), + name: Ident::from("$index"), value: var.value.clone(), xref: body_xref, }); @@ -2570,7 +2570,7 @@ fn ingest_for_block<'a>( // This is the implicit $count variable (name and value are both $count) // Add both $count and the unique counted name to context context_variables.push(ContextVariable { - name: Atom::from("$count"), + name: Ident::from("$count"), value: var.value.clone(), xref: body_xref, }); @@ -2720,8 +2720,8 @@ fn ingest_for_block<'a>( fn get_computed_for_loop_variable_expression<'a>( allocator: &'a Allocator, value: &str, - index_name: &Atom<'a>, - count_name: &Atom<'a>, + index_name: &Ident<'a>, + count_name: &Ident<'a>, diagnostics: &mut std::vec::Vec, ) -> Result, ()> { match value { @@ -2797,7 +2797,7 @@ fn get_computed_for_loop_variable_expression<'a>( } /// Helper: create a LexicalRead expression -fn create_lexical_read<'a>(allocator: &'a Allocator, name: &Atom<'a>) -> IrExpression<'a> { +fn create_lexical_read<'a>(allocator: &'a Allocator, name: &Ident<'a>) -> IrExpression<'a> { IrExpression::LexicalRead(Box::new_in( LexicalReadExpr { name: name.clone(), source_span: None }, allocator, @@ -2822,7 +2822,7 @@ fn create_number_literal<'a>(allocator: &'a Allocator, value: f64) -> IrExpressi } /// Helper: create a string literal as an AST expression wrapped in IR -fn create_string_literal_atom<'a>(allocator: &'a Allocator, value: Atom<'a>) -> IrExpression<'a> { +fn create_string_literal_atom<'a>(allocator: &'a Allocator, value: Ident<'a>) -> IrExpression<'a> { use crate::ast::expression::{AbsoluteSourceSpan, LiteralPrimitive, LiteralValue, ParseSpan}; IrExpression::Ast(Box::new_in( @@ -2967,7 +2967,7 @@ fn ingest_switch_block<'a>( // fn_name_suffix is hardcoded to "Case" without namespace prefix // This matches Angular's ingestSwitchBlock which passes 'Case' directly - let fn_name_suffix = Atom::from("Case"); + let fn_name_suffix = Ident::from("Case"); // Create the appropriate CREATE op // Namespace is always HTML for control flow blocks, matching Angular's hardcoded ir.Namespace.HTML @@ -3099,7 +3099,7 @@ fn ingest_defer_view<'a>( // This TemplateOp will be reified to a `ɵɵdomTemplate()` call. // IMPORTANT: In Angular, TemplateOp.xref IS the embedded view's xref (secondaryView.xref). // We use the same pattern here so that defer_resolve_targets can find elements by view xref. - let fn_name_suffix = Some(Atom::from(job.allocator.alloc_str(&format!("Defer{suffix}")))); + let fn_name_suffix = Some(Ident::from(job.allocator.alloc_str(&format!("Defer{suffix}")))); // Convert i18n metadata to placeholder, matching Angular's ingestDeferView which passes // i18nMeta through to createTemplateOp. This enables propagate_i18n_blocks to wrap the @@ -3573,14 +3573,14 @@ fn ingest_references_owned<'a>( /// It mirrors Angular TypeScript's `HostBindingInput` interface in `ingest.ts`. pub struct HostBindingInput<'a> { /// Name of the component/directive. - pub component_name: Atom<'a>, + pub component_name: Ident<'a>, /// CSS selector of the component/directive. - pub component_selector: Atom<'a>, + pub component_selector: Ident<'a>, /// Host property bindings (`[prop]="expr"`). pub properties: Vec<'a, R3BoundAttribute<'a>>, /// Static host attributes (`attr="value"`). /// Uses OutputExpression to match TypeScript's `{[key: string]: o.Expression}`. - pub attributes: FxHashMap, crate::output::ast::OutputExpression<'a>>, + pub attributes: FxHashMap, crate::output::ast::OutputExpression<'a>>, /// Host event bindings (`(event)="handler"`). pub events: Vec<'a, R3BoundEvent<'a>>, } @@ -3996,7 +3996,7 @@ fn ingest_host_dom_property<'a>( let (binding_kind, name) = if property.name.starts_with("attr.") { // Handle `attr.` prefix let stripped = &property.name[5..]; - (BindingKind::Attribute, Atom::from(allocator.alloc_str(stripped))) + (BindingKind::Attribute, Ident::from(allocator.alloc_str(stripped))) } else { let kind = match property.binding_type { BindingType::Property => BindingKind::Property, @@ -4096,7 +4096,7 @@ fn extract_element_from_selector(selector: &str) -> Option { /// Uses OutputExpression directly to match TypeScript's `o.Expression` parameter. fn ingest_host_attribute<'a>( job: &mut HostBindingCompilationJob<'a>, - name: Atom<'a>, + name: Ident<'a>, value: crate::output::ast::OutputExpression<'a>, ) { use crate::ir::expression::IrExpression; @@ -4298,7 +4298,7 @@ fn ingest_control_flow_insertion_point<'a, 'b>( parent_xref: XrefId, xref: XrefId, children: &'b [R3Node<'a>], -) -> Option> { +) -> Option> { // Find the single root element or template let mut root: Option> = None; @@ -4484,12 +4484,12 @@ impl<'a, 'b> RootNodeRef<'a, 'b> { } } - fn tag_name(&self) -> Atom<'a> { + fn tag_name(&self) -> Ident<'a> { match self { RootNodeRef::Element(elem) => elem.name.clone(), RootNodeRef::Template(tmpl) => { // Template should have a tag_name since we checked for it - tmpl.tag_name.clone().unwrap_or_else(|| Atom::from("")) + tmpl.tag_name.clone().unwrap_or_else(|| Ident::from("")) } } } @@ -4514,12 +4514,12 @@ mod tests { let unexpected_i18n = I18nMeta::Message(I18nMessage { instance_id: 0, nodes: Vec::new_in(&allocator), - meaning: Atom::from(""), - description: Atom::from(""), - custom_id: Atom::from(""), - id: Atom::from(""), + meaning: Ident::from(""), + description: Ident::from(""), + custom_id: Ident::from(""), + id: Ident::from(""), legacy_ids: Vec::new_in(&allocator), - message_string: Atom::from(""), + message_string: Ident::from(""), }); let result = convert_i18n_meta_to_placeholder( @@ -4559,8 +4559,8 @@ mod tests { fn get_computed_for_loop_variable_expression_returns_err_for_unknown_var() { let allocator = Allocator::default(); let mut diagnostics = std::vec::Vec::new(); - let index_name = Atom::from("ɵ$index_0"); - let count_name = Atom::from("ɵ$count_0"); + let index_name = Ident::from("ɵ$index_0"); + let count_name = Ident::from("ɵ$count_0"); let result = get_computed_for_loop_variable_expression( &allocator, @@ -4583,8 +4583,8 @@ mod tests { #[test] fn get_computed_for_loop_variable_expression_returns_ok_for_known_vars() { let allocator = Allocator::default(); - let index_name = Atom::from("ɵ$index_0"); - let count_name = Atom::from("ɵ$count_0"); + let index_name = Ident::from("ɵ$index_0"); + let count_name = Ident::from("ɵ$count_0"); for var in &["$index", "$count", "$first", "$last", "$even", "$odd"] { let mut diagnostics = std::vec::Vec::new(); diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/attach_source_locations.rs b/crates/oxc_angular_compiler/src/pipeline/phases/attach_source_locations.rs index 555bca355..56127dc77 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/attach_source_locations.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/attach_source_locations.rs @@ -7,7 +7,7 @@ //! //! Ported from Angular's `template/pipeline/src/phases/attach_source_locations.ts`. -use oxc_span::Atom; +use oxc_span::Ident; use crate::ir::ops::{CreateOp, CreateOpBase, SourceLocationOp, XrefId}; use crate::pipeline::compilation::ComponentCompilationJob; @@ -110,7 +110,7 @@ pub fn attach_source_locations(job: &mut ComponentCompilationJob<'_>) { let source_op = CreateOp::SourceLocation(SourceLocationOp { base: CreateOpBase::default(), target: loc.target, - template_url: Atom::from(template_path.as_str()), + template_url: Ident::from(template_path.as_str()), line: loc.line, column: loc.column, }); diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/binding_specialization.rs b/crates/oxc_angular_compiler/src/pipeline/phases/binding_specialization.rs index d686b2d62..4528d1353 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/binding_specialization.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/binding_specialization.rs @@ -21,7 +21,7 @@ //! Ported from Angular's `template/pipeline/src/phases/binding_specialization.ts`. use oxc_allocator::Box; -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; use crate::ast::expression::{AbsoluteSourceSpan, AngularExpression, EmptyExpr, ParseSpan}; @@ -237,8 +237,8 @@ fn specialize_in_view<'a>( &mut binding.expression, create_placeholder_expression(allocator), ); - let ns_atom = namespace.map(|ns| Atom::from(ns)); - let local_atom = Atom::from(local_name); + let ns_atom = namespace.map(|ns| Ident::from(ns)); + let local_atom = Ident::from(local_name); let new_op = UpdateOp::Attribute(AttributeOp { base: UpdateOpBase { source_span, ..Default::default() }, target, @@ -473,8 +473,8 @@ pub fn specialize_bindings_for_host(job: &mut HostBindingCompilationJob<'_>) { &mut binding.expression, create_placeholder_expression(allocator), ); - let ns_atom = namespace.map(|ns| Atom::from(ns)); - let local_atom = Atom::from(local_name); + let ns_atom = namespace.map(|ns| Ident::from(ns)); + let local_atom = Ident::from(local_name); let new_op = UpdateOp::Attribute(AttributeOp { base: UpdateOpBase { source_span, ..Default::default() }, target, diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/const_collection.rs b/crates/oxc_angular_compiler/src/pipeline/phases/const_collection.rs index 2ec5d59e7..7a1aabab0 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/const_collection.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/const_collection.rs @@ -10,7 +10,7 @@ use oxc_allocator::Vec as OxcVec; use oxc_diagnostics::OxcDiagnostic; -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; use oxc_allocator::Box; @@ -31,10 +31,10 @@ use crate::pipeline::selector::{parse_selector_to_r3_selector, r3_selector_to_ou #[derive(Debug, Clone)] enum AttributeValue<'a> { /// A string literal value. - String(Atom<'a>), + String(Ident<'a>), /// An i18n variable reference (stores just the variable name for efficient comparison). /// This will be serialized as a ReadVar expression in the const array. - I18nVar(Atom<'a>), + I18nVar(Ident<'a>), } /// Marker values for attribute arrays (matches Angular's AttributeMarker enum). @@ -71,19 +71,19 @@ struct ElementAttributes<'a> { known: std::collections::HashMap>, /// Static attributes (namespace, name, value). /// Value can be a string literal or an expression (for i18n variable references). - attributes: std::vec::Vec<(Option>, Atom<'a>, Option>)>, + attributes: std::vec::Vec<(Option>, Ident<'a>, Option>)>, /// Class names. - classes: std::vec::Vec>, + classes: std::vec::Vec>, /// Style properties. - styles: std::vec::Vec<(Atom<'a>, Option>)>, + styles: std::vec::Vec<(Ident<'a>, Option>)>, /// Property bindings (just names for the const array). - bindings: std::vec::Vec>, + bindings: std::vec::Vec>, /// Template bindings. - template: std::vec::Vec>, + template: std::vec::Vec>, /// i18n attributes. - i18n: std::vec::Vec>, + i18n: std::vec::Vec>, /// The ngProjectAs selector value (if present). - project_as: Option>, + project_as: Option>, } impl<'a> ElementAttributes<'a> { @@ -296,7 +296,7 @@ impl<'a> ElementAttributes<'a> { } // Fall back to truthy expression check (for boolean attributes) - if attr.truthy_expression { Some(AttributeValue::String(Atom::from(""))) } else { None } + if attr.truthy_expression { Some(AttributeValue::String(Ident::from(""))) } else { None } } fn is_empty(&self) -> bool { diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/convert_animations.rs b/crates/oxc_angular_compiler/src/pipeline/phases/convert_animations.rs index fa0121b60..a89942192 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/convert_animations.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/convert_animations.rs @@ -38,7 +38,7 @@ fn get_animation_kind(name: &str) -> AnimationKind { /// Info needed to create Animation CreateOps. struct AnimationInfo<'a> { target: XrefId, - name: oxc_span::Atom<'a>, + name: oxc_span::Ident<'a>, animation_kind: AnimationKind, handler_ops: OxcVec<'a, UpdateOp<'a>>, source_span: Option, @@ -47,7 +47,7 @@ struct AnimationInfo<'a> { /// Info needed to create AnimationString CreateOps. struct AnimationStringInfo<'a> { target: XrefId, - name: oxc_span::Atom<'a>, + name: oxc_span::Ident<'a>, animation_kind: AnimationKind, expression: Box<'a, IrExpression<'a>>, source_span: Option, @@ -167,7 +167,7 @@ pub fn convert_animations(job: &mut ComponentCompilationJob<'_>) { // Third pass: insert Animation CreateOps into create list after their target elements if !animations_to_create.is_empty() || !strings_to_create.is_empty() { - let mut missing_targets: Vec> = Vec::new(); + let mut missing_targets: Vec> = Vec::new(); if let Some(view) = job.view_mut(view_xref) { // Process Animation ops (Value kind) diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/convert_i18n_bindings.rs b/crates/oxc_angular_compiler/src/pipeline/phases/convert_i18n_bindings.rs index 79a5321ff..8c9e2c3b7 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/convert_i18n_bindings.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/convert_i18n_bindings.rs @@ -69,7 +69,7 @@ struct ConversionInfo<'a> { op_ptr: NonNull>, target: XrefId, i18n_context: XrefId, - name: oxc_span::Atom<'a>, + name: oxc_span::Ident<'a>, source_span: Option, /// The expression (if it's an interpolation, we extract sub-expressions). expression: Option>, @@ -78,7 +78,7 @@ struct ConversionInfo<'a> { /// Information extracted from an Interpolation expression. struct InterpolationInfo<'a> { expressions: Vec>, - i18n_placeholders: Vec>, + i18n_placeholders: Vec>, } /// Processes update ops for a single view, converting i18n bindings. diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/create_i18n_contexts.rs b/crates/oxc_angular_compiler/src/pipeline/phases/create_i18n_contexts.rs index 432c24e5f..442ea3823 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/create_i18n_contexts.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/create_i18n_contexts.rs @@ -11,7 +11,7 @@ //! //! Ported from Angular's `template/pipeline/src/phases/create_i18n_contexts.ts`. -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; use crate::ir::enums::I18nContextKind; @@ -38,8 +38,8 @@ pub fn create_i18n_contexts(job: &mut ComponentCompilationJob<'_>) { message_instance_id: u32, is_create_op: bool, // true for ExtractedAttribute, false for update ops // Additional fields to uniquely identify the attribute - target: XrefId, // Element xref this attribute belongs to - name: Atom<'a>, // Attribute name + target: XrefId, // Element xref this attribute belongs to + name: Ident<'a>, // Attribute name } let mut attr_ops_needing_context: Vec> = Vec::new(); diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/expand_safe_reads.rs b/crates/oxc_angular_compiler/src/pipeline/phases/expand_safe_reads.rs index 0d57a08d9..4c4c6833f 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/expand_safe_reads.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/expand_safe_reads.rs @@ -287,7 +287,7 @@ where /// Data extracted from an access expression for processing. enum AccessInfo<'a> { /// Property read: `.name` - PropertyRead { name: oxc_span::Atom<'a>, source_span: Option }, + PropertyRead { name: oxc_span::Ident<'a>, source_span: Option }, /// Keyed read: `[key]` KeyedRead { key: IrExpression<'a>, source_span: Option }, /// Function call: `(args)` diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/extract_i18n_messages.rs b/crates/oxc_angular_compiler/src/pipeline/phases/extract_i18n_messages.rs index b72c6e90a..8c1717c00 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/extract_i18n_messages.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/extract_i18n_messages.rs @@ -7,7 +7,7 @@ use std::ptr::NonNull; -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; use crate::ir::enums::{I18nContextKind, I18nParamValueFlags}; @@ -57,7 +57,7 @@ pub fn extract_i18n_messages(job: &mut ComponentCompilationJob<'_>) { let mut i18n_blocks: FxHashMap, Option)> = FxHashMap::default(); // xref -> (root, context) let mut i18n_contexts: FxHashMap)> = FxHashMap::default(); // xref -> (kind, i18n_block) - let mut context_params: FxHashMap, Vec)>> = + let mut context_params: FxHashMap, Vec)>> = FxHashMap::default(); // Create an i18n message for each context. @@ -154,7 +154,7 @@ pub fn extract_i18n_messages(job: &mut ComponentCompilationJob<'_>) { // Third pass: handle ICU sub-messages // Collect ICU info first to avoid borrow issues - let mut icu_sub_message_associations: Vec<(XrefId, XrefId, Option>)> = Vec::new(); + let mut icu_sub_message_associations: Vec<(XrefId, XrefId, Option>)> = Vec::new(); for view_xref in &view_xrefs { let view = if view_xref.0 == 0 { Some(&job.root) } else { job.view(*view_xref) }; @@ -241,7 +241,7 @@ pub fn extract_i18n_messages(job: &mut ComponentCompilationJob<'_>) { XrefId, // view_xref NonNull>, // op_ptr XrefId, // icu_context_xref - Atom<'_>, // placeholder name + Ident<'_>, // placeholder name String, // formatted value )> = Vec::new(); @@ -286,7 +286,7 @@ pub fn extract_i18n_messages(job: &mut ComponentCompilationJob<'_>) { for (view_xref, op_ptr, context_xref, name, formatted) in icu_placeholders_to_process { // Find the I18nContext and add the icu_placeholder_literal // Allocate the formatted string in the arena - let formatted_atom = Atom::from(allocator.alloc_str(&formatted)); + let formatted_atom = Ident::from(allocator.alloc_str(&formatted)); for vx in &view_xrefs_for_icu { let view = if vx.0 == 0 { Some(&mut job.root) } else { job.view_mut(*vx) }; @@ -352,7 +352,7 @@ pub fn extract_i18n_messages(job: &mut ComponentCompilationJob<'_>) { /// Formats a list of params into (placeholder, formatted_value) pairs. /// Returns the formatted params and whether postprocessing is needed. pub fn format_params( - params: &[(Atom<'_>, Vec)], + params: &[(Ident<'_>, Vec)], ) -> (std::vec::Vec<(String, String)>, bool) { let mut formatted = std::vec::Vec::new(); let mut needs_postprocessing = false; @@ -398,7 +398,7 @@ pub fn format_param_values(values: &[I18nParamValue]) -> Option { /// /// Ported from Angular's `formatIcuPlaceholder` function. pub fn format_icu_placeholder( - strings: &[Atom<'_>], + strings: &[Ident<'_>], expression_placeholders: &[I18nParamValue], ) -> String { let mut result = String::new(); diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/generate_local_let_references.rs b/crates/oxc_angular_compiler/src/pipeline/phases/generate_local_let_references.rs index fcd9a2bf3..332e235db 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/generate_local_let_references.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/generate_local_let_references.rs @@ -42,7 +42,7 @@ pub fn generate_local_let_references(job: &mut ComponentCompilationJob<'_>) { None => continue, }; - let mut names: FxHashMap> = FxHashMap::default(); + let mut names: FxHashMap> = FxHashMap::default(); for op in view.create.iter() { if let CreateOp::DeclareLet(let_decl) = op { names.insert(let_decl.xref, let_decl.name.clone()); @@ -85,7 +85,7 @@ pub fn generate_local_let_references(job: &mut ComponentCompilationJob<'_>) { let declared_name = let_names .get(&store_let.target) .cloned() - .unwrap_or_else(|| oxc_span::Atom::from("")); + .unwrap_or_else(|| oxc_span::Ident::from("")); // Create a new Variable op with StoreLetExpr as the initializer let store_let_expr = IrExpression::StoreLet(Box::new_in( diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/generate_projection_def.rs b/crates/oxc_angular_compiler/src/pipeline/phases/generate_projection_def.rs index e969c3d96..b6717d1bc 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/generate_projection_def.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/generate_projection_def.rs @@ -9,7 +9,7 @@ //! Ported from Angular's `template/pipeline/src/phases/generate_projection_def.ts`. use oxc_allocator::{Box as OxcBox, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use std::ptr::NonNull; use crate::ir::ops::{CreateOp, CreateOpBase, ProjectionDefOp, XrefId}; @@ -72,7 +72,7 @@ pub fn generate_projection_defs(job: &mut ComponentCompilationJob<'_>) { // Phase 2: Assign projection slot indexes and collect selectors let allocator = job.allocator; - let mut selectors: Vec> = Vec::new(); + let mut selectors: Vec> = Vec::new(); for (slot_index, ptr) in projection_ptrs.iter().enumerate() { // SAFETY: ptr is valid from our cursor traversal @@ -82,7 +82,7 @@ pub fn generate_projection_defs(job: &mut ComponentCompilationJob<'_>) { proj.projection_slot_index = slot_index as u32; // Collect selector (use "*" for wildcard if None) - let selector = proj.selector.clone().unwrap_or_else(|| Atom::from("*")); + let selector = proj.selector.clone().unwrap_or_else(|| Ident::from("*")); selectors.push(selector); } } @@ -112,7 +112,10 @@ pub fn generate_projection_defs(job: &mut ComponentCompilationJob<'_>) { if selector.as_str() == "*" { // Wildcard stays as string literal "*" def_elements.push(OutputExpression::Literal(OxcBox::new_in( - LiteralExpr { value: LiteralValue::String(Atom::from("*")), source_span: None }, + LiteralExpr { + value: LiteralValue::String(Ident::from("*")), + source_span: None, + }, allocator, ))); } else { diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/generate_variables.rs b/crates/oxc_angular_compiler/src/pipeline/phases/generate_variables.rs index 2cf8583d6..1435446d1 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/generate_variables.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/generate_variables.rs @@ -17,7 +17,7 @@ //! Ported from Angular's `template/pipeline/src/phases/generate_variables.ts`. use oxc_allocator::Box; -use oxc_span::Atom; +use oxc_span::Ident; use crate::ir::enums::{SemanticVariableKind, VariableFlags}; use crate::ir::expression::{ @@ -31,7 +31,7 @@ use crate::pipeline::compilation::ComponentCompilationJob; #[derive(Debug, Clone)] struct LocalRefInfo<'a> { /// Name of the local reference (e.g., "myDiv" from #myDiv). - name: Atom<'a>, + name: Ident<'a>, /// XrefId of the element this reference points to. target_id: XrefId, /// Slot of the target element. @@ -49,7 +49,7 @@ struct LetDeclarationInfo<'a> { /// Slot of the @let declaration. target_slot: Option, /// Variable name. - variable_name: Atom<'a>, + variable_name: Ident<'a>, } /// Lexical scope of a view, including a reference to its parent view's scope. @@ -59,9 +59,9 @@ struct Scope<'a, 'b> { /// XrefId of the view this scope belongs to. view: XrefId, /// Context variables (name, value) from the view. - context_variables: Vec<(Atom<'a>, Atom<'a>)>, + context_variables: Vec<(Ident<'a>, Ident<'a>)>, /// Alias variables (name, expression) from the view. - alias_variables: Vec<(Atom<'a>, IrExpression<'a>)>, + alias_variables: Vec<(Ident<'a>, IrExpression<'a>)>, /// Local references collected from elements within the view. references: Vec>, /// @let declarations collected from the view. @@ -554,7 +554,7 @@ fn clone_update_op<'a>(allocator: &'a oxc_allocator::Allocator, op: &UpdateOp<'a base: Default::default(), xref: XrefId(0), kind: SemanticVariableKind::Identifier, - name: Atom::from(""), + name: Ident::from(""), initializer: Box::new_in( IrExpression::NextContext(Box::new_in( NextContextExpr { steps: 0, source_span: None }, @@ -674,7 +674,7 @@ fn create_next_context_variable<'a>( base: Default::default(), xref, kind: SemanticVariableKind::Context, - name: Atom::from(""), // Empty = naming phase will assign it + name: Ident::from(""), // Empty = naming phase will assign it initializer: Box::new_in(initializer, allocator), flags: VariableFlags::NONE, view: Some(context_view), @@ -694,8 +694,8 @@ fn create_context_read_variable<'a>( allocator: &'a oxc_allocator::Allocator, xref: XrefId, view_xref: XrefId, - name: Atom<'a>, - context_value: Atom<'a>, + name: Ident<'a>, + context_value: Ident<'a>, ) -> UpdateOp<'a> { use crate::pipeline::compilation::CTX_REF; @@ -740,7 +740,7 @@ fn create_alias_variable<'a>( allocator: &'a oxc_allocator::Allocator, xref: XrefId, view_xref: XrefId, - name: Atom<'a>, + name: Ident<'a>, expression: IrExpression<'a>, ) -> UpdateOp<'a> { UpdateOp::Variable(UpdateVariableOp { @@ -795,7 +795,7 @@ fn create_reference_variable<'a>( fn create_context_let_reference_variable<'a>( allocator: &'a oxc_allocator::Allocator, xref: XrefId, - name: Atom<'a>, + name: Ident<'a>, target_id: XrefId, target_slot: Option, ) -> UpdateOp<'a> { diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/host_style_property_parsing.rs b/crates/oxc_angular_compiler/src/pipeline/phases/host_style_property_parsing.rs index 40e50ad0b..823e88848 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/host_style_property_parsing.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/host_style_property_parsing.rs @@ -64,21 +64,21 @@ pub fn parse_host_style_properties(job: &mut ComponentCompilationJob<'_>) { // Parse property and unit suffix let (property, unit) = parse_property(&hyphenated); - binding.name = oxc_span::Atom::from(allocator.alloc_str(&property)); - binding.unit = unit.map(|u| oxc_span::Atom::from(allocator.alloc_str(&u))); + binding.name = oxc_span::Ident::from(allocator.alloc_str(&property)); + binding.unit = unit.map(|u| oxc_span::Ident::from(allocator.alloc_str(&u))); } else if name.starts_with(STYLE_BANG) { binding.kind = BindingKind::StyleProperty; - binding.name = oxc_span::Atom::from("style"); + binding.name = oxc_span::Ident::from("style"); } else if name.starts_with(CLASS_DOT) { binding.kind = BindingKind::ClassName; let class_name = &name[CLASS_DOT.len()..]; let (property, _) = parse_property(class_name); - binding.name = oxc_span::Atom::from(allocator.alloc_str(&property)); + binding.name = oxc_span::Ident::from(allocator.alloc_str(&property)); } else if name.starts_with(CLASS_BANG) { binding.kind = BindingKind::ClassName; let class_name = &name[CLASS_BANG.len()..]; let (property, _) = parse_property(class_name); - binding.name = oxc_span::Atom::from(allocator.alloc_str(&property)); + binding.name = oxc_span::Ident::from(allocator.alloc_str(&property)); } } } @@ -163,21 +163,21 @@ pub fn parse_host_style_properties_for_host(job: &mut HostBindingCompilationJob< // Parse property and unit suffix let (property, unit) = parse_property(&hyphenated); - binding.name = oxc_span::Atom::from(allocator.alloc_str(&property)); - binding.unit = unit.map(|u| oxc_span::Atom::from(allocator.alloc_str(&u))); + binding.name = oxc_span::Ident::from(allocator.alloc_str(&property)); + binding.unit = unit.map(|u| oxc_span::Ident::from(allocator.alloc_str(&u))); } else if name.starts_with(STYLE_BANG) { binding.kind = BindingKind::StyleProperty; - binding.name = oxc_span::Atom::from("style"); + binding.name = oxc_span::Ident::from("style"); } else if name.starts_with(CLASS_DOT) { binding.kind = BindingKind::ClassName; let class_name = &name[CLASS_DOT.len()..]; let (property, _) = parse_property(class_name); - binding.name = oxc_span::Atom::from(allocator.alloc_str(&property)); + binding.name = oxc_span::Ident::from(allocator.alloc_str(&property)); } else if name.starts_with(CLASS_BANG) { binding.kind = BindingKind::ClassName; let class_name = &name[CLASS_BANG.len()..]; let (property, _) = parse_property(class_name); - binding.name = oxc_span::Atom::from(allocator.alloc_str(&property)); + binding.name = oxc_span::Ident::from(allocator.alloc_str(&property)); } } } diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/i18n_closure.rs b/crates/oxc_angular_compiler/src/pipeline/phases/i18n_closure.rs index 4e5c126ca..9cc0f90a9 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/i18n_closure.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/i18n_closure.rs @@ -27,7 +27,7 @@ //! `template/pipeline/src/phases/i18n_const_collection.ts`. use oxc_allocator::{Box as AllocBox, Vec as AllocVec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::i18n::serializer::format_i18n_placeholder_name; use crate::output::ast::{ @@ -59,7 +59,7 @@ pub fn create_closure_mode_guard<'a>( TypeofExpr { expr: AllocBox::new_in( OutputExpression::ReadVar(AllocBox::new_in( - ReadVarExpr { name: Atom::from(NG_I18N_CLOSURE_MODE), source_span: None }, + ReadVarExpr { name: Ident::from(NG_I18N_CLOSURE_MODE), source_span: None }, allocator, )), allocator, @@ -71,7 +71,7 @@ pub fn create_closure_mode_guard<'a>( // "undefined" let undefined_literal = OutputExpression::Literal(AllocBox::new_in( - LiteralExpr { value: LiteralValue::String(Atom::from("undefined")), source_span: None }, + LiteralExpr { value: LiteralValue::String(Ident::from("undefined")), source_span: None }, allocator, )); @@ -88,7 +88,7 @@ pub fn create_closure_mode_guard<'a>( // ngI18nClosureMode let closure_mode_var = OutputExpression::ReadVar(AllocBox::new_in( - ReadVarExpr { name: Atom::from(NG_I18N_CLOSURE_MODE), source_span: None }, + ReadVarExpr { name: Ident::from(NG_I18N_CLOSURE_MODE), source_span: None }, allocator, )); @@ -107,14 +107,14 @@ pub fn create_closure_mode_guard<'a>( /// I18n message metadata for JSDoc comments. pub struct I18nMessageMeta<'a> { /// Message description for translators. - pub description: Option>, + pub description: Option>, /// Message meaning for disambiguation. - pub meaning: Option>, + pub meaning: Option>, } impl<'a> I18nMessageMeta<'a> { /// Creates a new I18n message metadata. - pub fn new(description: Option>, meaning: Option>) -> Self { + pub fn new(description: Option>, meaning: Option>) -> Self { Self { description, meaning } } } @@ -135,14 +135,14 @@ pub fn create_i18n_jsdoc<'a>( allocator: &'a oxc_allocator::Allocator, meta: &I18nMessageMeta<'a>, ) -> LeadingComment<'a> { - // Convert Option to Option with arena allocation + // Convert Option to Option with arena allocation let desc = meta.description.as_ref().map(|d| { let s = allocator.alloc_str(d.as_str()); - Atom::from(s) + Ident::from(s) }); let meaning = meta.meaning.as_ref().map(|m| { let s = allocator.alloc_str(m.as_str()); - Atom::from(s) + Ident::from(s) }); // Suppress msgDescriptions warning if no description is provided @@ -168,8 +168,8 @@ pub fn create_i18n_jsdoc<'a>( /// ``` pub fn create_goog_get_msg_statements<'a>( allocator: &'a oxc_allocator::Allocator, - i18n_var_name: &Atom<'a>, - closure_var_name: &Atom<'a>, + i18n_var_name: &Ident<'a>, + closure_var_name: &Ident<'a>, message_string: &str, params: &[(String, String)], meta: Option<&I18nMessageMeta<'a>>, @@ -182,7 +182,7 @@ pub fn create_goog_get_msg_statements<'a>( // First arg: message string with {$placeholder} format let message_str = allocator.alloc_str(message_string); goog_args.push(OutputExpression::Literal(AllocBox::new_in( - LiteralExpr { value: LiteralValue::String(Atom::from(message_str)), source_span: None }, + LiteralExpr { value: LiteralValue::String(Ident::from(message_str)), source_span: None }, allocator, ))); @@ -195,10 +195,10 @@ pub fn create_goog_get_msg_statements<'a>( let key_str = allocator.alloc_str(&formatted_name); let value_str = allocator.alloc_str(value); entries.push(LiteralMapEntry { - key: Atom::from(key_str), + key: Ident::from(key_str), value: OutputExpression::Literal(AllocBox::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from(value_str)), + value: LiteralValue::String(Ident::from(value_str)), source_span: None, }, allocator, @@ -214,13 +214,13 @@ pub fn create_goog_get_msg_statements<'a>( // goog.getMsg reference let goog_var = OutputExpression::ReadVar(AllocBox::new_in( - ReadVarExpr { name: Atom::from("goog"), source_span: None }, + ReadVarExpr { name: Ident::from("goog"), source_span: None }, allocator, )); let goog_get_msg = OutputExpression::ReadProp(AllocBox::new_in( ReadPropExpr { receiver: AllocBox::new_in(goog_var, allocator), - name: Atom::from("getMsg"), + name: Ident::from("getMsg"), optional: false, source_span: None, }, @@ -288,7 +288,7 @@ pub fn create_goog_get_msg_statements<'a>( /// ``` pub fn create_localize_statements<'a>( allocator: &'a oxc_allocator::Allocator, - i18n_var_name: &Atom<'a>, + i18n_var_name: &Ident<'a>, localized_expr: OutputExpression<'a>, ) -> AllocVec<'a, OutputStatement<'a>> { let mut statements = AllocVec::new_in(allocator); @@ -333,8 +333,8 @@ pub fn create_localize_statements<'a>( /// ``` pub fn create_translation_declaration<'a>( allocator: &'a oxc_allocator::Allocator, - i18n_var_name: Atom<'a>, - closure_var_name: Atom<'a>, + i18n_var_name: Ident<'a>, + closure_var_name: Ident<'a>, message_for_closure: &str, params: &[(String, String)], localized_expr: OutputExpression<'a>, diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/i18n_const_collection.rs b/crates/oxc_angular_compiler/src/pipeline/phases/i18n_const_collection.rs index 1a8f950d2..492a384c9 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/i18n_const_collection.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/i18n_const_collection.rs @@ -8,7 +8,7 @@ use std::ptr::NonNull; use oxc_allocator::Vec as ArenaVec; -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; use crate::i18n::serializer::format_i18n_placeholder_name; @@ -132,7 +132,7 @@ pub fn collect_i18n_consts(job: &mut ComponentCompilationJob<'_>) { // Map: i18n_block xref -> const index let mut message_const_indices: FxHashMap = FxHashMap::default(); // Map: i18n_context xref -> i18n variable name (for attribute bindings) - let mut i18n_var_names_by_context: FxHashMap> = FxHashMap::default(); + let mut i18n_var_names_by_context: FxHashMap> = FxHashMap::default(); // Counter for unique variable names let mut i18n_var_counter: usize = 0; @@ -184,7 +184,7 @@ pub fn collect_i18n_consts(job: &mut ComponentCompilationJob<'_>) { // Add to consts array with statements as initializers and record the index. let var_name_str = allocator.alloc_str(&main_var_name); let main_var = OutputExpression::ReadVar(oxc_allocator::Box::new_in( - ReadVarExpr { name: Atom::from(var_name_str), source_span: None }, + ReadVarExpr { name: Ident::from(var_name_str), source_span: None }, allocator, )); let const_index = @@ -195,7 +195,7 @@ pub fn collect_i18n_consts(job: &mut ComponentCompilationJob<'_>) { // Add statements to consts_initializers and save the variable name. job.consts_initializers.extend(statements); let var_name_str = allocator.alloc_str(&main_var_name); - let var_name_atom = Atom::from(var_name_str); + let var_name_atom = Ident::from(var_name_str); i18n_var_names_by_context.insert(ctx_xref, var_name_atom.clone()); // This i18n message may correspond to an individual extracted attribute. If so, @@ -320,7 +320,7 @@ pub fn collect_i18n_consts(job: &mut ComponentCompilationJob<'_>) { let name_str = allocator.alloc_str(&expr.name); let name_literal = OutputExpression::Literal(oxc_allocator::Box::new_in( crate::output::ast::LiteralExpr { - value: LiteralValue::String(Atom::from(name_str)), + value: LiteralValue::String(Ident::from(name_str)), source_span: None, }, allocator, @@ -508,11 +508,11 @@ fn collect_message<'a>( let meta = if msg_info.description.is_some() || msg_info.meaning.is_some() { let desc = msg_info.description.as_ref().map(|d| { let s = allocator.alloc_str(d); - Atom::from(s) + Ident::from(s) }); let meaning = msg_info.meaning.as_ref().map(|m| { let s = allocator.alloc_str(m); - Atom::from(s) + Ident::from(s) }); Some(I18nMessageMeta::new(desc, meaning)) } else { @@ -542,8 +542,8 @@ fn collect_message<'a>( }; // Generate dual-mode translation declaration - let i18n_var_atom = Atom::from(allocator.alloc_str(&i18n_var_name)); - let closure_var_atom = Atom::from(allocator.alloc_str(&closure_var_name)); + let i18n_var_atom = Ident::from(allocator.alloc_str(&i18n_var_name)); + let closure_var_atom = Ident::from(allocator.alloc_str(&closure_var_name)); let statements = create_translation_declaration( allocator, @@ -593,11 +593,11 @@ fn generate_message_from_params(params: &[(String, String)]) -> String { /// Format params from an I18nContext into (placeholder, value) pairs. fn format_context_params( - params: &oxc_allocator::HashMap<'_, Atom<'_>, ArenaVec<'_, I18nParamValue>>, + params: &oxc_allocator::HashMap<'_, Ident<'_>, ArenaVec<'_, I18nParamValue>>, ) -> Vec<(String, String)> { use crate::pipeline::phases::extract_i18n_messages::format_params; - let params_vec: Vec<(Atom<'_>, Vec)> = + let params_vec: Vec<(Ident<'_>, Vec)> = params.iter().map(|(k, v)| (k.clone(), v.iter().copied().collect())).collect(); let (formatted, _needs_postprocessing) = format_params(¶ms_vec); @@ -640,14 +640,14 @@ fn create_localize_expression<'a>( let first_text = text_parts.first().map(|s| s.as_str()).unwrap_or(""); let head_cooked = serialize_i18n_head(first_text, &meaning, &description, &custom_id); let head_str = allocator.alloc_str(&head_cooked); - message_parts.push(Atom::from(head_str)); + message_parts.push(Ident::from(head_str)); // Subsequent parts: ":PLACEHOLDER_NAME:text" for (i, placeholder) in placeholder_order.iter().enumerate() { // Format placeholder name (UPPERCASE for $localize) let formatted_name = format_i18n_placeholder_name(placeholder, false); let name_str = allocator.alloc_str(&formatted_name); - placeholder_names.push(Atom::from(name_str)); + placeholder_names.push(Ident::from(name_str)); // Get the value for this placeholder // The params_map is keyed by the original placeholder name, but the message_string @@ -657,7 +657,7 @@ fn create_localize_expression<'a>( let value_str = allocator.alloc_str(&value); let literal_expr = OutputExpression::Literal(oxc_allocator::Box::new_in( crate::output::ast::LiteralExpr { - value: LiteralValue::String(Atom::from(value_str)), + value: LiteralValue::String(Ident::from(value_str)), source_span: None, }, allocator, @@ -668,21 +668,21 @@ fn create_localize_expression<'a>( let text_part = text_parts.get(i + 1).map(|s| s.as_str()).unwrap_or(""); let part_cooked = serialize_i18n_template_part(&formatted_name, text_part); let part_str = allocator.alloc_str(&part_cooked); - message_parts.push(Atom::from(part_str)); + message_parts.push(Ident::from(part_str)); } // Store metadata for potential future use (JSDoc generation in emitter) let desc_atom = description.map(|d| { let s = allocator.alloc_str(&d); - Atom::from(s) + Ident::from(s) }); let meaning_atom = meaning.map(|m| { let s = allocator.alloc_str(&m); - Atom::from(s) + Ident::from(s) }); let custom_id_atom = custom_id.map(|c| { let s = allocator.alloc_str(&c); - Atom::from(s) + Ident::from(s) }); OutputExpression::LocalizedString(oxc_allocator::Box::new_in( @@ -829,12 +829,12 @@ fn wrap_with_postprocess<'a>( crate::output::ast::ReadPropExpr { receiver: oxc_allocator::Box::new_in( OutputExpression::ReadVar(oxc_allocator::Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::I18N_POSTPROCESS), + name: Ident::from(Identifiers::I18N_POSTPROCESS), optional: false, source_span: None, }, @@ -858,13 +858,13 @@ fn wrap_with_postprocess<'a>( for var_name in var_names { let var_str = allocator.alloc_str(var_name); var_refs.push(OutputExpression::ReadVar(oxc_allocator::Box::new_in( - ReadVarExpr { name: Atom::from(var_str), source_span: None }, + ReadVarExpr { name: Ident::from(var_str), source_span: None }, allocator, ))); } entries.push(LiteralMapEntry { - key: Atom::from(key_str), + key: Ident::from(key_str), value: OutputExpression::LiteralArray(oxc_allocator::Box::new_in( LiteralArrayExpr { entries: var_refs, source_span: None }, allocator, @@ -898,7 +898,7 @@ mod tests { use crate::output::ast::{LiteralExpr, OutputExpression, ReadVarExpr}; use crate::output::emitter::JsEmitter; use oxc_allocator::Allocator; - use oxc_span::Atom; + use oxc_span::Ident; #[test] fn test_wrap_with_postprocess_uses_namespace_prefix() { @@ -913,7 +913,7 @@ mod tests { // Create a simple input expression (simulating a $localize result) let input_expr = OutputExpression::Literal(oxc_allocator::Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from("test message")), + value: LiteralValue::String(Ident::from("test message")), source_span: None, }, &allocator, @@ -941,7 +941,7 @@ mod tests { let allocator = Allocator::default(); let input_expr = OutputExpression::ReadVar(oxc_allocator::Box::new_in( - ReadVarExpr { name: Atom::from("i18n_0"), source_span: None }, + ReadVarExpr { name: Ident::from("i18n_0"), source_span: None }, &allocator, )); diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/i18n_text_extraction.rs b/crates/oxc_angular_compiler/src/pipeline/phases/i18n_text_extraction.rs index bcb8e201e..20ea582a8 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/i18n_text_extraction.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/i18n_text_extraction.rs @@ -9,7 +9,7 @@ use std::ptr::NonNull; -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; use crate::ir::enums::{I18nExpressionFor, I18nParamResolutionTime}; @@ -50,8 +50,12 @@ fn convert_i18n_text_in_view(job: &mut ComponentCompilationJob<'_>, view_xref: X // Track text ops to be replaced with IcuPlaceholder ops // (text_op_ptr, text_xref, icu_placeholder_name, initial_value) - let mut text_ops_to_replace_with_icu: Vec<(NonNull>, XrefId, Atom<'_>, Atom<'_>)> = - Vec::new(); + let mut text_ops_to_replace_with_icu: Vec<( + NonNull>, + XrefId, + Ident<'_>, + Ident<'_>, + )> = Vec::new(); // Track text ops to be removed (those without ICU placeholder) let mut text_nodes_to_remove: Vec>> = Vec::new(); @@ -260,7 +264,7 @@ fn convert_i18n_text_in_view(job: &mut ComponentCompilationJob<'_>, view_xref: X expression: oxc_allocator::Box::new_in(expr.clone_in(allocator), allocator), resolution_time, usage: I18nExpressionFor::I18nText, - name: oxc_span::Atom::from(""), + name: oxc_span::Ident::from(""), i18n_placeholder, icu_placeholder: info.icu_placeholder_xref, })); @@ -305,7 +309,7 @@ fn convert_i18n_text_in_view(job: &mut ComponentCompilationJob<'_>, view_xref: X ), resolution_time, usage: I18nExpressionFor::I18nText, - name: oxc_span::Atom::from(""), + name: oxc_span::Ident::from(""), i18n_placeholder: None, icu_placeholder: info.icu_placeholder_xref, })); diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/local_refs.rs b/crates/oxc_angular_compiler/src/pipeline/phases/local_refs.rs index 39c6b975d..6d6b6fb38 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/local_refs.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/local_refs.rs @@ -26,7 +26,7 @@ use crate::pipeline::compilation::{ComponentCompilationJob, ConstValue}; /// Info about an op that needs local refs serialized. struct LocalRefInfo<'a> { op_kind: LocalRefOpKind, - refs: std::vec::Vec<(oxc_span::Atom<'a>, oxc_span::Atom<'a>)>, + refs: std::vec::Vec<(oxc_span::Ident<'a>, oxc_span::Ident<'a>)>, } #[derive(Clone, Copy)] @@ -171,7 +171,7 @@ pub fn lift_local_refs(job: &mut ComponentCompilationJob<'_>) { /// not extracted to a top-level const declaration. fn serialize_local_refs_to_const_value<'a>( allocator: &'a oxc_allocator::Allocator, - refs: &[(oxc_span::Atom<'a>, oxc_span::Atom<'a>)], + refs: &[(oxc_span::Ident<'a>, oxc_span::Ident<'a>)], ) -> ConstValue<'a> { let mut entries = OxcVec::with_capacity_in(refs.len() * 2, allocator); diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/naming.rs b/crates/oxc_angular_compiler/src/pipeline/phases/naming.rs index 4a0992147..d6db424cf 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/naming.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/naming.rs @@ -11,7 +11,7 @@ //! //! Ported from Angular's `template/pipeline/src/phases/naming.ts`. -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; use crate::ir::enums::{BindingKind, SemanticVariableKind}; @@ -65,7 +65,7 @@ enum SemanticVariableKey<'a> { /// SavedView variable for a specific view. SavedView { view: XrefId }, /// Alias variable with a specific identifier. - Alias { identifier: Atom<'a> }, + Alias { identifier: Ident<'a> }, /// Reference variable (template ref like #defaultContent). /// Multiple child views accessing the same template ref share this key. /// Identified by the target element and offset. @@ -86,7 +86,7 @@ enum SemanticVariableKey<'a> { /// The view whose context is being read (the parent view with context variables). view: XrefId, /// The identifier name for the variable (e.g., "breadcrumb"). - identifier: Atom<'a>, + identifier: Ident<'a>, }, } @@ -104,7 +104,7 @@ enum SemanticVariableKey<'a> { /// We detect references by checking if the initializer is an `IrExpression::Reference`. fn make_semantic_key<'a>( kind: SemanticVariableKind, - name: &Atom<'a>, + name: &Ident<'a>, view: Option, initializer: &IrExpression<'a>, ) -> Option> { @@ -257,13 +257,14 @@ pub fn name_functions_and_variables(job: &mut ComponentCompilationJob<'_>) { let sanitized_base = sanitize_identifier(component_name); // Map from xref to generated variable name - let mut var_names: FxHashMap> = FxHashMap::default(); + let mut var_names: FxHashMap> = FxHashMap::default(); // Map from semantic key to generated variable name. // This enables name reuse for variables with the same semantic identity, // matching TypeScript's behavior where multiple Variable ops can share // the same SemanticVariable object. - let mut semantic_var_names: FxHashMap, Atom<'_>> = FxHashMap::default(); + let mut semantic_var_names: FxHashMap, Ident<'_>> = + FxHashMap::default(); // Recursively name views starting from root // TypeScript: addNamesToView(job.root, job.componentName, state, compatibility) @@ -302,8 +303,8 @@ fn add_names_to_root_view<'a>( base_name: &str, allocator: &'a oxc_allocator::Allocator, state: &mut NamingState, - var_names: &mut FxHashMap>, - semantic_var_names: &mut FxHashMap, Atom<'a>>, + var_names: &mut FxHashMap>, + semantic_var_names: &mut FxHashMap, Ident<'a>>, ) { // Name the root view // TypeScript: unit.fnName = unit.job.pool.uniqueName(sanitizeIdentifier(`${baseName}_${unit.job.fnSuffix}`), false) @@ -335,8 +336,8 @@ fn add_names_to_child_view<'a>( base_name: &str, allocator: &'a oxc_allocator::Allocator, state: &mut NamingState, - var_names: &mut FxHashMap>, - semantic_var_names: &mut FxHashMap, Atom<'a>>, + var_names: &mut FxHashMap>, + semantic_var_names: &mut FxHashMap, Ident<'a>>, ) { // Name this view // TypeScript: unit.fnName = unit.job.pool.uniqueName(sanitizeIdentifier(`${baseName}_${unit.job.fnSuffix}`), false) @@ -386,10 +387,10 @@ fn process_view_ops_depth_first<'a>( view_xref: Option, // None for root view base_name: &str, allocator: &'a oxc_allocator::Allocator, - fn_name: Option<&Atom<'a>>, + fn_name: Option<&Ident<'a>>, state: &mut NamingState, - var_names: &mut FxHashMap>, - semantic_var_names: &mut FxHashMap, Atom<'a>>, + var_names: &mut FxHashMap>, + semantic_var_names: &mut FxHashMap, Ident<'a>>, ) { // Phase 0: Process function ops FIRST (matches TypeScript ops() generator order) // Arrow functions have their own ops lists that contain Variable ops prepended @@ -550,10 +551,10 @@ fn process_create_ops_with_child_recursion<'a>( view_xref: Option, allocator: &'a oxc_allocator::Allocator, base_name: &str, - fn_name: Option<&Atom<'a>>, + fn_name: Option<&Ident<'a>>, state: &mut NamingState, - var_names: &mut FxHashMap>, - semantic_var_names: &mut FxHashMap, Atom<'a>>, + var_names: &mut FxHashMap>, + semantic_var_names: &mut FxHashMap, Ident<'a>>, child_views: &[ChildViewInfo], ) { // Group child views by their create op index for efficient lookup @@ -679,8 +680,8 @@ fn name_create_variable_op<'a>( var_op: &mut crate::ir::ops::VariableOp<'a>, allocator: &'a oxc_allocator::Allocator, state: &mut NamingState, - var_names: &mut FxHashMap>, - semantic_var_names: &mut FxHashMap, Atom<'a>>, + var_names: &mut FxHashMap>, + semantic_var_names: &mut FxHashMap, Ident<'a>>, ) { // Check if we need to assign a name let needs_naming = @@ -726,8 +727,8 @@ fn name_variable_op<'a>( var_op: &mut crate::ir::ops::UpdateVariableOp<'a>, allocator: &'a oxc_allocator::Allocator, state: &mut NamingState, - var_names: &mut FxHashMap>, - semantic_var_names: &mut FxHashMap, Atom<'a>>, + var_names: &mut FxHashMap>, + semantic_var_names: &mut FxHashMap, Ident<'a>>, ) { // Check if we need to assign a name let needs_naming = @@ -770,10 +771,10 @@ fn process_single_create_op_ref<'a>( op: &mut CreateOp<'a>, allocator: &'a oxc_allocator::Allocator, base_name: &str, - fn_name: Option<&Atom<'a>>, + fn_name: Option<&Ident<'a>>, state: &mut NamingState, - var_names: &mut FxHashMap>, - semantic_var_names: &mut FxHashMap, Atom<'a>>, + var_names: &mut FxHashMap>, + semantic_var_names: &mut FxHashMap, Ident<'a>>, ) { match op { CreateOp::Listener(listener) => { @@ -805,7 +806,7 @@ fn process_single_create_op_ref<'a>( .unwrap_or("start"); let new_name = format!("@{}.{}", listener.name.as_str(), phase_str); let name_str = allocator.alloc_str(&new_name); - listener.name = Atom::from(name_str); + listener.name = Ident::from(name_str); // Note: no trailing underscore - the @ in event_name will become _ after sanitize (new_name, "animation") } else { @@ -825,7 +826,7 @@ fn process_single_create_op_ref<'a>( let sanitized = sanitize_identifier(&handler_name); let name_str = allocator.alloc_str(&sanitized); - listener.handler_fn_name = Some(Atom::from(name_str)); + listener.handler_fn_name = Some(Ident::from(name_str)); } // Process handler ops inline for handler_op in listener.handler_ops.iter_mut() { @@ -844,7 +845,7 @@ fn process_single_create_op_ref<'a>( let handler_name = format!("{fn_name_str}_{tag}_{event_name}_{slot}_listener"); let sanitized = sanitize_identifier(&handler_name); let name_str = allocator.alloc_str(&sanitized); - listener.handler_fn_name = Some(Atom::from(name_str)); + listener.handler_fn_name = Some(Ident::from(name_str)); } // Process handler ops inline for handler_op in listener.handler_ops.iter_mut() { @@ -872,7 +873,7 @@ fn process_single_create_op_ref<'a>( let sanitized = sanitize_identifier(&handler_name); let name_str = allocator.alloc_str(&sanitized); - listener.handler_fn_name = Some(Atom::from(name_str)); + listener.handler_fn_name = Some(Ident::from(name_str)); } // Process handler ops inline for handler_op in listener.handler_ops.iter_mut() { @@ -892,7 +893,7 @@ fn process_single_create_op_ref<'a>( let handler_name = format!("{fn_name_str}_{animation_kind}_cb"); let sanitized = sanitize_identifier(&handler_name); let name_str = allocator.alloc_str(&sanitized); - animation.handler_fn_name = Some(Atom::from(name_str)); + animation.handler_fn_name = Some(Ident::from(name_str)); } // Process handler ops inline for handler_op in animation.handler_ops.iter_mut() { @@ -942,7 +943,7 @@ fn process_function_ops_in_view<'a>( view_xref: Option, allocator: &'a oxc_allocator::Allocator, state: &mut NamingState, - var_names: &mut FxHashMap>, + var_names: &mut FxHashMap>, ) { let functions = match view_xref { None => &job.root.functions, @@ -967,7 +968,7 @@ fn process_function_ops_in_view<'a>( // Each arrow function gets its own semantic variable map to prevent // deduplication with other scopes (listeners, update, other arrow functions). - let mut arrow_fn_semantic_var_names: FxHashMap, Atom<'a>> = + let mut arrow_fn_semantic_var_names: FxHashMap, Ident<'a>> = FxHashMap::default(); // Process Variable ops in this arrow function @@ -991,8 +992,8 @@ fn process_update_ops_in_view<'a>( view_xref: Option, allocator: &'a oxc_allocator::Allocator, state: &mut NamingState, - var_names: &mut FxHashMap>, - semantic_var_names: &mut FxHashMap, Atom<'a>>, + var_names: &mut FxHashMap>, + semantic_var_names: &mut FxHashMap, Ident<'a>>, ) { let update_ops = match view_xref { None => &mut job.root.update, @@ -1014,25 +1015,25 @@ fn process_update_ops_in_view<'a>( if prop.binding_kind == BindingKind::LegacyAnimation && !prop.name.starts_with('@') { let prefixed = format!("@{}", prop.name.as_str()); - prop.name = Atom::from(allocator.alloc_str(&prefixed)); + prop.name = Ident::from(allocator.alloc_str(&prefixed)); } } UpdateOp::DomProperty(prop) => { if prop.binding_kind == BindingKind::LegacyAnimation && !prop.name.starts_with('@') { let prefixed = format!("@{}", prop.name.as_str()); - prop.name = Atom::from(allocator.alloc_str(&prefixed)); + prop.name = Ident::from(allocator.alloc_str(&prefixed)); } } UpdateOp::StyleProp(style) => { if !style.name.starts_with("--") { let hyphenated = hyphenate(style.name.as_str()); - style.name = Atom::from(allocator.alloc_str(&hyphenated)); + style.name = Ident::from(allocator.alloc_str(&hyphenated)); } if style.name.contains("!important") { let stripped = style.name.as_str().replace("!important", ""); let stripped = stripped.trim_end(); - style.name = Atom::from(allocator.alloc_str(stripped)); + style.name = Ident::from(allocator.alloc_str(stripped)); } } UpdateOp::Variable(var_op) => { @@ -1053,9 +1054,9 @@ fn process_update_ops_in_view<'a>( fn get_variable_name<'a>( allocator: &'a oxc_allocator::Allocator, kind: SemanticVariableKind, - identifier: Option<&Atom<'a>>, + identifier: Option<&Ident<'a>>, state: &mut NamingState, -) -> Atom<'a> { +) -> Ident<'a> { let name = match kind { SemanticVariableKind::Context => { // Context uses post-increment (starts at 0) @@ -1088,14 +1089,14 @@ fn get_variable_name<'a>( }; let name_str = allocator.alloc_str(&name); - Atom::from(name_str) + Ident::from(name_str) } /// Propagates variable names to ReadVariableExpr expressions in a view. fn propagate_variable_names_in_view<'a>( create_ops: &mut crate::ir::list::CreateOpList<'a>, update_ops: &mut crate::ir::list::UpdateOpList<'a>, - var_names: &FxHashMap>, + var_names: &FxHashMap>, ) { // Process create operations for op in create_ops.iter_mut() { @@ -1123,7 +1124,7 @@ fn propagate_variable_names_in_view<'a>( /// Propagates a variable name to a ReadVariableExpr expression. fn propagate_name_to_expression<'a>( expr: &mut IrExpression<'a>, - var_names: &FxHashMap>, + var_names: &FxHashMap>, ) { if let IrExpression::ReadVariable(read_var) = expr { // Only set name if it's currently None @@ -1148,7 +1149,7 @@ pub fn name_functions_and_variables_for_host(job: &mut HostBindingCompilationJob // Generate the function name for the host binding function let fn_name = format!("{}_{}", component_name, job.fn_suffix); - job.root.fn_name = Some(oxc_span::Atom::from(allocator.alloc_str(&fn_name))); + job.root.fn_name = Some(oxc_span::Ident::from(allocator.alloc_str(&fn_name))); // Name listener handlers in create ops // Host listeners need names like: ComponentName_click_HostBindingHandler @@ -1162,7 +1163,7 @@ pub fn name_functions_and_variables_for_host(job: &mut HostBindingCompilationJob let handler_name = format!("{sanitized_base}_{event_name}_HostBindingHandler"); let sanitized = sanitize_identifier(&handler_name); let name_str = allocator.alloc_str(&sanitized); - listener.handler_fn_name = Some(Atom::from(name_str)); + listener.handler_fn_name = Some(Ident::from(name_str)); } } CreateOp::AnimationListener(listener) => { @@ -1172,7 +1173,7 @@ pub fn name_functions_and_variables_for_host(job: &mut HostBindingCompilationJob format!("{sanitized_base}_{animation_kind}_HostBindingHandler"); let sanitized = sanitize_identifier(&handler_name); let name_str = allocator.alloc_str(&sanitized); - listener.handler_fn_name = Some(Atom::from(name_str)); + listener.handler_fn_name = Some(Ident::from(name_str)); } } _ => {} @@ -1187,7 +1188,7 @@ mod tests { use crate::ir::expression::{IrExpression, NextContextExpr}; use crate::ir::ops::{UpdateVariableOp, XrefId}; use oxc_allocator::{Allocator, Box as AllocBox}; - use oxc_span::Atom; + use oxc_span::Ident; /// Helper: create an `UpdateVariableOp` representing a NextContext-based /// context variable for the given `view_xref`. The variable starts with @@ -1201,7 +1202,7 @@ mod tests { base: Default::default(), xref, kind: SemanticVariableKind::Context, - name: Atom::from(""), + name: Ident::from(""), initializer: AllocBox::new_in( IrExpression::NextContext(AllocBox::new_in( NextContextExpr { steps: 1, source_span: None }, @@ -1225,8 +1226,8 @@ mod tests { fn test_shared_semantic_map_deduplicates_context_variables() { let allocator = Allocator::default(); let mut state = NamingState { index: 0 }; - let mut var_names: FxHashMap> = FxHashMap::default(); - let mut semantic_var_names: FxHashMap, Atom<'_>> = + let mut var_names: FxHashMap> = FxHashMap::default(); + let mut semantic_var_names: FxHashMap, Ident<'_>> = FxHashMap::default(); let parent_view = XrefId(100); @@ -1280,12 +1281,12 @@ mod tests { fn test_arrow_function_gets_independent_context_variable_name() { let allocator = Allocator::default(); let mut state = NamingState { index: 0 }; - let mut var_names: FxHashMap> = FxHashMap::default(); + let mut var_names: FxHashMap> = FxHashMap::default(); let parent_view = XrefId(100); // --- Simulate the update scope naming a Context variable --- - let mut update_semantic: FxHashMap, Atom<'_>> = + let mut update_semantic: FxHashMap, Ident<'_>> = FxHashMap::default(); let mut update_ctx_var = make_context_var_op(&allocator, XrefId(1), parent_view); name_variable_op( @@ -1303,7 +1304,7 @@ mod tests { assert_eq!(state.index, 1); // --- Simulate an arrow function with its OWN semantic map (the fix) --- - let mut arrow_fn_semantic: FxHashMap, Atom<'_>> = + let mut arrow_fn_semantic: FxHashMap, Ident<'_>> = FxHashMap::default(); let mut arrow_ctx_var = make_context_var_op(&allocator, XrefId(2), parent_view); name_variable_op( @@ -1335,12 +1336,12 @@ mod tests { fn test_multiple_arrow_functions_get_independent_names() { let allocator = Allocator::default(); let mut state = NamingState { index: 0 }; - let mut var_names: FxHashMap> = FxHashMap::default(); + let mut var_names: FxHashMap> = FxHashMap::default(); let parent_view = XrefId(100); // Arrow function 1: own semantic map. - let mut arrow1_semantic: FxHashMap, Atom<'_>> = + let mut arrow1_semantic: FxHashMap, Ident<'_>> = FxHashMap::default(); let mut arrow1_ctx = make_context_var_op(&allocator, XrefId(1), parent_view); name_variable_op( @@ -1353,7 +1354,7 @@ mod tests { assert_eq!(arrow1_ctx.name.as_str(), "ctx_r0"); // Arrow function 2: own semantic map. - let mut arrow2_semantic: FxHashMap, Atom<'_>> = + let mut arrow2_semantic: FxHashMap, Ident<'_>> = FxHashMap::default(); let mut arrow2_ctx = make_context_var_op(&allocator, XrefId(2), parent_view); name_variable_op( @@ -1383,10 +1384,10 @@ mod tests { fn test_shared_map_causes_incorrect_deduplication_for_arrow_functions() { let allocator = Allocator::default(); let mut state = NamingState { index: 0 }; - let mut var_names: FxHashMap> = FxHashMap::default(); + let mut var_names: FxHashMap> = FxHashMap::default(); // Use a SINGLE shared semantic map (simulating the buggy behavior). - let mut shared_semantic: FxHashMap, Atom<'_>> = + let mut shared_semantic: FxHashMap, Ident<'_>> = FxHashMap::default(); let parent_view = XrefId(100); diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/parse_extracted_styles.rs b/crates/oxc_angular_compiler/src/pipeline/phases/parse_extracted_styles.rs index 97200b911..56df651b6 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/parse_extracted_styles.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/parse_extracted_styles.rs @@ -6,7 +6,7 @@ //! Ported from Angular's `template/pipeline/src/phases/parse_extracted_styles.ts`. use oxc_allocator::Box; -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashSet; use crate::ast::expression::{ @@ -65,7 +65,7 @@ pub fn parse_extracted_styles(job: &mut ComponentCompilationJob<'_>) { // We collect first because we need to insert new ops and remove old ones for view_xref in &view_xrefs { // Collect ops to process - let ops_to_process: std::vec::Vec<(XrefId, Atom<'_>, String)> = { + let ops_to_process: std::vec::Vec<(XrefId, Ident<'_>, String)> = { let Some(view) = job.view(*view_xref) else { continue; }; @@ -113,7 +113,7 @@ pub fn parse_extracted_styles(job: &mut ComponentCompilationJob<'_>) { LiteralPrimitive { span: ParseSpan::new(0, 0), source_span: AbsoluteSourceSpan::new(0, 0), - value: LiteralValue::String(Atom::from( + value: LiteralValue::String(Ident::from( allocator.alloc_str(&prop_value), )), }, @@ -129,7 +129,7 @@ pub fn parse_extracted_styles(job: &mut ComponentCompilationJob<'_>) { target, binding_kind: BindingKind::StyleProperty, namespace: None, - name: Atom::from(allocator.alloc_str(&prop_name)), + name: Ident::from(allocator.alloc_str(&prop_name)), value: Some(value_expr), security_context: SecurityContext::Style, truthy_expression: false, @@ -159,7 +159,7 @@ pub fn parse_extracted_styles(job: &mut ComponentCompilationJob<'_>) { target, binding_kind: BindingKind::ClassName, namespace: None, - name: Atom::from(allocator.alloc_str(&class_name)), + name: Ident::from(allocator.alloc_str(&class_name)), value: None, security_context: SecurityContext::None, truthy_expression: false, @@ -351,7 +351,7 @@ pub fn parse_extracted_styles_for_host(job: &mut HostBindingCompilationJob<'_>) let allocator = job.allocator; // Collect style/class attributes to parse - let ops_to_process: std::vec::Vec<(XrefId, Atom<'_>, String)> = job + let ops_to_process: std::vec::Vec<(XrefId, Ident<'_>, String)> = job .root .create .iter() @@ -386,7 +386,7 @@ pub fn parse_extracted_styles_for_host(job: &mut HostBindingCompilationJob<'_>) LiteralPrimitive { span: ParseSpan::new(0, 0), source_span: AbsoluteSourceSpan::new(0, 0), - value: LiteralValue::String(Atom::from( + value: LiteralValue::String(Ident::from( allocator.alloc_str(&prop_value), )), }, @@ -402,7 +402,7 @@ pub fn parse_extracted_styles_for_host(job: &mut HostBindingCompilationJob<'_>) target, binding_kind: BindingKind::StyleProperty, namespace: None, - name: Atom::from(allocator.alloc_str(&prop_name)), + name: Ident::from(allocator.alloc_str(&prop_name)), value: Some(value_expr), security_context: SecurityContext::Style, truthy_expression: false, @@ -429,7 +429,7 @@ pub fn parse_extracted_styles_for_host(job: &mut HostBindingCompilationJob<'_>) target, binding_kind: BindingKind::ClassName, namespace: None, - name: Atom::from(allocator.alloc_str(&class_name)), + name: Ident::from(allocator.alloc_str(&class_name)), value: None, security_context: SecurityContext::None, truthy_expression: false, diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/pipe_creation.rs b/crates/oxc_angular_compiler/src/pipeline/phases/pipe_creation.rs index 428d89ca3..1707442a1 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/pipe_creation.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/pipe_creation.rs @@ -212,7 +212,7 @@ struct PipeInfo<'a> { /// This is preserved from the expression to match Angular's behavior. target_slot: SlotHandle, /// Pipe name. - name: oxc_span::Atom<'a>, + name: oxc_span::Ident<'a>, /// Number of arguments (including the input expression). num_args: u32, /// Target element xref (for compatibility mode insertion ordering). diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/pure_literal_structures.rs b/crates/oxc_angular_compiler/src/pipeline/phases/pure_literal_structures.rs index a9eead152..476de4c13 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/pure_literal_structures.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/pure_literal_structures.rs @@ -292,21 +292,21 @@ fn create_pure_function_for_ir_array<'a>( /// Create a PureFunctionExpr for a literal map with IR expression values. fn create_pure_function_for_ir_map<'a>( - keys: &oxc_allocator::Vec<'a, oxc_span::Atom<'a>>, + keys: &oxc_allocator::Vec<'a, oxc_span::Ident<'a>>, values: &oxc_allocator::Vec<'a, IrExpression<'a>>, quoted: &oxc_allocator::Vec<'a, bool>, allocator: &'a oxc_allocator::Allocator, expressions: &ExpressionStore<'a>, ) -> Option> { let mut args: AllocVec<'a, IrExpression<'a>> = AllocVec::new_in(allocator); - let mut body_keys: AllocVec<'a, oxc_span::Atom<'a>> = AllocVec::new_in(allocator); + let mut body_keys: AllocVec<'a, oxc_span::Ident<'a>> = AllocVec::new_in(allocator); let mut body_values: AllocVec<'a, IrExpression<'a>> = AllocVec::new_in(allocator); let mut body_quoted: AllocVec<'a, bool> = AllocVec::new_in(allocator); let mut param_index: u32 = 0; for (i, value) in values.iter().enumerate() { // Get key and quoted from the arrays - let key = keys.get(i).cloned().unwrap_or_else(|| oxc_span::Atom::from("")); + let key = keys.get(i).cloned().unwrap_or_else(|| oxc_span::Ident::from("")); let is_quoted = quoted.get(i).copied().unwrap_or(false); body_keys.push(key); body_quoted.push(is_quoted); @@ -399,7 +399,7 @@ fn create_pure_function_for_map<'a>( allocator: &'a oxc_allocator::Allocator, ) -> Option> { let mut args: AllocVec<'a, IrExpression<'a>> = AllocVec::new_in(allocator); - let mut body_keys: AllocVec<'a, oxc_span::Atom<'a>> = AllocVec::new_in(allocator); + let mut body_keys: AllocVec<'a, oxc_span::Ident<'a>> = AllocVec::new_in(allocator); let mut body_values: AllocVec<'a, IrExpression<'a>> = AllocVec::new_in(allocator); let mut body_quoted: AllocVec<'a, bool> = AllocVec::new_in(allocator); let mut param_index: u32 = 0; @@ -417,7 +417,7 @@ fn create_pure_function_for_map<'a>( None // Skip spread keys } }) - .unwrap_or_else(|| (oxc_span::Atom::from(""), false)); + .unwrap_or_else(|| (oxc_span::Ident::from(""), false)); body_keys.push(key); body_quoted.push(quoted); diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/regular_expression_optimization.rs b/crates/oxc_angular_compiler/src/pipeline/phases/regular_expression_optimization.rs index 9913a22e2..cb7e6f79f 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/regular_expression_optimization.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/regular_expression_optimization.rs @@ -7,7 +7,7 @@ //! Ported from Angular's `template/pipeline/src/phases/regular_expression_optimization.ts`. use oxc_allocator::Box; -use oxc_span::Atom; +use oxc_span::Ident; use crate::ast::expression::AngularExpression; use crate::ir::expression::IrExpression; @@ -32,7 +32,7 @@ pub fn optimize_regular_expressions(job: &mut ComponentCompilationJob<'_>) { // First pass: Pool regexes and collect their names for replacement // We need to collect regex info and pool them first, then do the replacement - let mut regex_replacements: Vec<(String, Option, Atom<'_>)> = Vec::new(); + let mut regex_replacements: Vec<(String, Option, Ident<'_>)> = Vec::new(); // Collect and pool all non-global regexes from root view using split borrow { @@ -86,7 +86,7 @@ pub fn optimize_regular_expressions(job: &mut ComponentCompilationJob<'_>) { } /// Check if regex flags contain the global flag. -fn is_global_regex(flags: Option<&oxc_span::Atom<'_>>) -> bool { +fn is_global_regex(flags: Option<&oxc_span::Ident<'_>>) -> bool { flags.map_or(false, |f| f.contains('g')) } @@ -99,7 +99,7 @@ fn make_regex_key(body: &str, flags: Option<&str>) -> String { fn collect_and_pool_regexes_from_create_op<'a>( op: &CreateOp<'a>, pool: &mut crate::pipeline::constant_pool::ConstantPool<'a>, - replacements: &mut Vec<(String, Option, Atom<'a>)>, + replacements: &mut Vec<(String, Option, Ident<'a>)>, ) { match op { CreateOp::Variable(var) => { @@ -133,7 +133,7 @@ fn collect_and_pool_regexes_from_create_op<'a>( fn collect_and_pool_regexes_from_update_op<'a>( op: &UpdateOp<'a>, pool: &mut crate::pipeline::constant_pool::ConstantPool<'a>, - replacements: &mut Vec<(String, Option, Atom<'a>)>, + replacements: &mut Vec<(String, Option, Ident<'a>)>, ) { match op { UpdateOp::Property(prop) => { @@ -162,7 +162,7 @@ fn collect_and_pool_regexes_from_update_op<'a>( fn collect_and_pool_from_expr<'a>( expr: &IrExpression<'a>, pool: &mut crate::pipeline::constant_pool::ConstantPool<'a>, - replacements: &mut Vec<(String, Option, Atom<'a>)>, + replacements: &mut Vec<(String, Option, Ident<'a>)>, ) { if let IrExpression::Ast(ast_expr) = expr { if let AngularExpression::RegularExpressionLiteral(regex) = ast_expr.as_ref() { @@ -191,7 +191,7 @@ fn collect_and_pool_from_expr<'a>( fn transform_regexes_in_create_op<'a>( op: &mut CreateOp<'a>, allocator: &'a oxc_allocator::Allocator, - replacements: &[(String, Option, Atom<'a>)], + replacements: &[(String, Option, Ident<'a>)], ) { match op { CreateOp::Variable(var) => { @@ -225,7 +225,7 @@ fn transform_regexes_in_create_op<'a>( fn transform_regexes_in_update_op<'a>( op: &mut UpdateOp<'a>, allocator: &'a oxc_allocator::Allocator, - replacements: &[(String, Option, Atom<'a>)], + replacements: &[(String, Option, Ident<'a>)], ) { match op { UpdateOp::Property(prop) => { @@ -254,7 +254,7 @@ fn transform_regexes_in_update_op<'a>( fn transform_expr<'a>( expr: &mut IrExpression<'a>, allocator: &'a oxc_allocator::Allocator, - replacements: &[(String, Option, Atom<'a>)], + replacements: &[(String, Option, Ident<'a>)], ) { if let IrExpression::Ast(ast_expr) = expr { if let AngularExpression::RegularExpressionLiteral(regex) = ast_expr.as_ref() { @@ -285,7 +285,7 @@ fn transform_expr<'a>( /// Host version - only processes the root unit (no embedded views). pub fn optimize_regular_expressions_for_host(job: &mut HostBindingCompilationJob<'_>) { let allocator = job.allocator; - let mut regex_replacements: Vec<(String, Option, Atom<'_>)> = Vec::new(); + let mut regex_replacements: Vec<(String, Option, Ident<'_>)> = Vec::new(); // First pass: Pool regexes from root unit { diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/reify/angular_expression.rs b/crates/oxc_angular_compiler/src/pipeline/phases/reify/angular_expression.rs index 22c776241..269588e90 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/reify/angular_expression.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/reify/angular_expression.rs @@ -3,7 +3,7 @@ use std::cell::RefCell; use oxc_allocator::{Box, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::ast::expression::AngularExpression; use crate::ir::ops::XrefId; @@ -29,13 +29,13 @@ impl<'a> SafeConversionContext<'a> { } /// Allocates a new temporary variable name. - fn allocate_temp_name(&self) -> Atom<'a> { + fn allocate_temp_name(&self) -> Ident<'a> { let mut count = self.temp_count.borrow_mut(); // Use op_index 0 since angular_expression conversion happens during reify // The actual op_index will be adjusted by temporary_variables phase if needed let name = format!("tmp_0_{}", *count); *count += 1; - Atom::from(self.allocator.alloc_str(&name)) + Ident::from(self.allocator.alloc_str(&name)) } } @@ -130,7 +130,7 @@ fn convert_angular_expression_with_ctx<'a>( // Implicit receiver becomes ctx OutputExpression::ReadVar(Box::new_in( ReadVarExpr { - name: Atom::from("ctx"), + name: Ident::from("ctx"), source_span: Some(ir.source_span.to_span()), }, allocator, @@ -141,7 +141,7 @@ fn convert_angular_expression_with_ctx<'a>( // This receiver becomes ctx OutputExpression::ReadVar(Box::new_in( ReadVarExpr { - name: Atom::from("ctx"), + name: Ident::from("ctx"), source_span: Some(tr.source_span.to_span()), }, allocator, @@ -155,7 +155,7 @@ fn convert_angular_expression_with_ctx<'a>( if matches!(&prop.receiver, AngularExpression::ImplicitReceiver(_)) { return OutputExpression::ReadVar(Box::new_in( ReadVarExpr { - name: Atom::from("$event"), + name: Ident::from("$event"), source_span: Some(prop.source_span.to_span()), }, allocator, @@ -733,7 +733,7 @@ fn convert_angular_expression_with_ctx<'a>( } else { OutputExpression::Literal(Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from("")), + value: LiteralValue::String(Ident::from("")), source_span: Some(interp.source_span.to_span()), }, allocator, diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/reify/ir_expression.rs b/crates/oxc_angular_compiler/src/pipeline/phases/reify/ir_expression.rs index 1e98084c7..3ff0e6160 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/reify/ir_expression.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/reify/ir_expression.rs @@ -1,7 +1,7 @@ //! IR expression to Output AST conversion. use oxc_allocator::{Box, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::ast::expression::AngularExpression; use crate::ir::expression::{IrExpression, TwoWayBindingSetExpr}; @@ -39,7 +39,7 @@ pub fn convert_ir_expression<'a>( // Fallback to a debuggable name using xref (should not happen after naming phase) let fallback = format!("_unnamed_{}", var.xref.0); let fallback_str = allocator.alloc_str(&fallback); - Atom::from(fallback_str) + Ident::from(fallback_str) } }; OutputExpression::ReadVar(Box::new_in( @@ -52,7 +52,7 @@ pub fn convert_ir_expression<'a>( // Reference to the component context // This becomes `ctx` in the generated code OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ctx"), source_span: ctx.source_span }, + ReadVarExpr { name: Ident::from("ctx"), source_span: ctx.source_span }, allocator, )) } @@ -73,7 +73,7 @@ pub fn convert_ir_expression<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("ctx"), source_span: None }, + ReadVarExpr { name: Ident::from("ctx"), source_span: None }, allocator, )), allocator, @@ -150,7 +150,7 @@ pub fn convert_ir_expression<'a>( crate::ir::expression::RestoreViewTarget::Static(_) => { // Fallback: use _r if not resolved (shouldn't happen in correct flow) args.push(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("_r"), source_span: None }, + ReadVarExpr { name: Ident::from("_r"), source_span: None }, allocator, ))); } @@ -315,14 +315,14 @@ pub fn convert_ir_expression<'a>( IrExpression::TrackContext(_) => { // Reference to `this` for track functions OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("this"), source_span: None }, + ReadVarExpr { name: Ident::from("this"), source_span: None }, allocator, )) } IrExpression::ReadTemporary(tmp) => { // Read a temporary variable - let var_name = tmp.name.clone().unwrap_or_else(|| Atom::from("_tmp")); + let var_name = tmp.name.clone().unwrap_or_else(|| Ident::from("_tmp")); OutputExpression::ReadVar(Box::new_in( ReadVarExpr { name: var_name, source_span: None }, allocator, @@ -331,7 +331,7 @@ pub fn convert_ir_expression<'a>( IrExpression::AssignTemporary(assign) => { // Assign to a temporary variable: _tmp = expr - let var_name = assign.name.clone().unwrap_or_else(|| Atom::from("_tmp")); + let var_name = assign.name.clone().unwrap_or_else(|| Ident::from("_tmp")); let value = convert_ir_expression(allocator, &assign.expr, expressions, root_xref); OutputExpression::BinaryOperator(Box::new_in( BinaryOperatorExpr { @@ -820,7 +820,7 @@ pub fn convert_ir_expression<'a>( IrExpression::LiteralMap(map) => { let mut entries = OxcVec::with_capacity_in(map.values.len(), allocator); for (i, value) in map.values.iter().enumerate() { - let key = map.keys.get(i).cloned().unwrap_or_else(|| Atom::from("")); + let key = map.keys.get(i).cloned().unwrap_or_else(|| Ident::from("")); let quoted = map.quoted.get(i).copied().unwrap_or(false); let converted_value = convert_ir_expression(allocator, value, expressions, root_xref); @@ -846,7 +846,7 @@ pub fn convert_ir_expression<'a>( IrExpression::DerivedLiteralMap(map) => { let mut entries = OxcVec::with_capacity_in(map.values.len(), allocator); for (i, value) in map.values.iter().enumerate() { - let key = map.keys.get(i).cloned().unwrap_or_else(|| Atom::from("")); + let key = map.keys.get(i).cloned().unwrap_or_else(|| Ident::from("")); let quoted = map.quoted.get(i).copied().unwrap_or(false); let converted_value = convert_ir_expression(allocator, value, expressions, root_xref); diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/reify/mod.rs b/crates/oxc_angular_compiler/src/pipeline/phases/reify/mod.rs index 7cb143a9e..15551c736 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/reify/mod.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/reify/mod.rs @@ -15,7 +15,7 @@ mod utils; use oxc_allocator::{Box, Vec as OxcVec}; use oxc_diagnostics::OxcDiagnostic; -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; use crate::ast::expression::AngularExpression; @@ -103,7 +103,7 @@ fn convert_output_expr_ir_nodes<'a>( /// Context for reifying operations, holding information needed across views. struct ReifyContext<'a> { /// Map from view xref to its function name (set by naming phase). - view_fn_names: FxHashMap>, + view_fn_names: FxHashMap>, /// Map from view xref to its declaration count. view_decls: FxHashMap, /// Map from view xref to its variable count. @@ -1398,7 +1398,7 @@ fn reify_track_by<'a>( expressions: &ExpressionStore<'a>, root_xref: XrefId, track: &IrExpression<'a>, - track_fn_name: Option<&Atom<'a>>, + track_fn_name: Option<&Ident<'a>>, uses_component_instance: bool, track_by_ops: &mut Option>>, diagnostics: &mut Vec, @@ -1411,7 +1411,7 @@ fn reify_track_by<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, @@ -1428,14 +1428,14 @@ fn reify_track_by<'a>( // Allocate the parts into the arena first let receiver_name_str = allocator.alloc_str(&fn_name[..dot_pos]); let prop_name_str = allocator.alloc_str(&fn_name[dot_pos + 1..]); - let receiver_name = Atom::from(receiver_name_str); - let prop_name = Atom::from(prop_name_str); + let receiver_name = Ident::from(receiver_name_str); + let prop_name = Ident::from(prop_name_str); // Check if receiver ends with "()" indicating a function call if receiver_name.ends_with("()") { // componentInstance().fn pattern let call_name = - Atom::from(allocator.alloc_str(&receiver_name[..receiver_name.len() - 2])); + Ident::from(allocator.alloc_str(&receiver_name[..receiver_name.len() - 2])); return OutputExpression::ReadProp(Box::new_in( ReadPropExpr { receiver: Box::new_in( @@ -1447,7 +1447,7 @@ fn reify_track_by<'a>( receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( ReadVarExpr { - name: Atom::from("i0"), + name: Ident::from("i0"), source_span: None, }, allocator, @@ -1503,8 +1503,8 @@ fn reify_track_by<'a>( // Create the track function with params ($index, $item) let mut params = OxcVec::with_capacity_in(2, allocator); - params.push(FnParam { name: Atom::from("$index") }); - params.push(FnParam { name: Atom::from("$item") }); + params.push(FnParam { name: Ident::from("$index") }); + params.push(FnParam { name: Ident::from("$item") }); let fn_expr = if let Some(track_ops) = track_by_ops { // Complex case: track_by_ops is present (set by track_fn_optimization phase). diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/bindings.rs b/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/bindings.rs index dfbe89c41..e6f63c56b 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/bindings.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/bindings.rs @@ -1,7 +1,7 @@ //! Property, style, class, and attribute binding statement generation. use oxc_allocator::{Box, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::output::ast::{ LiteralExpr, LiteralValue, OutputExpression, OutputStatement, ReadPropExpr, ReadVarExpr, @@ -28,14 +28,14 @@ pub fn is_aria_attribute(name: &str) -> bool { /// DOM properties that need to be remapped on the compiler side. /// Note: this mapping has to be kept in sync with the equally named mapping in the Angular runtime. /// See: Angular's `template/pipeline/src/phases/reify.ts` -fn remap_dom_property<'a>(name: &Atom<'a>) -> Atom<'a> { +fn remap_dom_property<'a>(name: &Ident<'a>) -> Ident<'a> { match name.as_str() { - "class" => Atom::from("className"), - "for" => Atom::from("htmlFor"), - "formaction" => Atom::from("formAction"), - "innerHtml" => Atom::from("innerHTML"), - "readonly" => Atom::from("readOnly"), - "tabindex" => Atom::from("tabIndex"), + "class" => Ident::from("className"), + "for" => Ident::from("htmlFor"), + "formaction" => Ident::from("formAction"), + "innerHtml" => Ident::from("innerHTML"), + "readonly" => Ident::from("readOnly"), + "tabindex" => Ident::from("tabIndex"), _ => name.clone(), } } @@ -46,14 +46,14 @@ fn remap_dom_property<'a>(name: &Atom<'a>) -> Atom<'a> { /// This creates an expression like `i0.ɵɵsanitizeHtml`. fn create_sanitizer_expr<'a>( allocator: &'a oxc_allocator::Allocator, - sanitizer: &Atom<'a>, + sanitizer: &Ident<'a>, ) -> OutputExpression<'a> { // Create: i0.ɵɵsanitize* expression OutputExpression::ReadProp(Box::new_in( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, @@ -69,9 +69,9 @@ fn create_sanitizer_expr<'a>( /// Creates an ɵɵproperty() call statement with expression value. pub fn create_property_stmt_with_expr<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, value: OutputExpression<'a>, - sanitizer: Option<&Atom<'a>>, + sanitizer: Option<&Ident<'a>>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); args.push(OutputExpression::Literal(Box::new_in( @@ -91,7 +91,7 @@ pub fn create_property_stmt_with_expr<'a>( /// that sets the ARIA attribute rather than a DOM property. pub fn create_aria_property_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, value: OutputExpression<'a>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); @@ -106,7 +106,7 @@ pub fn create_aria_property_stmt<'a>( /// Creates a generic binding statement with expression value. pub fn create_binding_stmt_with_expr<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, value: OutputExpression<'a>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); @@ -122,9 +122,9 @@ pub fn create_binding_stmt_with_expr<'a>( /// Creates an ɵɵstyleProp() call statement with expression. pub fn create_style_prop_stmt_with_expr<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, value: OutputExpression<'a>, - unit: Option<&Atom<'a>>, + unit: Option<&Ident<'a>>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); args.push(OutputExpression::Literal(Box::new_in( @@ -145,7 +145,7 @@ pub fn create_style_prop_stmt_with_expr<'a>( /// Creates an ɵɵclassProp() call statement with expression. pub fn create_class_prop_stmt_with_expr<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, value: OutputExpression<'a>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); @@ -163,10 +163,10 @@ pub fn create_class_prop_stmt_with_expr<'a>( /// If sanitizer is None but namespace is Some, emits null for sanitizer. pub fn create_attribute_stmt_with_expr<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, value: OutputExpression<'a>, - sanitizer: Option<&Atom<'a>>, - namespace: Option<&Atom<'a>>, + sanitizer: Option<&Ident<'a>>, + namespace: Option<&Ident<'a>>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); args.push(OutputExpression::Literal(Box::new_in( @@ -198,9 +198,9 @@ pub fn create_attribute_stmt_with_expr<'a>( /// Creates an ɵɵtwoWayProperty() call statement. pub fn create_two_way_property_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, value: OutputExpression<'a>, - sanitizer: Option<&Atom<'a>>, + sanitizer: Option<&Ident<'a>>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); args.push(OutputExpression::Literal(Box::new_in( @@ -221,9 +221,9 @@ pub fn create_two_way_property_stmt<'a>( /// The property name is remapped if necessary (e.g., `for` -> `htmlFor`). pub fn create_dom_property_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, value: OutputExpression<'a>, - sanitizer: Option<&Atom<'a>>, + sanitizer: Option<&Ident<'a>>, ) -> OutputStatement<'a> { let remapped_name = remap_dom_property(name); let mut args = OxcVec::new_in(allocator); @@ -284,10 +284,10 @@ pub fn create_text_interpolate_stmt_with_args<'a>( /// Arguments: name, [s0, v0, s1, v1, ..., sN], [sanitizer] pub fn create_property_interpolate_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, interp_args: OxcVec<'a, OutputExpression<'a>>, expr_count: usize, - sanitizer: Option<&Atom<'a>>, + sanitizer: Option<&Ident<'a>>, ) -> OutputStatement<'a> { // Save length before consuming interp_args — the simple case check must use // the interpolation args count, not the final args count (which includes name @@ -327,11 +327,11 @@ pub fn create_property_interpolate_stmt<'a>( /// Arguments: name, [s0, v0, s1, v1, ..., sN], [sanitizer], [namespace] pub fn create_attribute_interpolate_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, interp_args: OxcVec<'a, OutputExpression<'a>>, expr_count: usize, - sanitizer: Option<&Atom<'a>>, - namespace: Option<&Atom<'a>>, + sanitizer: Option<&Ident<'a>>, + namespace: Option<&Ident<'a>>, ) -> OutputStatement<'a> { // Save length before consuming — same reason as create_property_interpolate_stmt. let interp_args_len = interp_args.len(); @@ -376,9 +376,9 @@ pub fn create_attribute_interpolate_stmt<'a>( /// For Angular 19, host/DomOnly property bindings use `ɵɵhostProperty` instead of `ɵɵdomProperty`. pub fn create_host_property_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, value: OutputExpression<'a>, - sanitizer: Option<&Atom<'a>>, + sanitizer: Option<&Ident<'a>>, ) -> OutputStatement<'a> { let remapped_name = remap_dom_property(name); let mut args = OxcVec::new_in(allocator); @@ -402,10 +402,10 @@ pub fn create_host_property_stmt<'a>( /// Signature: `ɵɵstylePropInterpolateN(prop, s0, v0, ..., [unit])` pub fn create_style_prop_interpolate_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, interp_args: OxcVec<'a, OutputExpression<'a>>, expr_count: usize, - unit: Option<&Atom<'a>>, + unit: Option<&Ident<'a>>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); // First arg: style property name diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/control_flow.rs b/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/control_flow.rs index 1ae1ea7ac..d3a6d1a09 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/control_flow.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/control_flow.rs @@ -1,7 +1,7 @@ //! Control flow and variable statement generation. use oxc_allocator::{Box, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::output::ast::{ DeclareVarStmt, LiteralExpr, LiteralValue, OutputExpression, OutputStatement, ReadPropExpr, @@ -27,10 +27,10 @@ use super::super::utils::create_instruction_call_stmt; pub fn create_conditional_create_stmt<'a>( allocator: &'a oxc_allocator::Allocator, slot: u32, - fn_name: Option>, + fn_name: Option>, decls: Option, vars: Option, - tag: Option<&Atom<'a>>, + tag: Option<&Ident<'a>>, attributes: Option, local_refs_index: Option, ) -> OutputStatement<'a> { @@ -52,7 +52,7 @@ pub fn create_conditional_create_stmt<'a>( // Fallback placeholder let placeholder_str = allocator.alloc_str(&format!("_c{slot}")); args.push(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(placeholder_str), source_span: None }, + ReadVarExpr { name: Ident::from(placeholder_str), source_span: None }, allocator, ))); } @@ -109,12 +109,12 @@ pub fn create_conditional_create_stmt<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::TEMPLATE_REF_EXTRACTOR), + name: Ident::from(Identifiers::TEMPLATE_REF_EXTRACTOR), optional: false, source_span: None, }, @@ -168,10 +168,10 @@ pub fn create_conditional_update_stmt<'a>( pub fn create_conditional_branch_create_stmt<'a>( allocator: &'a oxc_allocator::Allocator, slot: u32, - fn_name: Option>, + fn_name: Option>, decls: Option, vars: Option, - tag: Option<&Atom<'a>>, + tag: Option<&Ident<'a>>, attributes: Option, local_refs_index: Option, ) -> OutputStatement<'a> { @@ -193,7 +193,7 @@ pub fn create_conditional_branch_create_stmt<'a>( // Fallback placeholder let placeholder_str = allocator.alloc_str(&format!("_c{slot}")); args.push(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(placeholder_str), source_span: None }, + ReadVarExpr { name: Ident::from(placeholder_str), source_span: None }, allocator, ))); } @@ -250,12 +250,12 @@ pub fn create_conditional_branch_create_stmt<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::TEMPLATE_REF_EXTRACTOR), + name: Ident::from(Identifiers::TEMPLATE_REF_EXTRACTOR), optional: false, source_span: None, }, @@ -324,17 +324,17 @@ pub fn create_repeater_stmt<'a>( pub fn create_repeater_create_stmt_with_track_expr<'a>( allocator: &'a oxc_allocator::Allocator, slot: u32, - fn_name: Option>, + fn_name: Option>, body_decl_count: Option, body_var_count: Option, - tag: Option<&Atom<'a>>, + tag: Option<&Ident<'a>>, attributes: Option, track_fn_expr: OutputExpression<'a>, uses_component_instance: bool, - empty_fn_name: Option>, + empty_fn_name: Option>, empty_decls: Option, empty_vars: Option, - empty_tag: Option<&Atom<'a>>, + empty_tag: Option<&Ident<'a>>, empty_attributes: Option, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); @@ -355,7 +355,7 @@ pub fn create_repeater_create_stmt_with_track_expr<'a>( // Fallback placeholder let placeholder_str = allocator.alloc_str(&format!("_r{slot}")); args.push(OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(placeholder_str), source_span: None }, + ReadVarExpr { name: Ident::from(placeholder_str), source_span: None }, allocator, ))); } @@ -476,7 +476,7 @@ pub fn create_repeater_create_stmt_with_track_expr<'a>( /// All Variable ops use `const` (StmtModifier::Final), matching Angular's reify.ts. pub fn create_variable_decl_stmt_with_value<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, value: OutputExpression<'a>, ) -> OutputStatement<'a> { OutputStatement::DeclareVar(Box::new_in( @@ -524,7 +524,7 @@ mod tests { let stmt = create_conditional_create_stmt( &allocator, 0, - Some(Atom::from("TestComponent_Conditional_0_Template")), + Some(Ident::from("TestComponent_Conditional_0_Template")), Some(1), Some(0), None, @@ -553,7 +553,7 @@ mod tests { let stmt = create_conditional_create_stmt( &allocator, 0, - Some(Atom::from("TestComponent_Conditional_0_Template")), + Some(Ident::from("TestComponent_Conditional_0_Template")), Some(1), Some(0), None, @@ -575,7 +575,7 @@ mod tests { let stmt = create_conditional_branch_create_stmt( &allocator, 1, - Some(Atom::from("TestComponent_Conditional_1_Template")), + Some(Ident::from("TestComponent_Conditional_1_Template")), Some(1), Some(0), None, @@ -602,7 +602,7 @@ mod tests { let stmt = create_conditional_branch_create_stmt( &allocator, 1, - Some(Atom::from("TestComponent_Conditional_1_Template")), + Some(Ident::from("TestComponent_Conditional_1_Template")), Some(1), Some(0), None, diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/defer.rs b/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/defer.rs index 298973d90..87a7985c8 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/defer.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/defer.rs @@ -1,7 +1,7 @@ //! @defer block statement generation. use oxc_allocator::{Box, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::ir::enums::{DeferOpModifierKind, DeferTriggerKind}; use crate::output::ast::{ @@ -141,12 +141,12 @@ pub fn create_defer_stmt<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::DEFER_ENABLE_TIMER_SCHEDULING), + name: Ident::from(Identifiers::DEFER_ENABLE_TIMER_SCHEDULING), optional: false, source_span: None, }, diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/elements.rs b/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/elements.rs index 7bf8331ee..09d5636cf 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/elements.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/elements.rs @@ -1,7 +1,7 @@ //! Element, container, template, and text statement generation. use oxc_allocator::{Box, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::output::ast::{ LiteralExpr, LiteralValue, OutputExpression, OutputStatement, ReadPropExpr, ReadVarExpr, @@ -16,7 +16,7 @@ use super::super::utils::create_instruction_call_stmt; /// If localRefIndex is present, constIndex must also be present (even if null). pub fn create_element_args<'a>( allocator: &'a oxc_allocator::Allocator, - tag: &Atom<'a>, + tag: &Ident<'a>, slot: u32, attributes: Option, local_refs_index: Option, @@ -64,7 +64,7 @@ pub fn create_element_args<'a>( /// Creates an ɵɵelementStart() call statement. pub fn create_element_start_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - tag: &Atom<'a>, + tag: &Ident<'a>, slot: u32, attributes: Option, local_refs_index: Option, @@ -76,7 +76,7 @@ pub fn create_element_start_stmt<'a>( /// Creates an ɵɵelement() call statement. pub fn create_element_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - tag: &Atom<'a>, + tag: &Ident<'a>, slot: u32, attributes: Option, local_refs_index: Option, @@ -100,7 +100,7 @@ pub fn create_element_end_stmt<'a>(allocator: &'a oxc_allocator::Allocator) -> O /// This is an optimized version that skips directive matching at runtime. pub fn create_dom_element_start_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - tag: &Atom<'a>, + tag: &Ident<'a>, slot: u32, attributes: Option, local_refs_index: Option, @@ -115,7 +115,7 @@ pub fn create_dom_element_start_stmt<'a>( /// This is an optimized version that skips directive matching at runtime. pub fn create_dom_element_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - tag: &Atom<'a>, + tag: &Ident<'a>, slot: u32, attributes: Option, local_refs_index: Option, @@ -175,10 +175,10 @@ pub fn create_text_stmt<'a>( pub fn create_template_stmt<'a>( allocator: &'a oxc_allocator::Allocator, slot: u32, - fn_name: Option>, + fn_name: Option>, decls: Option, vars: Option, - tag: Option<&Atom<'a>>, + tag: Option<&Ident<'a>>, attributes: Option, local_refs_index: Option, ) -> OutputStatement<'a> { @@ -202,10 +202,10 @@ pub fn create_template_stmt<'a>( fn create_template_args<'a>( allocator: &'a oxc_allocator::Allocator, slot: u32, - fn_name: Option>, + fn_name: Option>, decls: Option, vars: Option, - tag: Option<&Atom<'a>>, + tag: Option<&Ident<'a>>, attributes: Option, local_refs_index: Option, ) -> OxcVec<'a, OutputExpression<'a>> { @@ -225,7 +225,7 @@ fn create_template_args<'a>( ))); } else { let placeholder_str = allocator.alloc_str(&format!("_r{slot}")); - let placeholder = Atom::from(placeholder_str); + let placeholder = Ident::from(placeholder_str); args.push(OutputExpression::ReadVar(Box::new_in( ReadVarExpr { name: placeholder, source_span: None }, allocator, @@ -283,12 +283,12 @@ fn create_template_args<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(Identifiers::TEMPLATE_REF_EXTRACTOR), + name: Ident::from(Identifiers::TEMPLATE_REF_EXTRACTOR), optional: false, source_span: None, }, @@ -326,10 +326,10 @@ fn create_template_args<'a>( pub fn create_dom_template_stmt<'a>( allocator: &'a oxc_allocator::Allocator, slot: u32, - fn_name: Option>, + fn_name: Option>, decls: Option, vars: Option, - tag: Option<&Atom<'a>>, + tag: Option<&Ident<'a>>, attributes: Option, local_refs_index: Option, ) -> OutputStatement<'a> { diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/misc.rs b/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/misc.rs index 97a665de8..3ec096d44 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/misc.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/reify/statements/misc.rs @@ -1,7 +1,7 @@ //! Miscellaneous statement generation (listener, animation, pipe, projection, etc.). use oxc_allocator::{Box, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::ir::enums::AnimationKind; use crate::output::ast::{ @@ -55,11 +55,11 @@ impl GlobalEventTarget { /// The `consumes_dollar_event` parameter controls whether the `$event` parameter is included. pub fn create_listener_stmt_with_handler<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, handler_stmts: OxcVec<'a, OutputStatement<'a>>, event_target: Option, use_capture: bool, - handler_fn_name: Option<&Atom<'a>>, + handler_fn_name: Option<&Ident<'a>>, consumes_dollar_event: bool, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); @@ -72,7 +72,7 @@ pub fn create_listener_stmt_with_handler<'a>( // Handler function: function name($event) { ... } or function name() { ... } let mut params = OxcVec::new_in(allocator); if consumes_dollar_event { - params.push(FnParam { name: Atom::from("$event") }); + params.push(FnParam { name: Ident::from("$event") }); } let handler_fn = OutputExpression::Function(Box::new_in( @@ -94,14 +94,14 @@ pub fn create_listener_stmt_with_handler<'a>( receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( crate::output::ast::ReadVarExpr { - name: Atom::from("i0"), + name: Ident::from("i0"), source_span: None, }, allocator, )), allocator, ), - name: Atom::from(target.resolver_instruction()), + name: Ident::from(target.resolver_instruction()), optional: false, source_span: None, }, @@ -135,10 +135,10 @@ pub fn create_listener_stmt_with_handler<'a>( /// The `consumes_dollar_event` parameter controls whether the `$event` parameter is included. pub fn create_dom_listener_stmt_with_handler<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, handler_stmts: OxcVec<'a, OutputStatement<'a>>, event_target: Option, - handler_fn_name: Option<&Atom<'a>>, + handler_fn_name: Option<&Ident<'a>>, consumes_dollar_event: bool, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); @@ -151,7 +151,7 @@ pub fn create_dom_listener_stmt_with_handler<'a>( // Handler function: function name($event) { ... } or function name() { ... } let mut params = OxcVec::new_in(allocator); if consumes_dollar_event { - params.push(FnParam { name: Atom::from("$event") }); + params.push(FnParam { name: Ident::from("$event") }); } let handler_fn = OutputExpression::Function(Box::new_in( @@ -173,14 +173,14 @@ pub fn create_dom_listener_stmt_with_handler<'a>( receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( crate::output::ast::ReadVarExpr { - name: Atom::from("i0"), + name: Ident::from("i0"), source_span: None, }, allocator, )), allocator, ), - name: Atom::from(target.resolver_instruction()), + name: Ident::from(target.resolver_instruction()), optional: false, source_span: None, }, @@ -196,9 +196,9 @@ pub fn create_dom_listener_stmt_with_handler<'a>( /// The `handler_fn_name` parameter sets the name of the handler function expression. pub fn create_two_way_listener_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, handler_stmts: OxcVec<'a, OutputStatement<'a>>, - handler_fn_name: Option<&Atom<'a>>, + handler_fn_name: Option<&Ident<'a>>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); // Event name (typically "{property}Change") @@ -210,7 +210,7 @@ pub fn create_two_way_listener_stmt<'a>( // Handler function: function name($event) { ... } // Two-way listeners always consume $event since they need the new value let mut params = OxcVec::new_in(allocator); - params.push(FnParam { name: Atom::from("$event") }); + params.push(FnParam { name: Ident::from("$event") }); let handler_fn = OutputExpression::Function(Box::new_in( FunctionExpr { @@ -236,10 +236,10 @@ pub fn create_two_way_listener_stmt<'a>( /// The `consumes_dollar_event` parameter controls whether the `$event` parameter is included. pub fn create_animation_listener_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, phase: crate::ir::enums::AnimationKind, handler_stmts: OxcVec<'a, OutputStatement<'a>>, - handler_fn_name: Option<&Atom<'a>>, + handler_fn_name: Option<&Ident<'a>>, consumes_dollar_event: bool, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); @@ -252,14 +252,14 @@ pub fn create_animation_listener_stmt<'a>( }; let full_name = allocator.alloc_str(&format!("@{}.{}", name.as_str(), phase_str)); args.push(OutputExpression::Literal(Box::new_in( - LiteralExpr { value: LiteralValue::String(Atom::from(full_name)), source_span: None }, + LiteralExpr { value: LiteralValue::String(Ident::from(full_name)), source_span: None }, allocator, ))); // Handler function: function name($event) { ... } or function name() { ... } let mut params = OxcVec::new_in(allocator); if consumes_dollar_event { - params.push(FnParam { name: Atom::from("$event") }); + params.push(FnParam { name: Ident::from("$event") }); } let handler_fn = OutputExpression::Function(Box::new_in( @@ -300,7 +300,7 @@ pub fn create_animation_string_stmt<'a>( /// It emits `syntheticHostProperty(name, value)`. pub fn create_animation_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, value: OutputExpression<'a>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); @@ -308,7 +308,7 @@ pub fn create_animation_stmt<'a>( // Animation name with @ prefix let full_name = allocator.alloc_str(&format!("@{}", name.as_str())); args.push(OutputExpression::Literal(Box::new_in( - LiteralExpr { value: LiteralValue::String(Atom::from(full_name)), source_span: None }, + LiteralExpr { value: LiteralValue::String(Ident::from(full_name)), source_span: None }, allocator, ))); args.push(value); @@ -334,7 +334,7 @@ pub fn create_animation_op_stmt<'a>( allocator: &'a oxc_allocator::Allocator, animation_kind: AnimationKind, handler_stmts: OxcVec<'a, OutputStatement<'a>>, - handler_fn_name: Option<&Atom<'a>>, + handler_fn_name: Option<&Ident<'a>>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); @@ -363,7 +363,7 @@ pub fn create_animation_op_stmt<'a>( /// Creates an animation binding call statement. pub fn create_animation_binding_stmt<'a>( allocator: &'a oxc_allocator::Allocator, - name: &Atom<'a>, + name: &Ident<'a>, value: OutputExpression<'a>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); @@ -387,7 +387,7 @@ pub fn create_animation_binding_stmt<'a>( pub fn create_control_stmt<'a>( allocator: &'a oxc_allocator::Allocator, value: OutputExpression<'a>, - name: &Atom<'a>, + name: &Ident<'a>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); args.push(value); @@ -438,7 +438,7 @@ pub fn create_enable_bindings_stmt<'a>( pub fn create_pipe_stmt<'a>( allocator: &'a oxc_allocator::Allocator, slot: u32, - name: &Atom<'a>, + name: &Ident<'a>, ) -> OutputStatement<'a> { let mut args = OxcVec::new_in(allocator); args.push(OutputExpression::Literal(Box::new_in( diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/reify/utils.rs b/crates/oxc_angular_compiler/src/pipeline/phases/reify/utils.rs index a9e958e80..f32bd20fc 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/reify/utils.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/reify/utils.rs @@ -1,7 +1,7 @@ //! Shared utilities for the reify phase. use oxc_allocator::{Box, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::output::ast::{ ExpressionStatement, InvokeFunctionExpr, OutputExpression, OutputStatement, ReadPropExpr, @@ -10,9 +10,9 @@ use crate::output::ast::{ /// Strips a prefix from a binding name if present. /// For example: "class.active" -> "active", "style.color" -> "color", "attr.aria-label" -> "aria-label" -pub fn strip_prefix<'a>(name: &Atom<'a>, prefix: &str) -> Atom<'a> { +pub fn strip_prefix<'a>(name: &Ident<'a>, prefix: &str) -> Ident<'a> { if name.as_str().starts_with(prefix) { - Atom::from(&name.as_str()[prefix.len()..]) + Ident::from(&name.as_str()[prefix.len()..]) } else { name.clone() } @@ -29,12 +29,12 @@ pub fn create_instruction_call_stmt<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(instruction), + name: Ident::from(instruction), optional: false, source_span: None, }, @@ -68,12 +68,12 @@ pub fn create_instruction_call_expr<'a>( ReadPropExpr { receiver: Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("i0"), source_span: None }, + ReadVarExpr { name: Ident::from("i0"), source_span: None }, allocator, )), allocator, ), - name: Atom::from(instruction), + name: Ident::from(instruction), optional: false, source_span: None, }, diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/remove_illegal_let_references.rs b/crates/oxc_angular_compiler/src/pipeline/phases/remove_illegal_let_references.rs index db2167f6d..0b34acb38 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/remove_illegal_let_references.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/remove_illegal_let_references.rs @@ -18,7 +18,7 @@ //! Ported from Angular's `template/pipeline/src/phases/remove_illegal_let_references.ts`. use oxc_allocator::Box; -use oxc_span::Atom; +use oxc_span::Ident; use crate::ast::expression::{ AbsoluteSourceSpan, AngularExpression, LiteralPrimitive, LiteralValue, ParseSpan, @@ -41,7 +41,7 @@ pub fn remove_illegal_let_references(job: &mut ComponentCompilationJob<'_>) { for view_xref in view_xrefs { // First pass: collect let variable names from Variable ops - let let_names: Vec> = { + let let_names: Vec> = { let view = match job.view(view_xref) { Some(v) => v, None => continue, @@ -77,7 +77,7 @@ pub fn remove_illegal_let_references(job: &mut ComponentCompilationJob<'_>) { // A let name is "declared" AFTER we finish transforming its Variable op. // This matches Angular which walks backward from the declaration op itself, // replacing self-references (e.g. `@let x = x + 1`) with `undefined`. - let mut declared_names: Vec> = Vec::new(); + let mut declared_names: Vec> = Vec::new(); for op in view.update.iter_mut() { // Check if this op declares a let variable — extract the name before diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/resolve_i18n_element_placeholders.rs b/crates/oxc_angular_compiler/src/pipeline/phases/resolve_i18n_element_placeholders.rs index 1543556ad..73bf72e58 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/resolve_i18n_element_placeholders.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/resolve_i18n_element_placeholders.rs @@ -4,7 +4,7 @@ //! //! Ported from Angular's `template/pipeline/src/phases/resolve_i18n_element_placeholders.ts`. -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; use crate::ir::enums::{I18nParamValueFlags, TemplateKind}; @@ -564,7 +564,7 @@ enum OpInfo<'a> { ElementStart { slot: Option, /// The start placeholder name (e.g., "START_TAG_DIV"). - start_name: Atom<'a>, + start_name: Ident<'a>, context_xref: XrefId, sub_template_index: Option, pending_structural: Option, @@ -574,7 +574,7 @@ enum OpInfo<'a> { ElementEnd { slot: Option, /// The close placeholder name (e.g., "CLOSE_TAG_DIV"). - close_name: Atom<'a>, + close_name: Ident<'a>, context_xref: XrefId, sub_template_index: Option, pending_structural: Option, @@ -583,7 +583,7 @@ enum OpInfo<'a> { view_xref: XrefId, slot: u32, /// The start placeholder name (e.g., "START_BLOCK_IF"). - start_name: Atom<'a>, + start_name: Ident<'a>, context_xref: XrefId, sub_template_index: Option, pending_structural: Option, @@ -594,7 +594,7 @@ enum OpInfo<'a> { view_xref: XrefId, slot: u32, /// The close placeholder name (e.g., "CLOSE_BLOCK_IF"). - close_name: Atom<'a>, + close_name: Ident<'a>, context_xref: XrefId, pending_structural: Option, }, @@ -608,7 +608,7 @@ fn add_param_to_context<'a>( value: I18nParamValue, allocator: &'a oxc_allocator::Allocator, ) { - let placeholder_atom = Atom::from(placeholder); + let placeholder_atom = Ident::from(placeholder); // Try root view first for op in job.root.create.iter_mut() { @@ -638,8 +638,8 @@ fn add_param_to_context<'a>( /// Add a value to a params map, creating the list if needed. fn add_to_params_map<'a>( - params: &mut oxc_allocator::HashMap<'a, Atom<'a>, oxc_allocator::Vec<'a, I18nParamValue>>, - placeholder: Atom<'a>, + params: &mut oxc_allocator::HashMap<'a, Ident<'a>, oxc_allocator::Vec<'a, I18nParamValue>>, + placeholder: Ident<'a>, value: I18nParamValue, allocator: &'a oxc_allocator::Allocator, ) { diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/resolve_i18n_expression_placeholders.rs b/crates/oxc_angular_compiler/src/pipeline/phases/resolve_i18n_expression_placeholders.rs index 524bf007e..9c7c5f37b 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/resolve_i18n_expression_placeholders.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/resolve_i18n_expression_placeholders.rs @@ -4,7 +4,7 @@ //! //! Ported from Angular's `template/pipeline/src/phases/resolve_i18n_expression_placeholders.ts`. -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; use crate::ir::enums::{I18nExpressionFor, I18nParamResolutionTime, I18nParamValueFlags}; @@ -50,7 +50,7 @@ pub fn resolve_i18n_expression_placeholders(job: &mut ComponentCompilationJob<'_ // (context_xref, placeholder_name, value, resolution_time, icu_placeholder_xref) struct ExprUpdate<'a> { context_xref: XrefId, - placeholder: Option>, + placeholder: Option>, value: I18nParamValue, resolution_time: I18nParamResolutionTime, icu_placeholder: Option, @@ -115,7 +115,7 @@ pub fn resolve_i18n_expression_placeholders(job: &mut ComponentCompilationJob<'_ }; // Add to params map - let placeholder_atom = Atom::from(placeholder.as_str()); + let placeholder_atom = Ident::from(placeholder.as_str()); if let Some(values) = params.get_mut(&placeholder_atom) { values.push(update.value); } else { diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/resolve_names.rs b/crates/oxc_angular_compiler/src/pipeline/phases/resolve_names.rs index d86be92c6..b3f4be299 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/resolve_names.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/resolve_names.rs @@ -11,7 +11,7 @@ use oxc_allocator::Box; use oxc_diagnostics::OxcDiagnostic; -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::FxHashMap; use crate::ast::expression::AngularExpression; @@ -81,13 +81,13 @@ pub fn resolve_names(job: &mut ComponentCompilationJob<'_>) { /// take precedence over other variables with the same name. #[derive(Default, Clone)] struct ScopeMaps<'a> { - scope: FxHashMap, XrefId>, - local_definitions: FxHashMap, XrefId>, + scope: FxHashMap, XrefId>, + local_definitions: FxHashMap, XrefId>, } impl<'a> ScopeMaps<'a> { /// Look up a name, checking local definitions first (they take precedence). - fn get(&self, name: &Atom<'a>) -> Option<&XrefId> { + fn get(&self, name: &Ident<'a>) -> Option<&XrefId> { self.local_definitions.get(name).or_else(|| self.scope.get(name)) } } diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/resolve_sanitizers.rs b/crates/oxc_angular_compiler/src/pipeline/phases/resolve_sanitizers.rs index aa11fc202..93cde79fa 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/resolve_sanitizers.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/resolve_sanitizers.rs @@ -6,7 +6,7 @@ //! //! Ported from Angular's `template/pipeline/src/phases/resolve_sanitizers.ts`. -use oxc_span::Atom; +use oxc_span::Ident; use crate::ast::r3::SecurityContext; use crate::ir::ops::{CreateOp, UpdateOp}; @@ -62,7 +62,7 @@ pub fn resolve_sanitizers(job: &mut ComponentCompilationJob<'_>) { for op in view.create.iter_mut() { if let CreateOp::ExtractedAttribute(attr) = op { if let Some(fn_name) = get_trusted_value_fn(attr.security_context) { - attr.trusted_value_fn = Some(Atom::from(fn_name)); + attr.trusted_value_fn = Some(Ident::from(fn_name)); } } } @@ -72,17 +72,17 @@ pub fn resolve_sanitizers(job: &mut ComponentCompilationJob<'_>) { match op { UpdateOp::Property(prop) => { if let Some(fn_name) = get_sanitizer_fn(prop.security_context) { - prop.sanitizer = Some(Atom::from(fn_name)); + prop.sanitizer = Some(Ident::from(fn_name)); } } UpdateOp::Attribute(attr) => { if let Some(fn_name) = get_sanitizer_fn(attr.security_context) { - attr.sanitizer = Some(Atom::from(fn_name)); + attr.sanitizer = Some(Ident::from(fn_name)); } } UpdateOp::DomProperty(dom_prop) => { if let Some(fn_name) = get_sanitizer_fn(dom_prop.security_context) { - dom_prop.sanitizer = Some(Atom::from(fn_name)); + dom_prop.sanitizer = Some(Ident::from(fn_name)); } } _ => {} @@ -100,7 +100,7 @@ pub fn resolve_sanitizers_for_host(job: &mut HostBindingCompilationJob<'_>) { for op in job.root.create.iter_mut() { if let CreateOp::ExtractedAttribute(attr) = op { if let Some(fn_name) = get_trusted_value_fn(attr.security_context) { - attr.trusted_value_fn = Some(Atom::from(fn_name)); + attr.trusted_value_fn = Some(Ident::from(fn_name)); } } } @@ -110,17 +110,17 @@ pub fn resolve_sanitizers_for_host(job: &mut HostBindingCompilationJob<'_>) { match op { UpdateOp::Property(prop) => { if let Some(fn_name) = get_sanitizer_fn(prop.security_context) { - prop.sanitizer = Some(Atom::from(fn_name)); + prop.sanitizer = Some(Ident::from(fn_name)); } } UpdateOp::Attribute(attr) => { if let Some(fn_name) = get_sanitizer_fn(attr.security_context) { - attr.sanitizer = Some(Atom::from(fn_name)); + attr.sanitizer = Some(Ident::from(fn_name)); } } UpdateOp::DomProperty(dom_prop) => { if let Some(fn_name) = get_sanitizer_fn(dom_prop.security_context) { - dom_prop.sanitizer = Some(Atom::from(fn_name)); + dom_prop.sanitizer = Some(Ident::from(fn_name)); } } _ => {} diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/save_restore_view.rs b/crates/oxc_angular_compiler/src/pipeline/phases/save_restore_view.rs index e2e1970f2..c977706d4 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/save_restore_view.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/save_restore_view.rs @@ -14,7 +14,7 @@ //! Ported from Angular's `template/pipeline/src/phases/save_restore_view.ts`. use oxc_allocator::{Box, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::ir::enums::{ExpressionKind, SemanticVariableKind, VariableFlags}; use crate::ir::expression::{ @@ -181,7 +181,7 @@ fn process_arrow_functions_in_view( // Per Angular (save_restore_view.ts lines 77-93): // The target is o.variable(expr.currentViewName) where currentViewName is 'view' let view_var_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("view"), source_span: None }, + ReadVarExpr { name: Ident::from("view"), source_span: None }, allocator, )); @@ -189,7 +189,7 @@ fn process_arrow_functions_in_view( base: UpdateOpBase::default(), xref: var_xref, kind: SemanticVariableKind::Context, - name: Atom::from(""), // Empty = naming phase will assign it + name: Ident::from(""), // Empty = naming phase will assign it initializer: Box::new_in( IrExpression::RestoreView(Box::new_in( RestoreViewExpr { @@ -395,7 +395,7 @@ fn add_restore_view_ops_only<'a>( base: UpdateOpBase::default(), xref: var_xref, kind: SemanticVariableKind::Context, - name: oxc_span::Atom::from(""), // Empty = naming phase will assign it + name: oxc_span::Ident::from(""), // Empty = naming phase will assign it initializer: Box::new_in( IrExpression::RestoreView(Box::new_in( RestoreViewExpr { view: RestoreViewTarget::Static(view_xref), source_span: None }, @@ -435,7 +435,7 @@ fn add_restore_view_to_listener<'a>( base: UpdateOpBase::default(), xref: var_xref, kind: SemanticVariableKind::Context, - name: oxc_span::Atom::from(""), // Empty = naming phase will assign it + name: oxc_span::Ident::from(""), // Empty = naming phase will assign it initializer: Box::new_in( IrExpression::RestoreView(Box::new_in( RestoreViewExpr { view: RestoreViewTarget::Static(view_xref), source_span: None }, @@ -511,7 +511,7 @@ fn create_saved_view_variable<'a>( base: Default::default(), xref, kind: SemanticVariableKind::SavedView, - name: oxc_span::Atom::from(""), // Empty = naming phase will assign it + name: oxc_span::Ident::from(""), // Empty = naming phase will assign it initializer: Box::new_in( IrExpression::GetCurrentView(Box::new_in( GetCurrentViewExpr { source_span: None }, diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/strip_nonrequired_parentheses.rs b/crates/oxc_angular_compiler/src/pipeline/phases/strip_nonrequired_parentheses.rs index 579285860..1c5f718a3 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/strip_nonrequired_parentheses.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/strip_nonrequired_parentheses.rs @@ -98,7 +98,7 @@ fn visit_expressions_for_required_parens( check_ir_expression_for_required_parens(&r.track, required); } CreateOp::I18nMessage(_) => { - // I18nMessage params are now formatted strings (Atom), not expressions + // I18nMessage params are now formatted strings (Ident), not expressions // No expressions to check here } CreateOp::I18nContext(_) => { diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/temporary_variables.rs b/crates/oxc_angular_compiler/src/pipeline/phases/temporary_variables.rs index 6c2bced42..d91730886 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/temporary_variables.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/temporary_variables.rs @@ -34,7 +34,7 @@ use std::cell::RefCell; use oxc_allocator::Allocator; -use oxc_span::Atom; +use oxc_span::Ident; use rustc_hash::{FxHashMap, FxHashSet}; use crate::ir::expression::{ @@ -388,7 +388,7 @@ fn generate_temporaries_for_update<'a>( fn create_declare_var_statement<'a>(allocator: &'a Allocator, name: &str) -> OutputStatement<'a> { OutputStatement::DeclareVar(oxc_allocator::Box::new_in( DeclareVarStmt { - name: Atom::from(allocator.alloc_str(name)), + name: Ident::from(allocator.alloc_str(name)), value: None, modifiers: StmtModifier::NONE, // Use `var` since there's no initializer leading_comment: None, @@ -416,12 +416,12 @@ fn assign_temp_names<'a>( match expr { IrExpression::AssignTemporary(assign) => { let name = tracker.borrow_mut().assign(assign.xref); - assign.name = Some(Atom::from(allocator.alloc_str(&name))); + assign.name = Some(Ident::from(allocator.alloc_str(&name))); } IrExpression::ReadTemporary(read) => { let name = tracker.borrow_mut().read(read.xref); if let Some(n) = name { - read.name = Some(Atom::from(allocator.alloc_str(&n))); + read.name = Some(Ident::from(allocator.alloc_str(&n))); } } // Recursively process nested expressions diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/track_fn_optimization.rs b/crates/oxc_angular_compiler/src/pipeline/phases/track_fn_optimization.rs index cb9f0f178..e508dec4d 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/track_fn_optimization.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/track_fn_optimization.rs @@ -9,7 +9,7 @@ //! //! Ported from Angular's `template/pipeline/src/phases/track_fn_optimization.ts`. -use oxc_span::Atom; +use oxc_span::Ident; use crate::ir::expression::{ IrExpression, TrackContextExpr, VisitorContextFlag, transform_expressions_in_expression, @@ -62,7 +62,7 @@ fn optimize_track_expression<'a>( // Check for simple $index or $item tracking if let Some(opt_fn) = check_simple_track_variable(&rep.track, expressions) { // Set the optimized track function name - rep.track_fn_name = Some(Atom::from(opt_fn)); + rep.track_fn_name = Some(Ident::from(opt_fn)); return; } @@ -77,13 +77,13 @@ fn optimize_track_expression<'a>( // Emit as ctx.methodName let fn_name = format!("ctx.{}", method_name); let name_str = allocator.alloc_str(&fn_name); - rep.track_fn_name = Some(Atom::from(name_str)); + rep.track_fn_name = Some(Ident::from(name_str)); } else { // Need to use componentInstance() to access the method // Create the full function reference string let fn_name = format!("{}().{}", Identifiers::COMPONENT_INSTANCE, method_name); let name_str = allocator.alloc_str(&fn_name); - rep.track_fn_name = Some(Atom::from(name_str)); + rep.track_fn_name = Some(Ident::from(name_str)); } return; } @@ -94,19 +94,24 @@ fn optimize_track_expression<'a>( // to the non-optimizable case below, which creates a wrapper function like: // `function _forTrack($index,$item) { return this.trackByFn; }` - // First check if the expression contains any ContextExpr or AST expressions - // that reference the component instance (implicit receiver) - let has_context = expression_contains_context(&rep.track, expressions); - if has_context { - rep.uses_component_instance = true; - } - - // For non-optimizable tracks, replace ContextExpr with TrackContextExpr - // This signals that context reads in track expressions need special handling + // The track function could not be optimized. + // Replace context reads with TrackContextExpr, since context reads in a track + // function are emitted specially (as `this` instead of `ctx`). + // + // Following Angular's implementation (track_fn_optimization.ts:54-70), we set + // usesComponentInstance inside the transform callback when a ContextExpr is found. + // This is the authoritative detection — transformExpressionsInExpression traverses + // all expression variants, so no ContextExpr can be missed regardless of nesting. + // + // By phase 34 (this phase), resolve_names (phase 31) has already converted all + // ImplicitReceiver AST nodes into Context IR expressions, so checking for Context + // during the transform is sufficient. + let found_context = std::cell::Cell::new(false); transform_expressions_in_expression( &mut rep.track, &|expr, _flags| { if let IrExpression::Context(ctx) = expr { + found_context.set(true); *expr = IrExpression::TrackContext(oxc_allocator::Box::new_in( TrackContextExpr { view: ctx.view, source_span: None }, allocator, @@ -115,6 +120,9 @@ fn optimize_track_expression<'a>( }, VisitorContextFlag::NONE, ); + if found_context.get() { + rep.uses_component_instance = true; + } // Also create an op list for the tracking expression since it may need // additional ops when generating the final code (e.g. temporary variables). @@ -146,179 +154,6 @@ fn optimize_track_expression<'a>( rep.track_by_ops = Some(track_by_ops); } -/// Check if an expression contains any Context expressions or AST expressions that -/// reference the component instance (implicit receiver). -fn expression_contains_context( - expr: &IrExpression<'_>, - expressions: &crate::pipeline::expression_store::ExpressionStore<'_>, -) -> bool { - match expr { - IrExpression::Context(_) => true, - // Check AST expressions for implicit receiver usage (this.property, this.method()) - IrExpression::Ast(ast) => ast_contains_implicit_receiver(ast), - // Check ExpressionRef by looking up the stored expression - IrExpression::ExpressionRef(id) => { - let stored_expr = expressions.get(*id); - ast_contains_implicit_receiver(stored_expr) - } - // Resolved expressions (created by resolveNames phase) - IrExpression::ResolvedCall(rc) => { - expression_contains_context(&rc.receiver, expressions) - || rc.args.iter().any(|e| expression_contains_context(e, expressions)) - } - IrExpression::ResolvedPropertyRead(rp) => { - expression_contains_context(&rp.receiver, expressions) - } - IrExpression::ResolvedBinary(rb) => { - expression_contains_context(&rb.left, expressions) - || expression_contains_context(&rb.right, expressions) - } - IrExpression::ResolvedKeyedRead(rk) => { - expression_contains_context(&rk.receiver, expressions) - || expression_contains_context(&rk.key, expressions) - } - IrExpression::ResolvedSafePropertyRead(rsp) => { - expression_contains_context(&rsp.receiver, expressions) - } - IrExpression::SafeTernary(st) => { - expression_contains_context(&st.guard, expressions) - || expression_contains_context(&st.expr, expressions) - } - IrExpression::SafePropertyRead(sp) => { - expression_contains_context(&sp.receiver, expressions) - } - IrExpression::SafeKeyedRead(sk) => { - expression_contains_context(&sk.receiver, expressions) - || expression_contains_context(&sk.index, expressions) - } - IrExpression::SafeInvokeFunction(sf) => { - expression_contains_context(&sf.receiver, expressions) - || sf.args.iter().any(|e| expression_contains_context(e, expressions)) - } - IrExpression::PipeBinding(pb) => { - pb.args.iter().any(|e| expression_contains_context(e, expressions)) - } - IrExpression::PipeBindingVariadic(pbv) => { - expression_contains_context(&pbv.args, expressions) - } - IrExpression::PureFunction(pf) => { - pf.args.iter().any(|e| expression_contains_context(e, expressions)) - } - IrExpression::Interpolation(i) => { - i.expressions.iter().any(|e| expression_contains_context(e, expressions)) - } - IrExpression::ResetView(rv) => expression_contains_context(&rv.expr, expressions), - IrExpression::ConditionalCase(cc) => { - cc.expr.as_ref().is_some_and(|e| expression_contains_context(e, expressions)) - } - IrExpression::TwoWayBindingSet(tbs) => { - expression_contains_context(&tbs.target, expressions) - || expression_contains_context(&tbs.value, expressions) - } - IrExpression::StoreLet(sl) => expression_contains_context(&sl.value, expressions), - IrExpression::ConstCollected(cc) => expression_contains_context(&cc.expr, expressions), - IrExpression::RestoreView(rv) => { - if let crate::ir::expression::RestoreViewTarget::Dynamic(e) = &rv.view { - expression_contains_context(e, expressions) - } else { - false - } - } - // Leaf expressions - _ => false, - } -} - -/// Check if an Angular AST expression contains any reference to the implicit receiver (this). -/// This includes property reads like `this.foo` and method calls like `this.bar()`. -fn ast_contains_implicit_receiver(ast: &crate::ast::expression::AngularExpression<'_>) -> bool { - use crate::ast::expression::AngularExpression; - - match ast { - // Direct implicit receiver reference - AngularExpression::ImplicitReceiver(_) => true, - // Property read - check if it's on implicit receiver or recurse - AngularExpression::PropertyRead(pr) => ast_contains_implicit_receiver(&pr.receiver), - // Safe property read - AngularExpression::SafePropertyRead(pr) => ast_contains_implicit_receiver(&pr.receiver), - // Keyed read - AngularExpression::KeyedRead(kr) => { - ast_contains_implicit_receiver(&kr.receiver) || ast_contains_implicit_receiver(&kr.key) - } - // Safe keyed read - AngularExpression::SafeKeyedRead(kr) => { - ast_contains_implicit_receiver(&kr.receiver) || ast_contains_implicit_receiver(&kr.key) - } - // Function call - AngularExpression::Call(call) => { - ast_contains_implicit_receiver(&call.receiver) - || call.args.iter().any(ast_contains_implicit_receiver) - } - // Safe call - AngularExpression::SafeCall(call) => { - ast_contains_implicit_receiver(&call.receiver) - || call.args.iter().any(ast_contains_implicit_receiver) - } - // Binary expression - AngularExpression::Binary(b) => { - ast_contains_implicit_receiver(&b.left) || ast_contains_implicit_receiver(&b.right) - } - // Unary expression - AngularExpression::Unary(u) => ast_contains_implicit_receiver(&u.expr), - // Conditional (ternary) - AngularExpression::Conditional(c) => { - ast_contains_implicit_receiver(&c.condition) - || ast_contains_implicit_receiver(&c.true_exp) - || ast_contains_implicit_receiver(&c.false_exp) - } - // Pipe binding - AngularExpression::BindingPipe(p) => { - ast_contains_implicit_receiver(&p.exp) - || p.args.iter().any(ast_contains_implicit_receiver) - } - // Not expressions - AngularExpression::PrefixNot(n) => ast_contains_implicit_receiver(&n.expression), - AngularExpression::NonNullAssert(n) => ast_contains_implicit_receiver(&n.expression), - // Typeof/void expressions - AngularExpression::TypeofExpression(t) => ast_contains_implicit_receiver(&t.expression), - AngularExpression::VoidExpression(v) => ast_contains_implicit_receiver(&v.expression), - AngularExpression::SpreadElement(spread) => { - ast_contains_implicit_receiver(&spread.expression) - } - // Chain - check all expressions - AngularExpression::Chain(c) => c.expressions.iter().any(ast_contains_implicit_receiver), - // Interpolation - check all expressions - AngularExpression::Interpolation(i) => { - i.expressions.iter().any(ast_contains_implicit_receiver) - } - // Template literals - AngularExpression::TemplateLiteral(t) => { - t.expressions.iter().any(ast_contains_implicit_receiver) - } - AngularExpression::TaggedTemplateLiteral(t) => { - ast_contains_implicit_receiver(&t.tag) - || t.template.expressions.iter().any(ast_contains_implicit_receiver) - } - // Array literal - AngularExpression::LiteralArray(arr) => { - arr.expressions.iter().any(ast_contains_implicit_receiver) - } - // Map literal - AngularExpression::LiteralMap(map) => map.values.iter().any(ast_contains_implicit_receiver), - // Parenthesized expression - AngularExpression::ParenthesizedExpression(p) => { - ast_contains_implicit_receiver(&p.expression) - } - // Arrow function - check the body - AngularExpression::ArrowFunction(arrow) => ast_contains_implicit_receiver(&arrow.body), - // Literals and other leaf nodes don't contain implicit receiver - AngularExpression::LiteralPrimitive(_) - | AngularExpression::Empty(_) - | AngularExpression::ThisReceiver(_) - | AngularExpression::RegularExpressionLiteral(_) => false, - } -} - /// Check if the track expression is a simple variable read of $index or $item. fn check_simple_track_variable( track: &IrExpression<'_>, @@ -482,14 +317,14 @@ mod tests { let prop_read = IrExpression::ResolvedPropertyRead(oxc_allocator::Box::new_in( ResolvedPropertyReadExpr { receiver: oxc_allocator::Box::new_in(ctx, alloc), - name: Atom::from(method_name), + name: Ident::from(method_name), source_span: None, }, alloc, )); let index_arg = IrExpression::OutputExpr(oxc_allocator::Box::new_in( OutputExpression::ReadVar(oxc_allocator::Box::new_in( - ReadVarExpr { name: Atom::from("$index"), source_span: None }, + ReadVarExpr { name: Ident::from("$index"), source_span: None }, alloc, )), alloc, diff --git a/crates/oxc_angular_compiler/src/pipeline/phases/track_variables.rs b/crates/oxc_angular_compiler/src/pipeline/phases/track_variables.rs index faf970110..1030fcaf2 100644 --- a/crates/oxc_angular_compiler/src/pipeline/phases/track_variables.rs +++ b/crates/oxc_angular_compiler/src/pipeline/phases/track_variables.rs @@ -7,7 +7,7 @@ //! Ported from Angular's `template/pipeline/src/phases/track_variables.ts`. use oxc_allocator::Box; -use oxc_span::Atom; +use oxc_span::Ident; use crate::ast::expression::AngularExpression; use crate::ir::expression::{ @@ -36,10 +36,10 @@ pub fn generate_track_variables(job: &mut ComponentCompilationJob<'_>) { for op in view.create.iter_mut() { if let CreateOp::RepeaterCreate(rep) = op { // Get $index names and $implicit name for this repeater - let index_names: Vec> = { - let mut names: Vec> = rep.var_names.index.iter().cloned().collect(); + let index_names: Vec> = { + let mut names: Vec> = rep.var_names.index.iter().cloned().collect(); // Also include '$index' itself - names.push(Atom::from("$index")); + names.push(Ident::from("$index")); names }; let implicit_name = rep.var_names.item.clone(); @@ -63,12 +63,12 @@ pub fn generate_track_variables(job: &mut ComponentCompilationJob<'_>) { fn transform_track_expression<'a>( allocator: &'a oxc_allocator::Allocator, expr: &mut Box<'a, IrExpression<'a>>, - index_names: &[Atom<'a>], - implicit_name: Option<&Atom<'a>>, + index_names: &[Ident<'a>], + implicit_name: Option<&Ident<'a>>, expressions: &crate::pipeline::expression_store::ExpressionStore<'a>, ) { - let index_names_clone: Vec> = index_names.to_vec(); - let implicit_name_clone: Option> = implicit_name.cloned(); + let index_names_clone: Vec> = index_names.to_vec(); + let implicit_name_clone: Option> = implicit_name.cloned(); transform_expressions_in_expression( expr, @@ -80,7 +80,7 @@ fn transform_track_expression<'a>( // Replace with o.variable('$index') *inner_expr = IrExpression::OutputExpr(Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("$index"), source_span: None }, + ReadVarExpr { name: Ident::from("$index"), source_span: None }, allocator, )), allocator, @@ -94,7 +94,7 @@ fn transform_track_expression<'a>( // Replace with o.variable('$item') *inner_expr = IrExpression::OutputExpr(Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("$item"), source_span: None }, + ReadVarExpr { name: Ident::from("$item"), source_span: None }, allocator, )), allocator, @@ -107,7 +107,7 @@ fn transform_track_expression<'a>( if lexical.name.as_str() == "$implicit" { *inner_expr = IrExpression::OutputExpr(Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("$item"), source_span: None }, + ReadVarExpr { name: Ident::from("$item"), source_span: None }, allocator, )), allocator, @@ -157,8 +157,8 @@ fn transform_track_expression<'a>( fn transform_angular_expression_for_track<'a>( allocator: &'a oxc_allocator::Allocator, expr: &AngularExpression<'a>, - index_names: &[Atom<'a>], - implicit_name: Option<&Atom<'a>>, + index_names: &[Ident<'a>], + implicit_name: Option<&Ident<'a>>, ) -> Option> { match expr { // Direct read of loop variable: `item` or `$index` @@ -171,7 +171,7 @@ fn transform_angular_expression_for_track<'a>( if index_names.iter().any(|n| *n == *name) { return Some(IrExpression::OutputExpr(Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("$index"), source_span: None }, + ReadVarExpr { name: Ident::from("$index"), source_span: None }, allocator, )), allocator, @@ -183,7 +183,7 @@ fn transform_angular_expression_for_track<'a>( if *name == *item_name { return Some(IrExpression::OutputExpr(Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("$item"), source_span: None }, + ReadVarExpr { name: Ident::from("$item"), source_span: None }, allocator, )), allocator, @@ -195,7 +195,7 @@ fn transform_angular_expression_for_track<'a>( if name.as_str() == "$implicit" { return Some(IrExpression::OutputExpr(Box::new_in( OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from("$item"), source_span: None }, + ReadVarExpr { name: Ident::from("$item"), source_span: None }, allocator, )), allocator, diff --git a/crates/oxc_angular_compiler/src/pipeline/selector.rs b/crates/oxc_angular_compiler/src/pipeline/selector.rs index 4d6a827e6..7b753653f 100644 --- a/crates/oxc_angular_compiler/src/pipeline/selector.rs +++ b/crates/oxc_angular_compiler/src/pipeline/selector.rs @@ -7,7 +7,7 @@ //! `core.ts` parseSelectorToR3Selector function. use oxc_allocator::{Allocator, Vec as OxcVec}; -use oxc_span::Atom; +use oxc_span::Ident; use crate::output::ast::{LiteralExpr, LiteralValue, OutputExpression}; @@ -441,7 +441,7 @@ pub fn r3_selector_to_output_expr<'a>( R3SelectorElement::String(s) => { result.push(OutputExpression::Literal(oxc_allocator::Box::new_in( LiteralExpr { - value: LiteralValue::String(Atom::from(allocator.alloc_str(s))), + value: LiteralValue::String(Ident::from(allocator.alloc_str(s))), source_span: None, }, allocator, diff --git a/crates/oxc_angular_compiler/src/styles/encapsulation.rs b/crates/oxc_angular_compiler/src/styles/encapsulation.rs index 17e1ed6c9..f0dd57048 100644 --- a/crates/oxc_angular_compiler/src/styles/encapsulation.rs +++ b/crates/oxc_angular_compiler/src/styles/encapsulation.rs @@ -33,6 +33,28 @@ const COMMENT_PLACEHOLDER: &str = "%COMMENT%"; const POLYFILL_HOST: &str = "-shadowcsshost"; const POLYFILL_HOST_NO_COMBINATOR: &str = "-shadowcsshost-no-combinator"; +/// Push a single UTF-8 character starting at byte position `i` from `source` into `result`. +/// Returns the number of bytes consumed (1 for ASCII, 2-4 for multi-byte). +/// +/// This replaces the incorrect `result.push(bytes[i] as char)` pattern which +/// corrupts multi-byte UTF-8 characters by treating each byte as a Latin-1 codepoint. +#[inline] +fn push_utf8_char(result: &mut String, source: &str, i: usize) -> usize { + // Determine UTF-8 character width from the leading byte per RFC 3629. + let b = source.as_bytes()[i]; + let width = if b < 0x80 { + 1 + } else if b < 0xE0 { + 2 + } else if b < 0xF0 { + 3 + } else { + 4 + }; + result.push_str(&source[i..i + width]); + width +} + // ============================================================================= // SafeSelector - Escapes problematic CSS patterns before processing // ============================================================================= @@ -101,8 +123,7 @@ impl SafeSelector { i += 1; } } else { - new_result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut new_result, &result, i); } } result = new_result; @@ -125,8 +146,7 @@ impl SafeSelector { new_result.push_str(&placeholder); i += 2; } else { - new_result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut new_result, &result, i); } } result = new_result; @@ -349,8 +369,7 @@ fn extract_comments(css: &str) -> (String, Vec) { result.push_str(COMMENT_PLACEHOLDER); } else { - result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut result, css, i); } } @@ -505,8 +524,7 @@ fn scope_keyframes_names( (name, name_end, None) } else { // No valid name found - result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut result, css, i); continue; }; @@ -571,8 +589,7 @@ fn scope_keyframes_names( } } - result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut result, css, i); } result @@ -627,8 +644,7 @@ fn scope_animation_rules( || !css.is_char_boundary(i + value_start) || !css.is_char_boundary(i + value_end) { - result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut result, css, i); continue; } let prefix = &css[i..i + prefix_end]; @@ -655,8 +671,7 @@ fn scope_animation_rules( } } - result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut result, css, i); } result @@ -2255,7 +2270,9 @@ fn split_by_combinators(selector: &str) -> Vec<(&str, &str)> { ')' => paren_depth = paren_depth.saturating_sub(1), '[' => bracket_depth += 1, ']' => bracket_depth = bracket_depth.saturating_sub(1), - ' ' | '>' | '+' | '~' if paren_depth == 0 && bracket_depth == 0 => { + ' ' | '\n' | '\t' | '\r' | '>' | '+' | '~' + if paren_depth == 0 && bracket_depth == 0 => + { // A space following an escaped hex value and followed by another hex character // (ie: ".\fc ber" for ".über") is not a separator between 2 selectors // Check: if the part ends with an escape placeholder AND next char is hex @@ -2276,7 +2293,13 @@ fn split_by_combinators(selector: &str) -> Vec<(&str, &str)> { // Collect the combinator (may include spaces around it) let combinator_start = i; while i < chars.len() - && (chars[i] == ' ' || chars[i] == '>' || chars[i] == '+' || chars[i] == '~') + && (chars[i] == ' ' + || chars[i] == '\n' + || chars[i] == '\t' + || chars[i] == '\r' + || chars[i] == '>' + || chars[i] == '+' + || chars[i] == '~') { i += 1; } @@ -2420,7 +2443,15 @@ fn scope_after_host_with_context(selector: &str, ctx: &mut ScopingContext) -> St // First part (pseudo-selector attached to host) - don't scope scoped_after.push_str(part); if !combinator.is_empty() - && combinator.chars().any(|c| c == ' ' || c == '>' || c == '+' || c == '~') + && combinator.chars().any(|c| { + c == ' ' + || c == '\n' + || c == '\t' + || c == '\r' + || c == '>' + || c == '+' + || c == '~' + }) { found_combinator = true; } @@ -2565,8 +2596,7 @@ fn replace_host_context_patterns(s: &str, replacement: &str) -> String { i = after; continue; } - result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut result, s, i); } result @@ -2624,8 +2654,7 @@ fn insert_polyfill_directives(css: &str) -> String { continue; } } - result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut result, css, i); } result @@ -2746,8 +2775,7 @@ fn insert_polyfill_rules(css: &str) -> String { } } } - result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut result, css, i); } result @@ -3032,8 +3060,7 @@ fn strip_deep_combinators(s: &str) -> String { continue; } - result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut result, s, i); } result @@ -3081,8 +3108,7 @@ fn strip_host_patterns(s: &str) -> String { continue; } - result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut result, s, i); } result @@ -3131,8 +3157,7 @@ fn remove_unscoped_rules(css: &str) -> String { continue; } } - result.push(bytes[i] as char); - i += 1; + i += push_utf8_char(&mut result, css, i); } result diff --git a/crates/oxc_angular_compiler/src/styles/minify.rs b/crates/oxc_angular_compiler/src/styles/minify.rs new file mode 100644 index 000000000..89f75a907 --- /dev/null +++ b/crates/oxc_angular_compiler/src/styles/minify.rs @@ -0,0 +1,68 @@ +use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; + +const COMPONENT_PLACEHOLDER: &str = "%COMP%"; +const MINIFY_PLACEHOLDER: &str = "OXCANGULARCOMPONENT"; + +/// Apply Angular style encapsulation and optionally minify the final CSS. +pub fn finalize_component_style( + style: &str, + encapsulate: bool, + content_attr: &str, + host_attr: &str, + minify: bool, +) -> String { + let style = if encapsulate { + super::shim_css_text(style, content_attr, host_attr) + } else { + style.to_string() + }; + + if !minify || style.trim().is_empty() { + return style; + } + + minify_component_style(&style).unwrap_or(style) +} + +/// Minify a final component CSS string while preserving Angular's `%COMP%` placeholder. +pub fn minify_component_style(style: &str) -> Option { + let css = style.replace(COMPONENT_PLACEHOLDER, MINIFY_PLACEHOLDER); + let mut stylesheet = StyleSheet::parse(&css, ParserOptions::default()).ok()?; + stylesheet.minify(MinifyOptions::default()).ok()?; + + let code = + stylesheet.to_css(PrinterOptions { minify: true, ..PrinterOptions::default() }).ok()?.code; + + Some(code.replace(MINIFY_PLACEHOLDER, COMPONENT_PLACEHOLDER)) +} + +#[cfg(test)] +mod tests { + use super::{finalize_component_style, minify_component_style}; + + #[test] + fn minifies_css_with_component_placeholders() { + let minified = minify_component_style( + "[_ngcontent-%COMP%] {\n color: red;\n background: transparent;\n}\n", + ) + .expect("style should minify"); + + assert_eq!(minified, "[_ngcontent-%COMP%]{color:red;background:0 0}"); + } + + #[test] + fn finalizes_emulated_styles_before_minifying() { + let finalized = finalize_component_style( + ":host {\n display: block;\n}\n.button {\n color: red;\n}\n", + true, + "_ngcontent-%COMP%", + "_nghost-%COMP%", + true, + ); + + assert_eq!( + finalized, + "[_nghost-%COMP%]{display:block}.button[_ngcontent-%COMP%]{color:red}" + ); + } +} diff --git a/crates/oxc_angular_compiler/src/styles/mod.rs b/crates/oxc_angular_compiler/src/styles/mod.rs index d11c1cea4..7d71b123b 100644 --- a/crates/oxc_angular_compiler/src/styles/mod.rs +++ b/crates/oxc_angular_compiler/src/styles/mod.rs @@ -5,5 +5,7 @@ //! - CSS transformation for component-scoped styles mod encapsulation; +mod minify; pub use encapsulation::{encapsulate_style, shim_css_text}; +pub use minify::{finalize_component_style, minify_component_style}; diff --git a/crates/oxc_angular_compiler/src/transform/control_flow.rs b/crates/oxc_angular_compiler/src/transform/control_flow.rs index d9efa0c84..11b934890 100644 --- a/crates/oxc_angular_compiler/src/transform/control_flow.rs +++ b/crates/oxc_angular_compiler/src/transform/control_flow.rs @@ -8,7 +8,7 @@ //! Ported from Angular's `render3/r3_control_flow.ts`. use oxc_allocator::{Allocator, Vec}; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use crate::ast::expression::{ASTWithSource, AngularExpression}; use crate::ast::html::HtmlBlockParameter; @@ -261,8 +261,8 @@ pub fn parse_conditional_params<'a>( ); let name_alloc = allocator.alloc_str(name_str); expression_alias = Some(R3Variable { - name: Atom::from(name_alloc), - value: Atom::from(name_alloc), // value same as name for alias + name: Ident::from(name_alloc), + value: Ident::from(name_alloc), // value same as name for alias source_span: name_span, key_span: name_span, value_span: None, // No value span for expression alias @@ -374,14 +374,14 @@ pub fn parse_for_loop_parameters<'a>( // Create item variable - use the allocator to intern the string // Calculate a span that only covers the item name (not "of items.foo.bar") - let item_name_atom = Atom::from(allocator.alloc_str(&item_name_owned)); + let item_name_atom = Ident::from(allocator.alloc_str(&item_name_owned)); let item_name_span = Span::new( expression_param.span.start, expression_param.span.start + item_name_str.len() as u32, ); let item = R3Variable { name: item_name_atom, - value: Atom::from("$implicit"), + value: Ident::from("$implicit"), source_span: item_name_span, key_span: item_name_span, value_span: None, @@ -579,8 +579,8 @@ fn parse_let_parameter<'a>( let name_alloc = allocator.alloc_str(name); let value_alloc = allocator.alloc_str(variable_name); context_variables.push(R3Variable { - name: Atom::from(name_alloc), - value: Atom::from(value_alloc), + name: Ident::from(name_alloc), + value: Ident::from(value_alloc), source_span, key_span, value_span: Some(value_span), @@ -605,8 +605,8 @@ fn create_default_context_variables<'a>( for &var_name in ALLOWED_FOR_LOOP_LET_VARIABLES { vars.push(R3Variable { - name: Atom::from(var_name), - value: Atom::from(var_name), + name: Ident::from(var_name), + value: Ident::from(var_name), source_span: empty_span, key_span: empty_span, value_span: None, @@ -620,8 +620,8 @@ fn create_default_context_variables<'a>( fn create_empty_variable<'a>(_allocator: &'a Allocator, span: Span) -> R3Variable<'a> { // Note: allocator reserved for future use (e.g., allocating default expression). R3Variable { - name: Atom::from(""), - value: Atom::from("$implicit"), + name: Ident::from(""), + value: Ident::from("$implicit"), source_span: span, key_span: span, value_span: None, @@ -641,7 +641,7 @@ fn create_empty_ast_with_source<'a>(allocator: &'a Allocator, span: Span) -> AST allocator, )), source: None, - location: Atom::from(""), + location: Ident::from(""), absolute_offset: span.start, } } @@ -663,8 +663,8 @@ fn parse_expression_to_ast_with_source<'a>( ASTWithSource { ast: result.ast, - source: Some(Atom::from(expr_str)), - location: Atom::from(""), + source: Some(Ident::from(expr_str)), + location: Ident::from(""), absolute_offset: span.start + expression_start_offset, } } @@ -1384,7 +1384,7 @@ fn parse_single_on_trigger<'a>( return; } } - let reference = params.map(|s| Atom::from(s.trim())); + let reference = params.map(|s| Ident::from(s.trim())); triggers.hover = Some(R3HoverDeferredTrigger { reference, source_span, @@ -1416,7 +1416,7 @@ fn parse_single_on_trigger<'a>( return; } } - let reference = params.map(|s| Atom::from(s.trim())); + let reference = params.map(|s| Ident::from(s.trim())); triggers.interaction = Some(R3InteractionDeferredTrigger { reference, source_span, @@ -1471,7 +1471,7 @@ fn parse_single_on_trigger<'a>( (result.reference, result.options) } else { // Simple reference name - (Some(Atom::from(trimmed)), None) + (Some(Ident::from(trimmed)), None) } } else { (None, None) @@ -1504,7 +1504,7 @@ fn parse_single_on_trigger<'a>( /// Result type for viewport trigger extraction. struct ViewportTriggerResult<'a> { - reference: Option>, + reference: Option>, options: Option>, errors: std::vec::Vec, } @@ -1535,7 +1535,7 @@ fn extract_viewport_trigger_and_options<'a>( let mut errors = std::vec::Vec::new(); if let AngularExpression::LiteralMap(map) = expr { - let mut trigger_ref: Option> = None; + let mut trigger_ref: Option> = None; let mut trigger_idx: Option = None; // First pass: find the trigger key, check for "root" key, and extract trigger value diff --git a/crates/oxc_angular_compiler/src/transform/html_to_r3.rs b/crates/oxc_angular_compiler/src/transform/html_to_r3.rs index d066bc136..8d03c96cf 100644 --- a/crates/oxc_angular_compiler/src/transform/html_to_r3.rs +++ b/crates/oxc_angular_compiler/src/transform/html_to_r3.rs @@ -6,7 +6,7 @@ //! Ported from Angular's `render3/r3_template_transform.ts`. use oxc_allocator::{Allocator, Box, FromIn, HashMap, Vec}; -use oxc_span::{Atom, Span}; +use oxc_span::{Ident, Span}; use rustc_hash::{FxHashMap, FxHashSet}; use crate::ast::expression::{ @@ -81,9 +81,9 @@ enum BindingPrefix { #[derive(Debug, Clone)] struct TemplateAttrInfo<'a> { /// The attribute name (e.g., `*ngFor`). - name: Atom<'a>, + name: Ident<'a>, /// The attribute value (e.g., `let item of items`). - value: Atom<'a>, + value: Ident<'a>, /// The full span of the attribute. span: Span, /// The span of the attribute name. @@ -102,8 +102,8 @@ pub struct TransformOptions { /// Inserts or updates a var entry in an ordered Vec, preserving first-insertion order. /// This matches JS object semantics where reassigning an existing key keeps its position. fn ordered_insert_var<'a>( - vec: &mut Vec<'a, (Atom<'a>, R3BoundText<'a>)>, - key: Atom<'a>, + vec: &mut Vec<'a, (Ident<'a>, R3BoundText<'a>)>, + key: Ident<'a>, value: R3BoundText<'a>, ) { if let Some(existing) = vec.iter_mut().find(|(k, _)| *k == key) { @@ -116,8 +116,8 @@ fn ordered_insert_var<'a>( /// Inserts or updates a placeholder entry in an ordered Vec, preserving first-insertion order. /// This matches JS object semantics where reassigning an existing key keeps its position. fn ordered_insert_placeholder<'a>( - vec: &mut Vec<'a, (Atom<'a>, R3IcuPlaceholder<'a>)>, - key: Atom<'a>, + vec: &mut Vec<'a, (Ident<'a>, R3IcuPlaceholder<'a>)>, + key: Ident<'a>, value: R3IcuPlaceholder<'a>, ) { if let Some(existing) = vec.iter_mut().find(|(k, _)| *k == key) { @@ -137,9 +137,9 @@ pub struct HtmlToR3Transform<'a> { /// Parse errors. /// Uses std::vec::Vec since ParseError contains Drop types (Arc, String). errors: std::vec::Vec, - styles: Vec<'a, Atom<'a>>, - style_urls: Vec<'a, Atom<'a>>, - ng_content_selectors: Vec<'a, Atom<'a>>, + styles: Vec<'a, Ident<'a>>, + style_urls: Vec<'a, Ident<'a>>, + ng_content_selectors: Vec<'a, Ident<'a>>, comment_nodes: Option>>, processed_nodes: FxHashSet, namespace_stack: std::vec::Vec, @@ -285,7 +285,7 @@ impl<'a> HtmlToR3Transform<'a> { }; // Reconstruct the @let text with semicolon let reconstructed = format!("@let {} = {};", decl.name.as_str(), value_text); - let text_value = Atom::from_in(reconstructed.as_str(), self.allocator); + let text_value = Ident::from_in(reconstructed.as_str(), self.allocator); let r3_text = R3Text { value: text_value, source_span: decl.span }; return Some(R3Node::Text(Box::new_in(r3_text, self.allocator))); } @@ -584,11 +584,11 @@ impl<'a> HtmlToR3Transform<'a> { (None, Some(tag)) => Some(*tag), (Some(prefix), None) => { // Has prefix but no tag name - use "ng-component" as default - Some(Atom::from_in(&format!(":{prefix}:ng-component"), self.allocator)) + Some(Ident::from_in(&format!(":{prefix}:ng-component"), self.allocator)) } (Some(prefix), Some(tag)) => { // Both prefix and tag name: ":prefix:tag_name" - Some(Atom::from_in(&format!(":{prefix}:{tag}"), self.allocator)) + Some(Ident::from_in(&format!(":{prefix}:{tag}"), self.allocator)) } }; @@ -596,11 +596,11 @@ impl<'a> HtmlToR3Transform<'a> { let full_name = match &tag_name { Some(tag) if tag.starts_with(':') => { // Namespace format: "MyComp:svg:rect" (tag_name already has :prefix:) - Atom::from_in(&format!("{}{}", element.name, tag), self.allocator) + Ident::from_in(&format!("{}{}", element.name, tag), self.allocator) } Some(tag) => { // Simple format: "MyComp:div" - Atom::from_in(&format!("{}:{}", element.name, tag), self.allocator) + Ident::from_in(&format!("{}:{}", element.name, tag), self.allocator) } None => element.name, }; @@ -703,7 +703,7 @@ impl<'a> HtmlToR3Transform<'a> { // Transform selectorless directives from HTML AST // For components, tag_name may be None (e.g., ``), in which case we use empty string // which matches TypeScript's behavior where elementName can be null. - let element_name = component.tag_name.as_ref().map_or("", oxc_span::Atom::as_str); + let element_name = component.tag_name.as_ref().map_or("", oxc_span::Ident::as_str); let directives = self.transform_directives(&component.directives, element_name); // Validate selectorless references @@ -789,7 +789,7 @@ impl<'a> HtmlToR3Transform<'a> { } } - fn qualify_element_name(&self, name: Atom<'a>, namespace: ElementNamespace) -> Atom<'a> { + fn qualify_element_name(&self, name: Ident<'a>, namespace: ElementNamespace) -> Ident<'a> { if namespace == ElementNamespace::Html { return name; } @@ -803,7 +803,7 @@ impl<'a> HtmlToR3Transform<'a> { && Self::namespace_from_prefix(prefix).is_some() { let qualified = format!(":{prefix}:{local}"); - return Atom::from_in(&qualified, self.allocator); + return Ident::from_in(&qualified, self.allocator); } let ns = match namespace { @@ -812,7 +812,7 @@ impl<'a> HtmlToR3Transform<'a> { ElementNamespace::Html => return name, }; let qualified = format!(":{ns}:{name_str}"); - Atom::from_in(&qualified, self.allocator) + Ident::from_in(&qualified, self.allocator) } /// Transforms HTML directives to R3 directives. @@ -899,8 +899,8 @@ impl<'a> HtmlToR3Transform<'a> { } else { seen_reference_names.insert(ref_name); references.push(R3Reference { - name: Atom::from_in(ref_name, self.allocator), - value: Atom::from_in("", self.allocator), + name: Ident::from_in(ref_name, self.allocator), + value: Ident::from_in("", self.allocator), source_span: attr.span, key_span: attr.name_span, value_span: None, @@ -942,7 +942,7 @@ impl<'a> HtmlToR3Transform<'a> { self.binding_parser.parse_binding(attr_value, value_span); inputs.push(R3BoundAttribute { - name: Atom::from_in(prop_name, self.allocator), + name: Ident::from_in(prop_name, self.allocator), binding_type, value: parse_result.ast, unit: None, @@ -960,7 +960,7 @@ impl<'a> HtmlToR3Transform<'a> { self.binding_parser.parse_event(attr_value, value_span); outputs.push(R3BoundEvent { - name: Atom::from_in(rest, self.allocator), + name: Ident::from_in(rest, self.allocator), handler: parse_result.ast, target: None, event_type: ParsedEventType::Regular, @@ -977,7 +977,7 @@ impl<'a> HtmlToR3Transform<'a> { self.binding_parser.parse_binding(attr_value, value_span); inputs.push(R3BoundAttribute { - name: Atom::from_in(rest, self.allocator), + name: Ident::from_in(rest, self.allocator), binding_type: BindingType::TwoWay, value: parse_result.ast, unit: None, @@ -990,7 +990,7 @@ impl<'a> HtmlToR3Transform<'a> { // Two-way binding also creates an output event let event_name = - Atom::from_in(&format!("{rest}Change"), self.allocator); + Ident::from_in(&format!("{rest}Change"), self.allocator); let event_parse_result = self.binding_parser.parse_event(attr_value, value_span); @@ -1016,7 +1016,7 @@ impl<'a> HtmlToR3Transform<'a> { self.binding_parser.parse_binding(value_str, value_span); inputs.push(R3BoundAttribute { - name: Atom::from_in(rest, self.allocator), + name: Ident::from_in(rest, self.allocator), binding_type: BindingType::LegacyAnimation, value: parse_result.ast, unit: None, @@ -1043,7 +1043,7 @@ impl<'a> HtmlToR3Transform<'a> { let parse_result = self.binding_parser.parse_binding(value_str, value_span); inputs.push(R3BoundAttribute { - name: Atom::from_in(prop_name, self.allocator), + name: Ident::from_in(prop_name, self.allocator), binding_type: BindingType::TwoWay, value: parse_result.ast, unit: None, @@ -1055,7 +1055,7 @@ impl<'a> HtmlToR3Transform<'a> { }); // Two-way binding also creates an output event - let event_name = Atom::from_in(&format!("{prop_name}Change"), self.allocator); + let event_name = Ident::from_in(&format!("{prop_name}Change"), self.allocator); let event_value_str = self.allocator.alloc_str(attr_value); let event_parse_result = self.binding_parser.parse_event(event_value_str, value_span); @@ -1114,7 +1114,7 @@ impl<'a> HtmlToR3Transform<'a> { let parse_result = self.binding_parser.parse_binding(value_str, value_span); inputs.push(R3BoundAttribute { - name: Atom::from_in(prop_name, self.allocator), + name: Ident::from_in(prop_name, self.allocator), binding_type: BindingType::Property, value: parse_result.ast, unit: None, @@ -1139,7 +1139,7 @@ impl<'a> HtmlToR3Transform<'a> { let parse_result = self.binding_parser.parse_event(value_str, value_span); outputs.push(R3BoundEvent { - name: Atom::from_in(event_name, self.allocator), + name: Ident::from_in(event_name, self.allocator), handler: parse_result.ast, target: None, event_type: ParsedEventType::Regular, @@ -1244,8 +1244,8 @@ impl<'a> HtmlToR3Transform<'a> { let icu_type_upper = expansion.expansion_type.as_str().to_uppercase(); let base_name = format!("VAR_{icu_type_upper}"); let expression_placeholder = - Atom::from_in(&self.generate_unique_icu_placeholder(&base_name), self.allocator); - let icu_placeholder_name = Atom::from_in("ICU", self.allocator); + Ident::from_in(&self.generate_unique_icu_placeholder(&base_name), self.allocator); + let icu_placeholder_name = Ident::from_in("ICU", self.allocator); // Create the I18nIcu for the i18n metadata // The cases are empty here since they're parsed separately into R3Icu.placeholders @@ -1270,15 +1270,15 @@ impl<'a> HtmlToR3Transform<'a> { // Serialize the message string for goog.getMsg and $localize let message_string_str = serialize_i18n_nodes(&nodes); - let message_string = Atom::from_in(&*message_string_str, self.allocator); + let message_string = Ident::from_in(&*message_string_str, self.allocator); let i18n_message = I18nMessage { instance_id: self.allocate_i18n_message_instance_id(), nodes, - meaning: Atom::from(""), - description: Atom::from(""), - custom_id: Atom::from(""), - id: Atom::from(""), + meaning: Ident::from(""), + description: Ident::from(""), + custom_id: Ident::from(""), + id: Ident::from(""), legacy_ids: Vec::new_in(self.allocator), message_string, }; @@ -1325,8 +1325,8 @@ impl<'a> HtmlToR3Transform<'a> { fn extract_placeholders_from_nodes( &mut self, nodes: &[HtmlNode<'a>], - placeholders: &mut Vec<'a, (Atom<'a>, R3IcuPlaceholder<'a>)>, - vars: &mut Vec<'a, (Atom<'a>, R3BoundText<'a>)>, + placeholders: &mut Vec<'a, (Ident<'a>, R3IcuPlaceholder<'a>)>, + vars: &mut Vec<'a, (Ident<'a>, R3BoundText<'a>)>, ) { for node in nodes { match node { @@ -1357,7 +1357,7 @@ impl<'a> HtmlToR3Transform<'a> { let icu_type_upper = nested_expansion.expansion_type.as_str().to_uppercase(); let base_name = format!("VAR_{icu_type_upper}"); let unique_name = self.generate_unique_icu_placeholder(&base_name); - let var_placeholder_name = Atom::from_in(&unique_name, self.allocator); + let var_placeholder_name = Ident::from_in(&unique_name, self.allocator); // Recursively extract from nested expansion cases FIRST. // This ensures placeholders and further nested ICU vars are processed @@ -1401,7 +1401,7 @@ impl<'a> HtmlToR3Transform<'a> { &mut self, text: &str, base_span: Span, - placeholders: &mut Vec<'a, (Atom<'a>, R3IcuPlaceholder<'a>)>, + placeholders: &mut Vec<'a, (Ident<'a>, R3IcuPlaceholder<'a>)>, ) { // Use default Angular interpolation markers let start_marker = "{{"; @@ -1433,7 +1433,7 @@ impl<'a> HtmlToR3Transform<'a> { let value_str = self.allocator.alloc_str(expr_content); let parse_result = self.binding_parser.parse_binding(value_str, interp_span); - let placeholder_key = Atom::from_in(interpolation, self.allocator); + let placeholder_key = Ident::from_in(interpolation, self.allocator); let bound_text = R3BoundText { value: parse_result.ast, source_span: interp_span, @@ -1516,7 +1516,7 @@ impl<'a> HtmlToR3Transform<'a> { // Static text - use value with ngsp replaced let value_atom = if has_ngsp { let value_no_ngsp = value_str.replace(NGSP_UNICODE, " "); - Atom::from_in(&value_no_ngsp, self.allocator) + Ident::from_in(&value_no_ngsp, self.allocator) } else { text.value }; @@ -1561,7 +1561,7 @@ impl<'a> HtmlToR3Transform<'a> { // Add start text node nodes.push(R3Node::Text(Box::new_in( R3Text { - value: Atom::from(self.allocator.alloc_str(start_text)), + value: Ident::from(self.allocator.alloc_str(start_text)), source_span: block.start_span, }, self.allocator, @@ -1585,7 +1585,7 @@ impl<'a> HtmlToR3Transform<'a> { nodes.push(R3Node::Text(Box::new_in( R3Text { - value: Atom::from(self.allocator.alloc_str(end_text)), + value: Ident::from(self.allocator.alloc_str(end_text)), source_span: end_span, }, self.allocator, @@ -1634,7 +1634,7 @@ impl<'a> HtmlToR3Transform<'a> { // Add start text node nodes.push(R3Node::Text(Box::new_in( R3Text { - value: Atom::from(self.allocator.alloc_str(start_text)), + value: Ident::from(self.allocator.alloc_str(start_text)), source_span: block.start_span, }, self.allocator, @@ -1659,7 +1659,7 @@ impl<'a> HtmlToR3Transform<'a> { nodes.push(R3Node::Text(Box::new_in( R3Text { - value: Atom::from(self.allocator.alloc_str(end_text)), + value: Ident::from(self.allocator.alloc_str(end_text)), source_span: end_span, }, self.allocator, @@ -1932,7 +1932,7 @@ impl<'a> HtmlToR3Transform<'a> { fn create_block_placeholder( &mut self, block_name: &str, - parameters: &[Atom<'a>], + parameters: &[Ident<'a>], source_span: Span, start_source_span: Span, end_source_span: Option, @@ -1949,15 +1949,15 @@ impl<'a> HtmlToR3Transform<'a> { self.block_placeholder_counter += 1; let start_name = if count == 0 { - Atom::from_in(format!("START_BLOCK_{block_upper}").as_str(), self.allocator) + Ident::from_in(format!("START_BLOCK_{block_upper}").as_str(), self.allocator) } else { - Atom::from_in(format!("START_BLOCK_{block_upper}_{count}").as_str(), self.allocator) + Ident::from_in(format!("START_BLOCK_{block_upper}_{count}").as_str(), self.allocator) }; let close_name = if count == 0 { - Atom::from_in(format!("CLOSE_BLOCK_{block_upper}").as_str(), self.allocator) + Ident::from_in(format!("CLOSE_BLOCK_{block_upper}").as_str(), self.allocator) } else { - Atom::from_in(format!("CLOSE_BLOCK_{block_upper}_{count}").as_str(), self.allocator) + Ident::from_in(format!("CLOSE_BLOCK_{block_upper}_{count}").as_str(), self.allocator) }; // Convert parameters to Atom vec @@ -1967,7 +1967,7 @@ impl<'a> HtmlToR3Transform<'a> { } let placeholder = I18nBlockPlaceholder { - name: Atom::from_in(block_name, self.allocator), + name: Ident::from_in(block_name, self.allocator), parameters: params, start_name, close_name, @@ -3177,7 +3177,7 @@ impl<'a> HtmlToR3Transform<'a> { ) -> R3BoundAttribute<'a> { let value_span = attr.value_span.unwrap_or(attr.span); let value = self.parse_binding_expression(&attr.value, value_span); - let name_atom = Atom::from(self.allocator.alloc_str(property_name)); + let name_atom = Ident::from(self.allocator.alloc_str(property_name)); // Look up security context based on element and property let security_context = get_security_context(element_name, property_name); @@ -3187,7 +3187,7 @@ impl<'a> HtmlToR3Transform<'a> { binding_type, security_context, value, - unit: unit.map(|u| Atom::from(self.allocator.alloc_str(u))), + unit: unit.map(|u| Ident::from(self.allocator.alloc_str(u))), source_span: attr.span, key_span: attr.name_span, value_span: attr.value_span, @@ -3196,7 +3196,7 @@ impl<'a> HtmlToR3Transform<'a> { } /// Parses a binding expression from an attribute value. - fn parse_binding_expression(&mut self, value: &Atom<'a>, span: Span) -> AngularExpression<'a> { + fn parse_binding_expression(&mut self, value: &Ident<'a>, span: Span) -> AngularExpression<'a> { let value_str = value.as_str(); if value_str.is_empty() { return self.create_empty_expression(span); @@ -3232,7 +3232,7 @@ impl<'a> HtmlToR3Transform<'a> { self.errors.push(error); } - let name_atom = Atom::from(self.allocator.alloc_str(property_name)); + let name_atom = Ident::from(self.allocator.alloc_str(property_name)); R3BoundAttribute { name: name_atom, @@ -3269,12 +3269,12 @@ impl<'a> HtmlToR3Transform<'a> { let (event_name, target) = if let Some(colon_pos) = name.find(':') { let target = &name[..colon_pos]; let event = &name[colon_pos + 1..]; - (event, Some(Atom::from(self.allocator.alloc_str(target)))) + (event, Some(Ident::from(self.allocator.alloc_str(target)))) } else { (name, None) }; - let name_atom = Atom::from(self.allocator.alloc_str(event_name)); + let name_atom = Ident::from(self.allocator.alloc_str(event_name)); R3BoundEvent { name: name_atom, @@ -3292,7 +3292,7 @@ impl<'a> HtmlToR3Transform<'a> { fn create_bound_event(&mut self, name: &str, attr: &HtmlAttribute<'a>) -> R3BoundEvent<'a> { let handler_span = self.calculate_event_handler_span(attr); let handler = self.parse_event_expression(&attr.value, handler_span); - let name_atom = Atom::from(self.allocator.alloc_str(name)); + let name_atom = Ident::from(self.allocator.alloc_str(name)); R3BoundEvent { name: name_atom, @@ -3330,13 +3330,13 @@ impl<'a> HtmlToR3Transform<'a> { let phase_str = &name[dot_pos + 1..]; // Phase is lowercased per TypeScript: matches[1].toLowerCase() let phase_lower = phase_str.to_lowercase(); - (event_name, Some(Atom::from(self.allocator.alloc_str(&phase_lower)))) + (event_name, Some(Ident::from(self.allocator.alloc_str(&phase_lower)))) } else { (name, None) }; // Use only the event name (trigger name), not including the phase - let name_atom = Atom::from(self.allocator.alloc_str(event_name)); + let name_atom = Ident::from(self.allocator.alloc_str(event_name)); R3BoundEvent { name: name_atom, @@ -3355,7 +3355,7 @@ impl<'a> HtmlToR3Transform<'a> { /// The `$event` is added separately in the ingest phase. fn create_two_way_event(&mut self, name: &str, attr: &HtmlAttribute<'a>) -> R3BoundEvent<'a> { let handler_span = self.calculate_event_handler_span(attr); - let name_atom = Atom::from(self.allocator.alloc_str(name)); + let name_atom = Ident::from(self.allocator.alloc_str(name)); // For two-way binding, the handler is just the target expression (e.g., `name`) // NOT the full assignment `name = $event`. @@ -3375,7 +3375,7 @@ impl<'a> HtmlToR3Transform<'a> { } /// Parses an event handler expression. - fn parse_event_expression(&mut self, value: &Atom<'a>, span: Span) -> AngularExpression<'a> { + fn parse_event_expression(&mut self, value: &Ident<'a>, span: Span) -> AngularExpression<'a> { let value_str = value.as_str(); if value_str.is_empty() { return self.create_empty_expression(span); @@ -3425,7 +3425,7 @@ impl<'a> HtmlToR3Transform<'a> { } // Still create the variable even if invalid (for error recovery) - let name_atom = Atom::from(self.allocator.alloc_str(name)); + let name_atom = Ident::from(self.allocator.alloc_str(name)); Some(R3Variable { name: name_atom, value: attr.value, @@ -3456,7 +3456,7 @@ impl<'a> HtmlToR3Transform<'a> { } // Still create the reference even if invalid (for error recovery) - let name_atom = Atom::from(self.allocator.alloc_str(name)); + let name_atom = Ident::from(self.allocator.alloc_str(name)); Some(R3Reference { name: name_atom, value: attr.value, @@ -3499,7 +3499,7 @@ impl<'a> HtmlToR3Transform<'a> { template_attr.name.strip_prefix('*').unwrap_or(&template_attr.name); // Allocate the directive name in the arena for long-lived reference let directive_name = self.allocator.alloc_str(directive_name); - let directive_name_atom = Atom::from(directive_name); + let directive_name_atom = Ident::from(directive_name); // Get the value span (if present) let value_span = template_attr.value_span.unwrap_or(template_attr.span); @@ -3580,7 +3580,7 @@ impl<'a> HtmlToR3Transform<'a> { ); attributes.push(R3TextAttribute { name: directive_name_atom, - value: Atom::from(""), + value: Ident::from(""), source_span: directive_source_span, key_span: Some(directive_source_span), value_span: None, @@ -3641,7 +3641,7 @@ impl<'a> HtmlToR3Transform<'a> { // Use the full binding name (e.g., "ngForOf", not "of") let full_name = expr.key.source.as_str(); - let binding_name_atom = Atom::from(self.allocator.alloc_str(full_name)); + let binding_name_atom = Ident::from(self.allocator.alloc_str(full_name)); let source_span = Span::new(expr.source_span.start, expr.source_span.end); // Parse the expression value from source @@ -3687,7 +3687,7 @@ impl<'a> HtmlToR3Transform<'a> { Span::new(template_attr.name_span.start + 1, template_attr.name_span.end); attributes.push(R3TextAttribute { name: directive_name_atom, - value: Atom::from(""), + value: Ident::from(""), source_span: directive_source_span, key_span: Some(directive_source_span), value_span: None, @@ -3765,7 +3765,7 @@ impl<'a> HtmlToR3Transform<'a> { let directive_name: &str = template_attr.name.strip_prefix('*').unwrap_or(&template_attr.name); let directive_name = self.allocator.alloc_str(directive_name); - let directive_name_atom = Atom::from(directive_name); + let directive_name_atom = Ident::from(directive_name); let value_span = template_attr.value_span.unwrap_or(template_attr.span); let key_span = Span::new(template_attr.name_span.start + 1, template_attr.name_span.end); @@ -3829,7 +3829,7 @@ impl<'a> HtmlToR3Transform<'a> { Span::new(template_attr.name_span.start + 1, template_attr.name_span.end); attributes.push(R3TextAttribute { name: directive_name_atom, - value: Atom::from(""), + value: Ident::from(""), source_span: directive_source_span, key_span: Some(directive_source_span), value_span: None, @@ -3884,7 +3884,7 @@ impl<'a> HtmlToR3Transform<'a> { } let full_name = expr.key.source.as_str(); - let binding_name_atom = Atom::from(self.allocator.alloc_str(full_name)); + let binding_name_atom = Ident::from(self.allocator.alloc_str(full_name)); let source_span = Span::new(expr.source_span.start, expr.source_span.end); let expr_value = if let Some(v) = &expr.value { @@ -3927,7 +3927,7 @@ impl<'a> HtmlToR3Transform<'a> { Span::new(template_attr.name_span.start + 1, template_attr.name_span.end); attributes.push(R3TextAttribute { name: directive_name_atom, - value: Atom::from(""), + value: Ident::from(""), source_span: directive_source_span, key_span: Some(directive_source_span), value_span: None, @@ -4016,12 +4016,12 @@ impl<'a> HtmlToR3Transform<'a> { /// Gets the tag name from a wrapped R3Node for template wrapping. /// Returns the element name for R3Element, "ng-content" for Content, None for R3Template. /// Reference: r3_template_transform.ts lines 1018-1026 - fn get_wrapped_tag_name(&self, node: &R3Node<'a>) -> Option> { + fn get_wrapped_tag_name(&self, node: &R3Node<'a>) -> Option> { match node { R3Node::Element(elem) => Some(elem.name), R3Node::Template(_) => None, // Content has a readonly name = 'ng-content' in TypeScript - R3Node::Content(_) => Some(Atom::from("ng-content")), + R3Node::Content(_) => Some(Ident::from("ng-content")), _ => None, } } @@ -4078,16 +4078,16 @@ impl<'a> HtmlToR3Transform<'a> { let mut placeholder_attrs = HashMap::new_in(self.allocator); for (k, v) in attrs { placeholder_attrs.insert( - Atom::from(self.allocator.alloc_str(&k)), - Atom::from(self.allocator.alloc_str(&v)), + Ident::from(self.allocator.alloc_str(&k)), + Ident::from(self.allocator.alloc_str(&v)), ); } I18nMeta::Node(I18nNode::TagPlaceholder(I18nTagPlaceholder { - tag: Atom::from(self.allocator.alloc_str(tag_name)), + tag: Ident::from(self.allocator.alloc_str(tag_name)), attrs: placeholder_attrs, - start_name: Atom::from(self.allocator.alloc_str(&start_name)), - close_name: Atom::from(self.allocator.alloc_str(&close_name)), + start_name: Ident::from(self.allocator.alloc_str(&start_name)), + close_name: Ident::from(self.allocator.alloc_str(&close_name)), children: Vec::new_in(self.allocator), is_void, source_span: element.span, @@ -4131,16 +4131,16 @@ impl<'a> HtmlToR3Transform<'a> { let mut placeholder_attrs = HashMap::new_in(self.allocator); for (k, v) in attrs { placeholder_attrs.insert( - Atom::from(self.allocator.alloc_str(&k)), - Atom::from(self.allocator.alloc_str(&v)), + Ident::from(self.allocator.alloc_str(&k)), + Ident::from(self.allocator.alloc_str(&v)), ); } I18nMeta::Node(I18nNode::TagPlaceholder(I18nTagPlaceholder { - tag: Atom::from(self.allocator.alloc_str(tag_name)), + tag: Ident::from(self.allocator.alloc_str(tag_name)), attrs: placeholder_attrs, - start_name: Atom::from(self.allocator.alloc_str(&start_name)), - close_name: Atom::from(self.allocator.alloc_str(&close_name)), + start_name: Ident::from(self.allocator.alloc_str(&start_name)), + close_name: Ident::from(self.allocator.alloc_str(&close_name)), children: Vec::new_in(self.allocator), is_void, source_span: component.span, @@ -4201,7 +4201,7 @@ impl<'a> HtmlToR3Transform<'a> { ) -> R3Variable<'a> { let name = var.key.source; // When value is None, Angular uses "$implicit" as the default binding - let value = var.value.as_ref().map_or(Atom::from("$implicit"), |v| v.source); + let value = var.value.as_ref().map_or(Ident::from("$implicit"), |v| v.source); let value_span = var.value.as_ref().map(|v| Span::new(v.span.start, v.span.end)); R3Variable { @@ -4252,7 +4252,7 @@ impl<'a> HtmlToR3Transform<'a> { if start > 0 { let text_before = &text[current_pos..abs_start]; if !text_before.is_empty() { - let text_atom = Atom::from_in(text_before, self.allocator); + let text_atom = Ident::from_in(text_before, self.allocator); children.push(I18nNode::Text(I18nText { value: text_atom, source_span: span })); } } @@ -4266,8 +4266,8 @@ impl<'a> HtmlToR3Transform<'a> { // Generate placeholder name using the i18n placeholder registry let placeholder_name = self.i18n_placeholder_registry.get_placeholder_name("INTERPOLATION", expr); - let name_atom = Atom::from_in(&placeholder_name, self.allocator); - let value_atom = Atom::from_in(expr, self.allocator); + let name_atom = Ident::from_in(&placeholder_name, self.allocator); + let value_atom = Ident::from_in(expr, self.allocator); children.push(I18nNode::Placeholder(I18nPlaceholder { value: value_atom, @@ -4287,7 +4287,7 @@ impl<'a> HtmlToR3Transform<'a> { if current_pos < text.len() { let remaining = &text[current_pos..]; if !remaining.is_empty() { - let text_atom = Atom::from_in(remaining, self.allocator); + let text_atom = Ident::from_in(remaining, self.allocator); children.push(I18nNode::Text(I18nText { value: text_atom, source_span: span })); } } @@ -4350,7 +4350,7 @@ impl<'a> HtmlToR3Transform<'a> { if token.parts.len() >= 3 { // Before adding an expression, commit the current string buffer // (even if empty, we need a string before each expression) - strings.push(Atom::from_in(current_string.as_str(), self.allocator)); + strings.push(Ident::from_in(current_string.as_str(), self.allocator)); current_string.clear(); let start_marker = &token.parts[0]; @@ -4394,7 +4394,7 @@ impl<'a> HtmlToR3Transform<'a> { } // Commit the trailing string (after the last expression) - strings.push(Atom::from_in(current_string.as_str(), self.allocator)); + strings.push(Ident::from_in(current_string.as_str(), self.allocator)); // Create the Interpolation expression let span = ParseSpan::new(0, text.span.end - text.span.start); @@ -4442,7 +4442,7 @@ impl<'a> HtmlToR3Transform<'a> { if token.parts.len() >= 3 { // Before adding an expression, commit the current string buffer // (even if empty, we need a string before each expression) - strings.push(Atom::from_in(current_string.as_str(), self.allocator)); + strings.push(Ident::from_in(current_string.as_str(), self.allocator)); current_string.clear(); let start_marker = &token.parts[0]; @@ -4483,7 +4483,7 @@ impl<'a> HtmlToR3Transform<'a> { } // Commit the trailing string (after the last expression) - strings.push(Atom::from_in(current_string.as_str(), self.allocator)); + strings.push(Ident::from_in(current_string.as_str(), self.allocator)); // Create the Interpolation expression let span = ParseSpan::new(0, value_span.size()); @@ -4545,14 +4545,14 @@ impl<'a> HtmlToR3Transform<'a> { (BindingType::Property, name, None, security_context) }; - let name_atom = Atom::from(self.allocator.alloc_str(final_name)); + let name_atom = Ident::from(self.allocator.alloc_str(final_name)); Some(R3BoundAttribute { name: name_atom, binding_type, security_context, value: expr, - unit: unit.map(|u| Atom::from(self.allocator.alloc_str(u))), + unit: unit.map(|u| Ident::from(self.allocator.alloc_str(u))), source_span: attr.span, key_span: attr.name_span, value_span: attr.value_span, @@ -4630,7 +4630,7 @@ impl<'a> HtmlToR3Transform<'a> { } /// Gets text content from an element. - fn get_text_content(&self, element: &HtmlElement<'a>) -> Option> { + fn get_text_content(&self, element: &HtmlElement<'a>) -> Option> { if element.children.len() == 1 && let HtmlNode::Text(text) = &element.children[0] { @@ -4642,7 +4642,7 @@ impl<'a> HtmlToR3Transform<'a> { /// Gets the stylesheet href from a link element. /// Only returns resolvable URLs per Angular's `isStyleUrlResolvable`. /// Reference: style_url_resolver.ts lines 12-18 - fn get_stylesheet_href(&self, element: &HtmlElement<'a>) -> Option> { + fn get_stylesheet_href(&self, element: &HtmlElement<'a>) -> Option> { let mut is_stylesheet = false; let mut href = None; @@ -4671,13 +4671,13 @@ impl<'a> HtmlToR3Transform<'a> { } /// Gets the ng-content selector. - fn get_ng_content_selector(&self, element: &HtmlElement<'a>) -> Atom<'a> { + fn get_ng_content_selector(&self, element: &HtmlElement<'a>) -> Ident<'a> { for attr in &element.attrs { if attr.name.as_str() == "select" && !attr.value.is_empty() { return attr.value; } } - Atom::from("*") + Ident::from("*") } /// Checks if an element is a void element. @@ -4777,12 +4777,12 @@ fn parse_i18n_meta_with_message<'a>( I18nMeta::Message(I18nMessage { instance_id, nodes: Vec::new_in(allocator), - meaning: Atom::from_in(meaning, allocator), - description: Atom::from_in(description, allocator), - custom_id: Atom::from_in(custom_id, allocator), - id: Atom::from(""), + meaning: Ident::from_in(meaning, allocator), + description: Ident::from_in(description, allocator), + custom_id: Ident::from_in(custom_id, allocator), + id: Ident::from(""), legacy_ids: Vec::new_in(allocator), - message_string: Atom::from_in(message_string, allocator), + message_string: Ident::from_in(message_string, allocator), }) } diff --git a/crates/oxc_angular_compiler/tests/expression_parser_test.rs b/crates/oxc_angular_compiler/tests/expression_parser_test.rs index 334342882..98da0b78c 100644 --- a/crates/oxc_angular_compiler/tests/expression_parser_test.rs +++ b/crates/oxc_angular_compiler/tests/expression_parser_test.rs @@ -1089,7 +1089,7 @@ mod parse_template_bindings { /// Helper to extract key source from ASTWithSource if present. fn get_value_source<'a>(value: &'a Option>) -> Option<&'a str> { - value.as_ref().and_then(|v| v.source.as_ref().map(oxc_span::Atom::as_str)) + value.as_ref().and_then(|v| v.source.as_ref().map(oxc_span::Ident::as_str)) } /// Humanize bindings into (key, value, is_variable) tuples. @@ -1128,7 +1128,7 @@ mod parse_template_bindings { let allocator = Box::leak(Box::new(Allocator::default())); let parser = Parser::new(allocator, value); let template_key = TemplateBindingIdentifier { - source: oxc_span::Atom::from(key), + source: oxc_span::Ident::from(key), span: oxc_angular_compiler::ast::expression::AbsoluteSourceSpan::new( 0, key.len() as u32, @@ -1288,7 +1288,7 @@ mod parse_interpolation { fn get_strings<'a>(result: &'a ParseResult<'a>) -> Vec<&'a str> { match &result.ast { AngularExpression::Interpolation(interp) => { - interp.strings.iter().map(oxc_span::Atom::as_str).collect() + interp.strings.iter().map(oxc_span::Ident::as_str).collect() } _ => vec![], } diff --git a/crates/oxc_angular_compiler/tests/integration_test.rs b/crates/oxc_angular_compiler/tests/integration_test.rs index 9fcaebe71..31c8b605c 100644 --- a/crates/oxc_angular_compiler/tests/integration_test.rs +++ b/crates/oxc_angular_compiler/tests/integration_test.rs @@ -13,7 +13,7 @@ use oxc_angular_compiler::{ transform::html_to_r3::{HtmlToR3Transform, TransformOptions}, transform_angular_file, }; -use oxc_span::Atom; +use oxc_span::Ident; /// Compiles an Angular template to JavaScript. fn compile_template_to_js(template: &str, component_name: &str) -> String { @@ -55,12 +55,12 @@ fn compile_template_to_js_with_version( let options = IngestOptions { angular_version: Some(version), ..Default::default() }; ingest_component_with_options( &allocator, - Atom::from(component_name), + Ident::from(component_name), r3_result.nodes, options, ) } else { - ingest_component(&allocator, Atom::from(component_name), r3_result.nodes) + ingest_component(&allocator, Ident::from(component_name), r3_result.nodes) }; // Stage 4-5: Transform and emit @@ -914,13 +914,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); // The consts array should contain ["ngFor", "ngForOf"] in that order // This is emitted under the Template marker (4) in the attribute array @@ -975,13 +969,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -1037,13 +1025,7 @@ export class ExternalComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -1188,6 +1170,130 @@ fn test_nested_for_with_outer_scope_track() { insta::assert_snapshot!("nested_for_with_outer_scope_track", js); } +/// Tests that `track prefix() + item.id` generates a regular function (not arrow function). +/// When a binary expression in track contains a component method call, the generated +/// track function must use `function` declaration to properly bind `this`. +#[test] +fn test_for_track_binary_with_component_method() { + let js = compile_template_to_js( + r#"@for (item of items; track prefix() + item.id) {
{{item.name}}
}"#, + "TestComponent", + ); + // Must generate a regular function, not an arrow function, because prefix() needs `this` + assert!( + js.contains("function _forTrack"), + "Track with binary operator containing component method should generate a regular function. Output:\n{js}" + ); + assert!( + js.contains("this.prefix()"), + "Track function should use 'this.prefix()' for component method access. Output:\n{js}" + ); + // Must NOT be an arrow function (arrow functions don't bind `this`) + assert!( + !js.contains("const _forTrack"), + "Should NOT generate an arrow function (const _forTrack = ...) for track expressions that reference component members. Output:\n{js}" + ); + insta::assert_snapshot!("for_track_binary_with_component_method", js); +} + +/// Tests that nullish coalescing (??) in track with component method generates a regular function. +/// This is the exact pattern from the original bug report: `track item.prefix ?? defaultPrefix()` +#[test] +fn test_for_track_nullish_coalescing_with_component_method() { + let js = compile_template_to_js( + r#"@for (item of items; track item.prefix ?? defaultPrefix()) {
{{item.name}}
}"#, + "TestComponent", + ); + assert!( + js.contains("function _forTrack"), + "Track with ?? operator containing component method should generate a regular function. Output:\n{js}" + ); + assert!( + !js.contains("const _forTrack"), + "Should NOT generate an arrow function for track with ?? referencing component members. Output:\n{js}" + ); + insta::assert_snapshot!("for_track_nullish_coalescing_with_component_method", js); +} + +/// Tests that ternary in track with component method generates a regular function. +#[test] +fn test_for_track_ternary_with_component_method() { + let js = compile_template_to_js( + r#"@for (item of items; track useId() ? item.id : item.name) {
{{item.name}}
}"#, + "TestComponent", + ); + assert!( + js.contains("function _forTrack"), + "Track with ternary containing component method should generate a regular function. Output:\n{js}" + ); + assert!( + !js.contains("const _forTrack"), + "Should NOT generate an arrow function for track with ternary referencing component members. Output:\n{js}" + ); + insta::assert_snapshot!("for_track_ternary_with_component_method", js); +} + +/// Tests that a complex track expression with multiple component references and binary operators +/// generates a regular function. Mirrors the original bug: `(tag.queryPrefix ?? queryPrefix()) + '.' + tag.key` +#[test] +fn test_for_track_complex_binary_with_nullish_coalescing() { + let js = compile_template_to_js( + r#"@for (tag of visibleTags(); track (tag.queryPrefix ?? queryPrefix()) + '.' + tag.key) { {{ tag.key }} }"#, + "TestComponent", + ); + assert!( + js.contains("function _forTrack"), + "Complex track with ?? and + containing component method should generate a regular function. Output:\n{js}" + ); + assert!( + !js.contains("const _forTrack"), + "Should NOT generate an arrow function. Output:\n{js}" + ); + assert!( + js.contains("this.queryPrefix()"), + "Track function should use 'this.queryPrefix()' for component method access. Output:\n{js}" + ); + insta::assert_snapshot!("for_track_complex_binary_with_nullish_coalescing", js); +} + +/// Tests that a track expression with only item property reads in binary operators +/// correctly generates an arrow function (no component context needed). +#[test] +fn test_for_track_binary_without_component_context() { + let js = compile_template_to_js( + r#"@for (item of items; track item.type + ':' + item.id) {
{{item.name}}
}"#, + "TestComponent", + ); + // This should be an arrow function since no component members are referenced + assert!( + js.contains("const _forTrack"), + "Track with binary operator using only item properties should generate an arrow function. Output:\n{js}" + ); + assert!( + !js.contains("function _forTrack"), + "Should NOT generate a regular function when no component members are referenced. Output:\n{js}" + ); + insta::assert_snapshot!("for_track_binary_without_component_context", js); +} + +/// Tests that negation (!) in track with component method generates a regular function. +#[test] +fn test_for_track_not_with_component_method() { + let js = compile_template_to_js( + r#"@for (item of items; track !isDisabled()) {
{{item.name}}
}"#, + "TestComponent", + ); + assert!( + js.contains("function _forTrack"), + "Track with ! operator containing component method should generate a regular function. Output:\n{js}" + ); + assert!( + !js.contains("const _forTrack"), + "Should NOT generate an arrow function. Output:\n{js}" + ); + insta::assert_snapshot!("for_track_not_with_component_method", js); +} + #[test] fn test_if_inside_for() { let js = compile_template_to_js( @@ -1463,13 +1569,7 @@ import { Component } from '@angular/core'; export class StyledComponent {} "#; - let result = transform_angular_file( - &allocator, - "styled.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "styled.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -1506,13 +1606,8 @@ import { Component } from '@angular/core'; export class MultiStyledComponent {} "; - let result = transform_angular_file( - &allocator, - "multi-styled.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = + transform_angular_file(&allocator, "multi-styled.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors()); @@ -1524,6 +1619,35 @@ export class MultiStyledComponent {} insta::assert_snapshot!("component_with_multiple_styles", result.code); } +#[test] +fn test_component_with_minified_styles() { + let allocator = Allocator::default(); + let source = r#" +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-styled', + template: '
Hello
', + styles: ['.container { color: red; background: transparent; }'] +}) +export class StyledComponent {} +"#; + + let mut options = ComponentTransformOptions::default(); + options.minify_component_styles = true; + + let result = + transform_angular_file(&allocator, "styled.component.ts", source, Some(&options), None); + + assert_eq!(result.component_count, 1); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + assert!( + result.code.contains(".container[_ngcontent-%COMP%]{color:red;background:0 0}"), + "Generated code should contain minified component styles: {}", + result.code + ); +} + #[test] fn test_component_without_styles_downgrades_encapsulation() { let allocator = Allocator::default(); @@ -1537,13 +1661,7 @@ import { Component } from '@angular/core'; export class NoStylesComponent {} "; - let result = transform_angular_file( - &allocator, - "no-styles.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "no-styles.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors()); @@ -1722,13 +1840,7 @@ import { Component } from '@angular/core'; export class BadgeComponent {} "; - let result = transform_angular_file( - &allocator, - "badge.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "badge.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -1797,13 +1909,7 @@ export class SecondComponent { } "; - let result = transform_angular_file( - &allocator, - "multi.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "multi.component.ts", source, None, None); assert_eq!(result.component_count, 2, "Should compile both components"); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -1904,13 +2010,7 @@ export class GridComponent { } "; - let result = transform_angular_file( - &allocator, - "grid.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "grid.component.ts", source, None, None); assert_eq!(result.component_count, 1, "Should compile the component"); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -1980,13 +2080,7 @@ import { Component } from '@angular/core'; export class MenuComponent {} "; - let result = transform_angular_file( - &allocator, - "menu.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "menu.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -2016,13 +2110,7 @@ import { Component } from '@angular/core'; export class MultiMenuComponent {} "; - let result = transform_angular_file( - &allocator, - "multi-menu.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "multi-menu.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -2061,13 +2149,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -2120,13 +2202,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert_eq!(result.component_count, 1); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -2262,13 +2338,7 @@ export class MatFabButton { export class MatMiniFabButton {} "; - let result = transform_angular_file( - &allocator, - "fab.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "fab.ts", source, None, None); assert_eq!(result.component_count, 2, "Should compile both components"); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -2359,13 +2429,7 @@ export class MatMiniFabButton {{ " ); - let result = transform_angular_file( - &allocator, - "fab.ts", - &source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "fab.ts", &source, None, None); assert_eq!(result.component_count, 2, "Should compile both components"); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -2467,13 +2531,7 @@ export class MatMiniFabButton { let resources = ResolvedResources { templates, styles: std::collections::HashMap::new() }; - let result = transform_angular_file( - &allocator, - "fab.ts", - source, - &ComponentTransformOptions::default(), - Some(&resources), - ); + let result = transform_angular_file(&allocator, "fab.ts", source, None, Some(&resources)); assert_eq!(result.component_count, 2, "Should compile both components"); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -2559,13 +2617,7 @@ export class MatDrawerContainer { } "; - let result = transform_angular_file( - &allocator, - "drawer.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "drawer.ts", source, None, None); assert_eq!(result.component_count, 3, "Should compile all three components"); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -2633,13 +2685,7 @@ export class TestComponent { } "; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -2695,13 +2741,7 @@ export class TestComponent { } "; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -2757,13 +2797,7 @@ export class BitLabelComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -2840,13 +2874,7 @@ export class TestComponent { } "; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -3031,8 +3059,7 @@ import { Component } from '@angular/core'; export class SvgInSwitchCaseComponent {} "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.ts", source, None, None); // Verify that conditionalCreate uses ":svg:svg" not just "svg" assert!( @@ -3094,13 +3121,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); eprintln!("OUTPUT:\n{}", result.code); @@ -3342,13 +3363,8 @@ export class DashboardBoxComponent { } "#; - let result = transform_angular_file( - &allocator, - "dashboard-box.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = + transform_angular_file(&allocator, "dashboard-box.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -3598,8 +3614,7 @@ export class TestComponent { "; // OXC always uses Full mode (elementStart, not domElementStart) - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.ts", source, None, None); // Should use elementStart (Full mode), NOT domElementStart (DomOnly mode) assert!( @@ -3637,8 +3652,7 @@ import { Component } from '@angular/core'; export class TestComponent {} "; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.ts", source, None, None); // OXC in local compilation mode: always Full mode for component templates assert!( @@ -3668,8 +3682,7 @@ import { Component } from '@angular/core'; export class TestComponent {} "; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.ts", source, None, None); // Non-standalone should always use Full mode assert!( @@ -3886,7 +3899,7 @@ export class TestComponent {} angular_version: Some(AngularVersion::new(21, 0, 0)), ..Default::default() }; - let result = transform_angular_file(&allocator, "test.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.ts", source, Some(&options), None); // Should use Full mode (elementStart), NOT DomOnly (domElementStart) assert!( @@ -3923,7 +3936,7 @@ export class TestComponent {} angular_version: Some(AngularVersion::new(21, 0, 0)), ..Default::default() }; - let result = transform_angular_file(&allocator, "test.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.ts", source, Some(&options), None); // Implicit standalone + empty imports should still use Full mode assert!( @@ -3956,8 +3969,7 @@ import { Component } from '@angular/core'; export class TestComponent {} "; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.ts", source, None, None); // OXC in local compilation mode: always Full mode for component templates assert!( @@ -3993,8 +4005,7 @@ export class TestComponent { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.ts", source, None, None); // OXC in local compilation mode: always Full mode for component templates assert!( @@ -4028,8 +4039,7 @@ import { AsyncPipe, DatePipe, SlicePipe } from '@angular/common'; export class TestComponent {} "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.ts", source, None, None); // OXC in local compilation mode: always Full mode for component templates assert!( @@ -4064,8 +4074,7 @@ export class HighlightDirective {} export class TestComponent {} "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.ts", source, None, None); assert!( result.code.contains("ɵɵelementStart"), @@ -4134,13 +4143,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); // Pure property bindings keep Bindings marker (3), NOT I18n marker (6). // The i18n marker on a property binding is a no-op for directive matching. @@ -4172,13 +4175,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); // The consts array should contain [6,"heading"] (AttributeMarker.I18n = 6) // because the interpolated attribute has an i18n message (i18n-heading). @@ -4225,13 +4222,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); // The consts array should NOT contain [6,"cuTooltip"] because [cuTooltip]="expr" // is a pure property binding, not an interpolated attribute. @@ -4395,7 +4386,8 @@ export class TestComponent { ..ComponentTransformOptions::default() }; - let result = transform_angular_file(&allocator, "test.component.ts", source, &options, None); + let result = + transform_angular_file(&allocator, "test.component.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -4454,7 +4446,8 @@ export class TestComponent { ..ComponentTransformOptions::default() }; - let result = transform_angular_file(&allocator, "test.component.ts", source, &options, None); + let result = + transform_angular_file(&allocator, "test.component.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -4499,7 +4492,8 @@ export class TestComponent { ..ComponentTransformOptions::default() }; - let result = transform_angular_file(&allocator, "test.component.ts", source, &options, None); + let result = + transform_angular_file(&allocator, "test.component.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -4552,13 +4546,7 @@ import { Component } from '@angular/core'; export class IconComponent {} "#; - let result = transform_angular_file( - &allocator, - "icon.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "icon.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); let code = &result.code; eprintln!("OUTPUT:\n{code}"); @@ -4603,13 +4591,7 @@ export class IconComponent { } "#; - let result = transform_angular_file( - &allocator, - "icon.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "icon.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); let code = &result.code; eprintln!("OUTPUT:\n{code}"); @@ -4655,13 +4637,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); let code = &result.code; eprintln!("OUTPUT:\n{code}"); @@ -4886,7 +4862,7 @@ export class ToastPositionHelperDirective { &allocator, "toast-position-helper.directive.ts", source, - &ComponentTransformOptions::default(), + None, None, ); @@ -4949,13 +4925,7 @@ export class MultiDepDirective { } "#; - let result = transform_angular_file( - &allocator, - "multi-dep.directive.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "multi-dep.directive.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5021,13 +4991,8 @@ export class ChatBotTriggerComponent { } "#; - let result = transform_angular_file( - &allocator, - "chatbot-trigger.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = + transform_angular_file(&allocator, "chatbot-trigger.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5081,13 +5046,7 @@ export class LoginFormComponent { } "#; - let result = transform_angular_file( - &allocator, - "login-form.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "login-form.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5154,13 +5113,7 @@ export class TabsComponent { } "#; - let result = transform_angular_file( - &allocator, - "tabs.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "tabs.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5223,13 +5176,7 @@ export class MixedQueryComponent { } "#; - let result = transform_angular_file( - &allocator, - "mixed-query.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "mixed-query.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5478,13 +5425,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5520,13 +5461,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5561,13 +5496,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5604,13 +5533,7 @@ import { Component } from '@angular/core'; export class TestComponent {} "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5637,13 +5560,7 @@ import { Component } from '@angular/core'; export class TestComponent {} "; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5685,13 +5602,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5740,13 +5651,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5805,13 +5710,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "test.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); @@ -5905,7 +5804,8 @@ export class TestComponent {} "#; let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.component.ts", source, &options, None); + let result = + transform_angular_file(&allocator, "test.component.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); let normalized = result.code.replace([' ', '\n', '\t'], ""); @@ -5932,8 +5832,7 @@ import { Component } from '@angular/core'; export class LegacyComponent {} "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); let normalized = result.code.replace([' ', '\n', '\t'], ""); @@ -5964,7 +5863,8 @@ export class ImplicitStandaloneComponent {} "#; let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.component.ts", source, &options, None); + let result = + transform_angular_file(&allocator, "test.component.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); let normalized = result.code.replace([' ', '\n', '\t'], ""); @@ -5997,7 +5897,8 @@ export class AppComponent {} "#; let options = ComponentTransformOptions { jit: true, ..Default::default() }; - let result = transform_angular_file(&allocator, "app.component.ts", source, &options, None); + let result = + transform_angular_file(&allocator, "app.component.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); // Should have __decorate import from tslib @@ -6048,7 +5949,8 @@ export class AppComponent {} "#; let options = ComponentTransformOptions { jit: true, ..Default::default() }; - let result = transform_angular_file(&allocator, "app.component.ts", source, &options, None); + let result = + transform_angular_file(&allocator, "app.component.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); // Should have resource import for template @@ -6085,7 +5987,8 @@ export class AppComponent {} "#; let options = ComponentTransformOptions { jit: true, ..Default::default() }; - let result = transform_angular_file(&allocator, "app.component.ts", source, &options, None); + let result = + transform_angular_file(&allocator, "app.component.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); // Should have resource import for style @@ -6116,7 +6019,8 @@ export class AppComponent { "#; let options = ComponentTransformOptions { jit: true, ..Default::default() }; - let result = transform_angular_file(&allocator, "app.component.ts", source, &options, None); + let result = + transform_angular_file(&allocator, "app.component.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); // Should have ctorParameters static property @@ -6153,7 +6057,8 @@ export class AppComponent { "#; let options = ComponentTransformOptions { jit: true, ..Default::default() }; - let result = transform_angular_file(&allocator, "app.component.ts", source, &options, None); + let result = + transform_angular_file(&allocator, "app.component.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); // Should have let declaration @@ -6191,7 +6096,7 @@ export class HighlightDirective { let options = ComponentTransformOptions { jit: true, ..Default::default() }; let result = - transform_angular_file(&allocator, "highlight.directive.ts", source, &options, None); + transform_angular_file(&allocator, "highlight.directive.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); // Should have __decorate with Directive @@ -6201,181 +6106,1331 @@ export class HighlightDirective { result.code ); - // Should NOT have ɵdir or ɵfac + // Should NOT have ɵdir or ɵfac + assert!( + !result.code.contains("ɵdir") && !result.code.contains("ɵfac"), + "JIT directive output should NOT contain AOT definitions. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_directive", result.code); +} + +#[test] +fn test_jit_full_component_example() { + // Full example matching the issue #97 scenario + let allocator = Allocator::default(); + let source = r#" +import { Component, signal } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; +import { Lib1 } from 'lib1'; +import { TitleService } from './title.service'; + +@Component({ + selector: 'app-root', + imports: [RouterOutlet, Lib1], + templateUrl: './app.html', + styleUrl: './app.css', +}) +export class App { + titleService; + title = signal('app'); + constructor(titleService: TitleService) { + this.titleService = titleService; + this.title.set(this.titleService.getTitle()); + } +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = + transform_angular_file(&allocator, "app.component.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // Should have all JIT characteristics + assert!( + result.code.contains("import { __decorate } from \"tslib\""), + "Missing __decorate import" + ); + assert!( + result.code.contains("angular:jit:template:file;./app.html"), + "Missing template resource import" + ); + assert!( + result.code.contains("angular:jit:style:file;./app.css"), + "Missing style resource import" + ); + assert!(result.code.contains("let App = class App"), "Missing class restructuring"); + assert!(result.code.contains("ctorParameters"), "Missing ctorParameters"); + assert!(result.code.contains("__decorate("), "Missing __decorate call"); + assert!(result.code.contains("export { App }"), "Missing named export"); + + // Should NOT have AOT output + assert!(!result.code.contains("ɵcmp"), "Should not contain ɵcmp"); + assert!(!result.code.contains("ɵfac"), "Should not contain ɵfac"); + assert!(!result.code.contains("defineComponent"), "Should not contain defineComponent"); + + insta::assert_snapshot!("jit_full_component", result.code); +} + +#[test] +fn test_jit_prop_decorators_emitted() { + // Bug fix: member decorators (@Input, @Output, etc.) must be downleveled + // to static propDecorators so Angular's JIT runtime can discover inputs/outputs. + // Without this, @Input/@Output decorators are silently lost, breaking data binding. + let allocator = Allocator::default(); + let source = r#" +import { Directive, Input, Output, HostBinding, EventEmitter } from '@angular/core'; + +@Directive({ + selector: '[appHighlight]', +}) +export class HighlightDirective { + @Input() color: string = 'yellow'; + @Input('aliasName') title: string = ''; + @Output() colorChange = new EventEmitter(); + @HostBinding('class.active') isActive = false; +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = + transform_angular_file(&allocator, "highlight.directive.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // propDecorators must be present — Angular's JIT runtime reads this + assert!( + result.code.contains("propDecorators"), + "JIT output must emit static propDecorators. Got:\n{}", + result.code + ); + + // Each decorated member should appear in propDecorators + assert!(result.code.contains("color:"), "propDecorators should list 'color'"); + assert!(result.code.contains("title:"), "propDecorators should list 'title'"); + assert!(result.code.contains("colorChange:"), "propDecorators should list 'colorChange'"); + assert!(result.code.contains("isActive:"), "propDecorators should list 'isActive'"); + + // The decorator type references should be present + assert!(result.code.contains("type: Input"), "propDecorators should reference Input"); + assert!(result.code.contains("type: Output"), "propDecorators should reference Output"); + assert!( + result.code.contains("type: HostBinding"), + "propDecorators should reference HostBinding" + ); + + // The original decorators must be removed from the class body + assert!(!result.code.contains("@Input()"), "@Input decorator must be removed from class body"); + assert!( + !result.code.contains("@Output()"), + "@Output decorator must be removed from class body" + ); + + insta::assert_snapshot!("jit_prop_decorators", result.code); +} + +#[test] +fn test_jit_union_type_ctor_params() { + // Angular-aligned union type behavior for ctorParameters. + // Angular's typeReferenceToExpression filters ONLY `null` literal types. + // If exactly one non-null type remains, it resolves; otherwise unresolvable. + // + // `T | null` → resolves to T (1 non-null type) + // `undefined | T` → unresolvable (2 non-null types: undefined + T) + // `null | undefined | T` → unresolvable (2 non-null types: undefined + T) + // + // See: angular/packages/compiler-cli/src/ngtsc/transform/jit/src/downlevel_decorators_transform.ts + let allocator = Allocator::default(); + let source = r#" +import { Component } from '@angular/core'; +import { ServiceA } from './a.service'; +import { ServiceB } from './b.service'; +import { ServiceC } from './c.service'; + +@Component({ selector: 'test', template: '' }) +export class TestComponent { + constructor( + svcA: undefined | ServiceA, + svcB: null | undefined | ServiceB, + svcC: ServiceC | null, + ) {} +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = + transform_angular_file(&allocator, "test.component.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // `ServiceC | null` resolves correctly (1 non-null type) + assert!( + result.code.contains("type: ServiceC"), + "ctorParameters should resolve 'ServiceC | null' to ServiceC. Got:\n{}", + result.code + ); + + // `undefined | ServiceA` and `null | undefined | ServiceB` are unresolvable per Angular spec + // (2 non-null types remain after filtering null) + assert!( + !result.code.contains("type: ServiceA"), + "ctorParameters must not resolve 'undefined | ServiceA' (2 non-null types). Got:\n{}", + result.code + ); + assert!( + !result.code.contains("type: ServiceB"), + "ctorParameters must not resolve 'null | undefined | ServiceB' (2 non-null types). Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_union_type_ctor_params", result.code); +} + +#[test] +fn test_jit_abstract_class() { + let allocator = Allocator::default(); + let source = r#" +import { Injectable } from '@angular/core'; + +@Injectable() +export abstract class BaseProvider { + protected abstract get name(): string; + protected abstract initialize(): void; + + public greet(): string { + return `Hello from ${this.name}`; + } +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = + transform_angular_file(&allocator, "base.provider.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // The abstract keyword should NOT appear before "class" in the output + // (JIT converts to class expression which can't be abstract) + assert!( + !result.code.contains("abstract class"), + "JIT output should not contain 'abstract class'. Got:\n{}", + result.code + ); + + // Should have proper class expression + assert!( + result.code.contains("let BaseProvider = class BaseProvider"), + "JIT output should have class expression. Got:\n{}", + result.code + ); + + // Should have __decorate call + assert!( + result.code.contains("__decorate("), + "JIT output should use __decorate. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_abstract_class", result.code); +} + +#[test] +fn test_jit_non_angular_class_decorators_lowered() { + // When a class has both Angular and non-Angular class-level decorators, + // ALL decorators must be lowered into the __decorate() call. + // Non-Angular decorators left as raw @Decorator syntax on a class expression + // cause TS1206 (decorators are not valid on class expressions). + let allocator = Allocator::default(); + let source = r#" +import { Injectable } from '@angular/core'; +import { State } from '@ngxs/store'; + +interface TodoStateModel { + items: string[]; +} + +@State({ name: 'todo', defaults: { items: [] } }) +@Injectable() +export class TodoState {} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = transform_angular_file(&allocator, "todo.state.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // No raw @State decorator should remain in the output + assert!( + !result.code.contains("@State"), + "Non-Angular class decorators should be lowered, not left as raw syntax. Got:\n{}", + result.code + ); + + // Both decorators should appear in the __decorate call + assert!( + result.code.contains("State("), + "Non-Angular class decorator State should appear in __decorate call. Got:\n{}", + result.code + ); + assert!( + result.code.contains("Injectable()"), + "Angular class decorator Injectable should appear in __decorate call. Got:\n{}", + result.code + ); + + // Decorator order should be preserved (State before Injectable) + let state_pos = result.code.find("State(").unwrap(); + let injectable_pos = result.code.find("Injectable()").unwrap(); + assert!( + state_pos < injectable_pos, + "Decorator order should be preserved (State before Injectable). Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_non_angular_class_decorators", result.code); +} + +#[test] +fn test_jit_non_angular_method_decorators_lowered() { + // Non-Angular method decorators should be lowered to __decorate() calls + // on the class prototype (for instance methods) or class itself (for static methods). + let allocator = Allocator::default(); + let source = r#" +import { Injectable } from '@angular/core'; +import { State, Action, Selector } from '@ngxs/store'; + +@State({ name: 'todo' }) +@Injectable() +export class TodoState { + @Selector() + static todos(state: any): any[] { return state.items; } + + @Action(AddTodo) + add(ctx: any, action: any) { ctx.setState(action); } +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = transform_angular_file(&allocator, "todo.state.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // No raw @Selector or @Action decorator should remain + assert!( + !result.code.contains("@Selector"), + "Non-Angular method decorators should be lowered. Got:\n{}", + result.code + ); + assert!( + !result.code.contains("@Action"), + "Non-Angular method decorators should be lowered. Got:\n{}", + result.code + ); + + // Static method → __decorate([Selector()], TodoState, "todos", null) + assert!( + result.code.contains("__decorate([Selector()], TodoState, \"todos\", null)"), + "Static method decorator should use class directly (no .prototype). Got:\n{}", + result.code + ); + + // Instance method → __decorate([Action(AddTodo)], TodoState.prototype, "add", null) + assert!( + result.code.contains("__decorate([Action(AddTodo)], TodoState.prototype, \"add\", null)"), + "Instance method decorator should use .prototype. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_non_angular_method_decorators", result.code); +} + +#[test] +fn test_jit_full_ngxs_example() { + // Full example with NGXS-style decorators: @State, @Selector, @Action combined with @Injectable + let allocator = Allocator::default(); + let source = r#" +import { Injectable } from '@angular/core'; +import { State, Action, Selector, StateContext } from '@ngxs/store'; + +interface TodoStateModel { + items: TodoItem[]; + filter: string; +} + +interface TodoItem { + text: string; + done: boolean; +} + +class AddTodo { + static readonly type = '[Todo] Add'; + constructor(public text: string) {} +} + +class ToggleTodo { + static readonly type = '[Todo] Toggle'; + constructor(public index: number) {} +} + +@State({ name: 'todo', defaults: { items: [], filter: 'all' } }) +@Injectable() +export class TodoState { + @Selector() + static todos(state: TodoStateModel): TodoItem[] { return state.items; } + + @Selector() + static filter(state: TodoStateModel): string { return state.filter; } + + @Action(AddTodo) + add(ctx: StateContext, action: AddTodo) { /* ... */ } + + @Action(ToggleTodo) + toggle(ctx: StateContext, action: ToggleTodo) { /* ... */ } +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = transform_angular_file(&allocator, "todo.state.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // No raw decorators should remain anywhere + assert!( + !result.code.contains("@State") + && !result.code.contains("@Injectable") + && !result.code.contains("@Selector") + && !result.code.contains("@Action"), + "No raw decorator syntax should remain in output. Got:\n{}", + result.code + ); + + // Member __decorate calls should come before class __decorate + let selector_decorate = + result.code.find("__decorate([Selector()], TodoState, \"todos\"").unwrap(); + let class_decorate = result.code.find("TodoState = __decorate(").unwrap(); + assert!( + selector_decorate < class_decorate, + "Member decorators should be emitted before class decorator. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_full_ngxs_example", result.code); +} + +#[test] +fn test_jit_non_angular_property_decorator_uses_void_0() { + // TypeScript uses `void 0` (not `null`) as the 4th argument for property decorators + // because properties don't have an existing descriptor on the prototype. + // Methods use `null` which tells __decorate to call Object.getOwnPropertyDescriptor. + let allocator = Allocator::default(); + let source = r#" +import { Injectable } from '@angular/core'; + +function Validate() { return function(t: any, k: string) {}; } +function Log(target: any, key: string, desc: PropertyDescriptor) {} + +@Injectable() +export class MyService { + @Validate() + name: string = ''; + + @Log + greet() { return 'hello'; } +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = transform_angular_file(&allocator, "my.service.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // Property decorator should use `void 0` + assert!( + result.code.contains("__decorate([Validate()], MyService.prototype, \"name\", void 0)"), + "Property decorator should use `void 0` as 4th arg. Got:\n{}", + result.code + ); + + // Method decorator should use `null` + assert!( + result.code.contains("__decorate([Log], MyService.prototype, \"greet\", null)"), + "Method decorator should use `null` as 4th arg. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_property_decorator_void_0", result.code); +} + +#[test] +fn test_jit_mixed_angular_and_non_angular_decorators_on_same_member() { + // When a member has both Angular and non-Angular decorators, the Angular + // decorator goes into propDecorators while the non-Angular one is lowered + // to a __decorate() call. Both must be stripped from the class body. + let allocator = Allocator::default(); + let source = r#" +import { Directive, Input, Output, EventEmitter } from '@angular/core'; + +function Required() { return function(t: any, k: string) {}; } +function Throttle(ms: number) { return function(t: any, k: string, d: any) {}; } + +@Directive({ selector: '[appField]' }) +export class FieldDirective { + @Required() + @Input() + value: string = ''; + + @Throttle(300) + @Output() + valueChange = new EventEmitter(); + + @Throttle(100) + onChange() {} +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = + transform_angular_file(&allocator, "field.directive.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // No raw decorators should remain + assert!( + !result.code.contains("@Required") + && !result.code.contains("@Input") + && !result.code.contains("@Throttle") + && !result.code.contains("@Output"), + "No raw decorator syntax should remain. Got:\n{}", + result.code + ); + + // Angular decorators should appear in propDecorators + assert!( + result.code.contains("propDecorators"), + "Angular member decorators should be in propDecorators. Got:\n{}", + result.code + ); + assert!( + result.code.contains("type: Input"), + "propDecorators should contain Input. Got:\n{}", + result.code + ); + assert!( + result.code.contains("type: Output"), + "propDecorators should contain Output. Got:\n{}", + result.code + ); + + // Non-Angular decorators should be lowered via __decorate() + assert!( + result + .code + .contains("__decorate([Required()], FieldDirective.prototype, \"value\", void 0)"), + "Non-Angular property decorator should use __decorate with void 0. Got:\n{}", + result.code + ); + assert!( + result.code.contains( + "__decorate([Throttle(300)], FieldDirective.prototype, \"valueChange\", void 0)" + ), + "Non-Angular property decorator should use __decorate with void 0. Got:\n{}", + result.code + ); + assert!( + result + .code + .contains("__decorate([Throttle(100)], FieldDirective.prototype, \"onChange\", null)"), + "Non-Angular method decorator should use __decorate with null. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_mixed_angular_non_angular_same_member", result.code); +} + +#[test] +fn test_jit_multiple_non_angular_decorators_on_same_member() { + // Multiple non-Angular decorators on the same member should all appear + // in a single __decorate() call for that member. + let allocator = Allocator::default(); + let source = r#" +import { Injectable } from '@angular/core'; + +function Log() { return function(t: any, k: string, d: any) {}; } +function Memoize() { return function(t: any, k: string, d: any) {}; } +function Validate() { return function(t: any, k: string) {}; } + +@Injectable() +export class MyService { + @Log() + @Memoize() + compute() { return 42; } + + @Validate() + @Log() + name: string = ''; +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = transform_angular_file(&allocator, "my.service.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // Multiple decorators on method should be in single __decorate call, in source order + assert!( + result + .code + .contains("__decorate([Log(), Memoize()], MyService.prototype, \"compute\", null)"), + "Multiple method decorators should be in one __decorate call. Got:\n{}", + result.code + ); + + // Multiple decorators on property should also be in single __decorate call + assert!( + result + .code + .contains("__decorate([Validate(), Log()], MyService.prototype, \"name\", void 0)"), + "Multiple property decorators should be in one __decorate call. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_multiple_decorators_same_member", result.code); +} + +#[test] +fn test_jit_multiple_decorated_classes_in_same_file() { + // Multiple Angular-decorated classes in the same file should each get + // their own class expression conversion and __decorate calls. + let allocator = Allocator::default(); + let source = r#" +import { Component, Injectable } from '@angular/core'; + +function Logger() { return function(t: any) { return t; }; } + +@Component({ selector: 'app-foo', template: '

foo

' }) +export class FooComponent {} + +@Logger() +@Injectable() +export class FooService { + @Logger() + doWork() {} +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = transform_angular_file(&allocator, "foo.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // Both classes should be converted to class expressions + assert!( + result.code.contains("let FooComponent = class FooComponent"), + "FooComponent should be a class expression. Got:\n{}", + result.code + ); + assert!( + result.code.contains("let FooService = class FooService"), + "FooService should be a class expression. Got:\n{}", + result.code + ); + + // Both should have __decorate calls + assert!( + result.code.contains("FooComponent = __decorate("), + "FooComponent should have a __decorate call. Got:\n{}", + result.code + ); + assert!( + result.code.contains("FooService = __decorate("), + "FooService should have a __decorate call. Got:\n{}", + result.code + ); + + // No raw decorators + assert!( + !result.code.contains("@Component") + && !result.code.contains("@Injectable") + && !result.code.contains("@Logger"), + "No raw decorator syntax should remain. Got:\n{}", + result.code + ); + + // FooService should include Logger in its class __decorate + let service_decorate_pos = result.code.find("FooService = __decorate(").unwrap(); + let service_decorate_section = &result.code[service_decorate_pos..]; + assert!( + service_decorate_section.contains("Logger()"), + "FooService __decorate should include Logger. Got:\n{}", + result.code + ); + + // FooService member decorator should also be lowered + assert!( + result.code.contains("__decorate([Logger()], FooService.prototype, \"doWork\", null)"), + "FooService method decorator should be lowered. Got:\n{}", + result.code + ); + + // Both should be re-exported + assert!( + result.code.contains("export { FooComponent }"), + "FooComponent should be re-exported. Got:\n{}", + result.code + ); + assert!( + result.code.contains("export { FooService }"), + "FooService should be re-exported. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_multiple_classes_same_file", result.code); +} + +#[test] +fn test_jit_non_exported_class_with_decorators() { + // A non-exported Angular class with non-Angular decorators should still + // be lowered but without an export statement. + let allocator = Allocator::default(); + let source = r#" +import { Injectable } from '@angular/core'; + +function Singleton() { return function(t: any) { return t; }; } + +@Singleton() +@Injectable() +class InternalService { + @Singleton() + getInstance() {} +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = + transform_angular_file(&allocator, "internal.service.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // Should be converted to class expression + assert!( + result.code.contains("let InternalService = class InternalService"), + "Non-exported class should still be converted. Got:\n{}", + result.code + ); + + // No raw decorators + assert!( + !result.code.contains("@Singleton") && !result.code.contains("@Injectable"), + "No raw decorator syntax should remain. Got:\n{}", + result.code + ); + + // Should NOT have an export statement + assert!( + !result.code.contains("export {") && !result.code.contains("export default"), + "Non-exported class should not get an export statement. Got:\n{}", + result.code + ); + + // Both class decorators should be in __decorate + assert!( + result.code.contains("InternalService = __decorate("), + "Should have class __decorate. Got:\n{}", + result.code + ); + + // Member decorator should be lowered + assert!( + result.code.contains( + "__decorate([Singleton()], InternalService.prototype, \"getInstance\", null)" + ), + "Member decorator should be lowered. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_non_exported_class", result.code); +} + +#[test] +fn test_jit_default_exported_class_with_decorators() { + // A default-exported Angular class with non-Angular decorators should + // be lowered with `export default ClassName` at the end. + let allocator = Allocator::default(); + let source = r#" +import { Injectable } from '@angular/core'; + +function Logger() { return function(t: any) { return t; }; } + +@Logger() +@Injectable() +export default class AppService { + @Logger() + process() {} +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = transform_angular_file(&allocator, "app.service.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // Should be class expression + assert!( + result.code.contains("let AppService = class AppService"), + "Default-exported class should be converted. Got:\n{}", + result.code + ); + + // Should have `export default AppService` (not `export { AppService }`) + assert!( + result.code.contains("export default AppService"), + "Should use export default. Got:\n{}", + result.code + ); + assert!( + !result.code.contains("export { AppService }"), + "Should NOT use named export for default export. Got:\n{}", + result.code + ); + + // No raw decorators + assert!( + !result.code.contains("@Logger") && !result.code.contains("@Injectable"), + "No raw decorator syntax should remain. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_default_export_class", result.code); +} + +#[test] +fn test_jit_getter_setter_decorators() { + // Decorators on getter/setter methods should be lowered like regular methods + // (using null, not void 0, since they are accessor methods not property fields). + let allocator = Allocator::default(); + let source = r#" +import { Directive, Input } from '@angular/core'; + +function Validate() { return function(t: any, k: string, d: any) {}; } +function Transform() { return function(t: any, k: string, d: any) {}; } + +@Directive({ selector: '[appField]' }) +export class FieldDirective { + private _value = ''; + + @Validate() + @Input() + get value() { return this._value; } + set value(v: string) { this._value = v; } + + @Transform() + get computed() { return this._value.toUpperCase(); } +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = + transform_angular_file(&allocator, "field.directive.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // No raw decorators + assert!( + !result.code.contains("@Validate") + && !result.code.contains("@Input") + && !result.code.contains("@Transform"), + "No raw decorator syntax should remain. Got:\n{}", + result.code + ); + + // Getter decorator should use null (method/accessor, not property) + assert!( + result.code.contains("__decorate([Validate()], FieldDirective.prototype, \"value\", null)"), + "Getter decorator should use null (accessor). Got:\n{}", + result.code + ); + assert!( + result + .code + .contains("__decorate([Transform()], FieldDirective.prototype, \"computed\", null)"), + "Getter decorator should use null (accessor). Got:\n{}", + result.code + ); + + // Angular decorator should be in propDecorators + assert!( + result.code.contains("type: Input"), + "Angular getter decorator should be in propDecorators. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_getter_setter_decorators", result.code); +} + +#[test] +fn test_jit_decorator_with_complex_arguments() { + // Decorators with complex arguments (objects, arrays, arrow functions, + // template literals) should have their argument text preserved verbatim. + let allocator = Allocator::default(); + let source = r#" +import { Injectable } from '@angular/core'; + +function Config(opts: any) { return function(t: any) { return t; }; } +function Transform(fn: any) { return function(t: any, k: string, d: any) {}; } + +@Config({ + name: 'test', + deps: [ServiceA, ServiceB], + factory: () => new TestService(), +}) +@Injectable() +export class TestService { + @Transform((val: string) => val.trim()) + process() {} +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = + transform_angular_file(&allocator, "test.service.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // No raw decorators should remain + assert!( + !result.code.contains("@Config") + && !result.code.contains("@Injectable") + && !result.code.contains("@Transform"), + "No raw decorator syntax should remain. Got:\n{}", + result.code + ); + + // Complex arguments should be preserved in the __decorate call + assert!( + result.code.contains("Config("), + "Config decorator with complex args should be in __decorate. Got:\n{}", + result.code + ); + assert!( + result.code.contains("factory: () => new TestService()"), + "Arrow function argument should be preserved. Got:\n{}", + result.code + ); + assert!( + result.code.contains("deps: [ServiceA, ServiceB]"), + "Array argument should be preserved. Got:\n{}", + result.code + ); + + // Method decorator with arrow function arg + assert!( + result.code.contains("Transform((val) => val.trim())"), + "Arrow function in method decorator should be preserved. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("jit_complex_decorator_arguments", result.code); +} + +#[test] +fn test_jit_angular_param_decorators_not_in_member_decorate() { + // Angular parameter decorators (@Inject, @Optional, @Self, @SkipSelf, @Host, @Attribute) + // should NOT be emitted in __decorate() calls if they appear on a member. + // While these are designed for constructor params, if someone puts them on a member, + // they should be treated as Angular decorators (not lowered via __decorate). + let allocator = Allocator::default(); + let source = r#" +import { Injectable, Inject, Optional } from '@angular/core'; + +function Custom() { return function(t: any, k: string) {}; } + +@Injectable() +export class MyService { + @Inject('TOKEN') + token: any; + + @Optional() + optionalDep: any; + + @Custom() + customProp: string = ''; +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = transform_angular_file(&allocator, "my.service.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // @Custom should be lowered via __decorate (it's non-Angular) + assert!( + result.code.contains("__decorate([Custom()], MyService.prototype, \"customProp\", void 0)"), + "Non-Angular decorator should be in __decorate. Got:\n{}", + result.code + ); + + // @Inject and @Optional should NOT appear in __decorate calls for members + // They are Angular decorators and should not be treated as non-Angular + let member_decorate_calls: Vec<&str> = result + .code + .lines() + .filter(|l| l.contains("__decorate(") && l.contains(".prototype")) + .collect(); + for call in &member_decorate_calls { + assert!( + !call.contains("Inject(") && !call.contains("Optional()"), + "Angular param decorators should not appear in member __decorate calls. Got:\n{}", + call + ); + } + + insta::assert_snapshot!("jit_angular_param_decorators_on_members", result.code); +} + +// ========================================================================= +// Reference output comparison tests +// ========================================================================= +// These tests compare our output against the actual output from Angular's +// official JIT compiler (@angular/compiler-cli) + TypeScript emit pipeline. +// Reference outputs were generated by compiling TypeScript files with +// Angular's downlevel_decorators_transform followed by tsc emit. + +#[test] +fn test_jit_reference_ngxs_animals_state() { + // Reference: AnimalsState from Angular's actual JIT output + // Non-Angular @State class decorator + @Injectable, with @Selector (static) and @Action (instance) + let allocator = Allocator::default(); + let source = r#" +import { Injectable } from '@angular/core'; +import { State, Action, Selector } from '@ngxs/store'; + +@State({ + name: 'animals', + defaults: [] +}) +@Injectable() +class AnimalsState { + @Selector() + static getAnimals(state: string[]): string[] { + return state; + } + + @Action({ type: 'AddAnimal' }) + addAnimal(ctx: any, action: any): void {} +} +"#; + + let options = ComponentTransformOptions { jit: true, ..Default::default() }; + let result = + transform_angular_file(&allocator, "animals.state.ts", source, Some(&options), None); + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + // Angular reference output (from full-compiled-output.js): + // __decorate([Action({type:'AddAnimal'})], AnimalsState.prototype, "addAnimal", null); + // __decorate([Selector()], AnimalsState, "getAnimals", null); + // AnimalsState = __decorate([State({...}), Injectable()], AnimalsState); + + // Instance method → prototype, null + assert!( + result.code.contains("__decorate([Action({ type: \"AddAnimal\" })], AnimalsState.prototype, \"addAnimal\", null)"), + "Instance method should match Angular reference output. Got:\n{}", + result.code + ); + + // Static method → class directly, null + assert!( + result.code.contains("__decorate([Selector()], AnimalsState, \"getAnimals\", null)"), + "Static method should match Angular reference output. Got:\n{}", + result.code + ); + + // Instance __decorate calls should come before static ones (TypeScript ordering) + let instance_pos = result.code.find("AnimalsState.prototype").unwrap(); + let static_pos = result.code.find("AnimalsState, \"getAnimals\"").unwrap(); + assert!( + instance_pos < static_pos, + "Instance member __decorate should come before static. Got:\n{}", + result.code + ); + + // Class __decorate should include both State and Injectable in source order + let class_decorate = result.code.find("AnimalsState = __decorate(").unwrap(); + let class_section = &result.code[class_decorate..]; + assert!( + class_section.contains("State(") && class_section.contains("Injectable()"), + "Class __decorate should include both decorators. Got:\n{}", + result.code + ); + + // No raw decorators assert!( - !result.code.contains("ɵdir") && !result.code.contains("ɵfac"), - "JIT directive output should NOT contain AOT definitions. Got:\n{}", + !result.code.contains("@State") + && !result.code.contains("@Injectable") + && !result.code.contains("@Selector") + && !result.code.contains("@Action"), + "No raw decorator syntax should remain. Got:\n{}", result.code ); - insta::assert_snapshot!("jit_directive", result.code); + insta::assert_snapshot!("jit_reference_animals_state", result.code); } #[test] -fn test_jit_full_component_example() { - // Full example matching the issue #97 scenario +fn test_jit_reference_ordering() { + // Reference: OrderTestState from Angular's actual JIT output + // Tests that instance members are emitted before static members, + // each group in source order. This matches TypeScript's emit behavior. let allocator = Allocator::default(); let source = r#" -import { Component, signal } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; -import { Lib1 } from 'lib1'; -import { TitleService } from './title.service'; +import { Injectable } from '@angular/core'; +import { State, Action, Selector } from '@ngxs/store'; -@Component({ - selector: 'app-root', - imports: [RouterOutlet, Lib1], - templateUrl: './app.html', - styleUrl: './app.css', -}) -export class App { - titleService; - title = signal('app'); - constructor(titleService: TitleService) { - this.titleService = titleService; - this.title.set(this.titleService.getTitle()); - } +@State({ name: 'order', defaults: {} }) +@Injectable() +class OrderTestState { + @Action({ type: 'First' }) + instanceFirst(ctx: any): void {} + + @Selector() + static staticSecond(state: any): any { return state; } + + @Action({ type: 'Third' }) + instanceThird(ctx: any): void {} + + @Selector() + static staticFourth(state: any): any { return state; } } "#; let options = ComponentTransformOptions { jit: true, ..Default::default() }; - let result = transform_angular_file(&allocator, "app.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "order.state.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); - // Should have all JIT characteristics - assert!( - result.code.contains("import { __decorate } from \"tslib\""), - "Missing __decorate import" - ); - assert!( - result.code.contains("angular:jit:template:file;./app.html"), - "Missing template resource import" - ); - assert!( - result.code.contains("angular:jit:style:file;./app.css"), - "Missing style resource import" - ); - assert!(result.code.contains("let App = class App"), "Missing class restructuring"); - assert!(result.code.contains("ctorParameters"), "Missing ctorParameters"); - assert!(result.code.contains("__decorate("), "Missing __decorate call"); - assert!(result.code.contains("export { App }"), "Missing named export"); + // Angular reference output ordering (from decorate-patterns-output.js): + // __decorate([Action({type:'First'})], OrderTestState.prototype, "instanceFirst", null); + // __decorate([Action({type:'Third'})], OrderTestState.prototype, "instanceThird", null); + // __decorate([Selector()], OrderTestState, "staticSecond", null); + // __decorate([Selector()], OrderTestState, "staticFourth", null); + // OrderTestState = __decorate([State({...}), Injectable()], OrderTestState); - // Should NOT have AOT output - assert!(!result.code.contains("ɵcmp"), "Should not contain ɵcmp"); - assert!(!result.code.contains("ɵfac"), "Should not contain ɵfac"); - assert!(!result.code.contains("defineComponent"), "Should not contain defineComponent"); + let first_pos = result.code.find("\"instanceFirst\"").unwrap(); + let third_pos = result.code.find("\"instanceThird\"").unwrap(); + let second_pos = result.code.find("\"staticSecond\"").unwrap(); + let fourth_pos = result.code.find("\"staticFourth\"").unwrap(); + let class_pos = result.code.find("OrderTestState = __decorate(").unwrap(); - insta::assert_snapshot!("jit_full_component", result.code); + // Instance members first (in source order) + assert!(first_pos < third_pos, "instanceFirst before instanceThird"); + // Then static members (in source order) + assert!(third_pos < second_pos, "instance group before static group"); + assert!(second_pos < fourth_pos, "staticSecond before staticFourth"); + // Class decorator last + assert!(fourth_pos < class_pos, "member decorators before class decorator"); + + insta::assert_snapshot!("jit_reference_ordering", result.code); } #[test] -fn test_jit_prop_decorators_emitted() { - // Bug fix: member decorators (@Input, @Output, etc.) must be downleveled - // to static propDecorators so Angular's JIT runtime can discover inputs/outputs. - // Without this, @Input/@Output decorators are silently lost, breaking data binding. +fn test_jit_reference_decorate_patterns() { + // Reference: TestDecoratePatternsService from Angular's actual JIT output + // Tests property/method/static/getter/setter decorator patterns let allocator = Allocator::default(); let source = r#" -import { Directive, Input, Output, HostBinding, EventEmitter } from '@angular/core'; +import { Injectable } from '@angular/core'; -@Directive({ - selector: '[appHighlight]', -}) -export class HighlightDirective { - @Input() color: string = 'yellow'; - @Input('aliasName') title: string = ''; - @Output() colorChange = new EventEmitter(); - @HostBinding('class.active') isActive = false; +function CustomPropDecorator(): any { return () => {}; } +function CustomMethodDecorator(): any { return () => {}; } + +@Injectable() +class TestDecoratePatternsService { + @CustomPropDecorator() + myProp: string = 'hello'; + + @CustomMethodDecorator() + myMethod(): void {} + + @CustomMethodDecorator() + static myStaticMethod(): void {} + + @CustomPropDecorator() + get myGetter(): string { return ''; } + + @CustomPropDecorator() + set mySetter(val: string) {} } "#; let options = ComponentTransformOptions { jit: true, ..Default::default() }; let result = - transform_angular_file(&allocator, "highlight.directive.ts", source, &options, None); + transform_angular_file(&allocator, "patterns.service.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); - // propDecorators must be present — Angular's JIT runtime reads this + // Angular reference output (from decorate-patterns-output.js): + // __decorate([CustomPropDecorator()], X.prototype, "myProp", void 0); + // __decorate([CustomMethodDecorator()], X.prototype, "myMethod", null); + // __decorate([CustomPropDecorator()], X.prototype, "myGetter", null); + // __decorate([CustomPropDecorator()], X.prototype, "mySetter", null); + // __decorate([CustomMethodDecorator()], X, "myStaticMethod", null); + + // Property → void 0 assert!( - result.code.contains("propDecorators"), - "JIT output must emit static propDecorators. Got:\n{}", + result.code.contains("__decorate([CustomPropDecorator()], TestDecoratePatternsService.prototype, \"myProp\", void 0)"), + "Property decorator should use void 0 (Angular reference). Got:\n{}", result.code ); - // Each decorated member should appear in propDecorators - assert!(result.code.contains("color:"), "propDecorators should list 'color'"); - assert!(result.code.contains("title:"), "propDecorators should list 'title'"); - assert!(result.code.contains("colorChange:"), "propDecorators should list 'colorChange'"); - assert!(result.code.contains("isActive:"), "propDecorators should list 'isActive'"); + // Method → null + assert!( + result.code.contains("__decorate([CustomMethodDecorator()], TestDecoratePatternsService.prototype, \"myMethod\", null)"), + "Method decorator should use null (Angular reference). Got:\n{}", + result.code + ); - // The decorator type references should be present - assert!(result.code.contains("type: Input"), "propDecorators should reference Input"); - assert!(result.code.contains("type: Output"), "propDecorators should reference Output"); + // Static method → class, null assert!( - result.code.contains("type: HostBinding"), - "propDecorators should reference HostBinding" + result.code.contains("__decorate([CustomMethodDecorator()], TestDecoratePatternsService, \"myStaticMethod\", null)"), + "Static method should use class directly (Angular reference). Got:\n{}", + result.code ); - // The original decorators must be removed from the class body - assert!(!result.code.contains("@Input()"), "@Input decorator must be removed from class body"); + // Getter → null (accessor, not property) assert!( - !result.code.contains("@Output()"), - "@Output decorator must be removed from class body" + result.code.contains("__decorate([CustomPropDecorator()], TestDecoratePatternsService.prototype, \"myGetter\", null)"), + "Getter should use null (Angular reference). Got:\n{}", + result.code ); - insta::assert_snapshot!("jit_prop_decorators", result.code); + // Setter → null (accessor, not property) + assert!( + result.code.contains("__decorate([CustomPropDecorator()], TestDecoratePatternsService.prototype, \"mySetter\", null)"), + "Setter should use null (Angular reference). Got:\n{}", + result.code + ); + + // Ordering: instance members first (myProp, myMethod, myGetter, mySetter), then static + let prop_pos = result.code.find("\"myProp\"").unwrap(); + let method_pos = result.code.find("\"myMethod\"").unwrap(); + let getter_pos = result.code.find("\"myGetter\"").unwrap(); + let setter_pos = result.code.find("\"mySetter\"").unwrap(); + let static_pos = result.code.find("\"myStaticMethod\"").unwrap(); + + assert!(prop_pos < static_pos, "instance before static"); + assert!(method_pos < static_pos, "instance before static"); + assert!(getter_pos < static_pos, "instance before static"); + assert!(setter_pos < static_pos, "instance before static"); + + insta::assert_snapshot!("jit_reference_decorate_patterns", result.code); } #[test] -fn test_jit_union_type_ctor_params() { - // Angular-aligned union type behavior for ctorParameters. - // Angular's typeReferenceToExpression filters ONLY `null` literal types. - // If exactly one non-null type remains, it resolves; otherwise unresolvable. - // - // `T | null` → resolves to T (1 non-null type) - // `undefined | T` → unresolvable (2 non-null types: undefined + T) - // `null | undefined | T` → unresolvable (2 non-null types: undefined + T) - // - // See: angular/packages/compiler-cli/src/ngtsc/transform/jit/src/downlevel_decorators_transform.ts +fn test_jit_reference_angular_member_decorators() { + // Reference: MyService from Angular's actual JIT output + // Angular member decorators go into propDecorators, constructor params into ctorParameters let allocator = Allocator::default(); let source = r#" -import { Component } from '@angular/core'; -import { ServiceA } from './a.service'; -import { ServiceB } from './b.service'; -import { ServiceC } from './c.service'; +import { Injectable, Inject, Optional, Input, Output, ViewChild, HostListener, HostBinding, ContentChild } from '@angular/core'; + +@Injectable() +class MyService { + @Input() + myInput: string = ''; + + @Output() + myOutput: any; + + @ViewChild('ref') + myViewChild: any; + + @HostBinding('class.active') + isActive: boolean = false; + + @HostListener('click', ['$event']) + onClick(event: Event): void {} + + @ContentChild('content') + myContent: any; -@Component({ selector: 'test', template: '' }) -export class TestComponent { constructor( - svcA: undefined | ServiceA, - svcB: null | undefined | ServiceB, - svcC: ServiceC | null, + @Inject('TOKEN') private token: string, + @Optional() private optService: any, ) {} + + normalMethod(): void {} } "#; let options = ComponentTransformOptions { jit: true, ..Default::default() }; - let result = transform_angular_file(&allocator, "test.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "my.service.ts", source, Some(&options), None); assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); - // `ServiceC | null` resolves correctly (1 non-null type) + // Angular reference: propDecorators should contain all Angular member decorators + // From full-compiled-output.js: + // static propDecorators = { + // myInput: [{ type: Input }], + // myOutput: [{ type: Output }], + // myViewChild: [{ type: ViewChild, args: ['ref',] }], + // isActive: [{ type: HostBinding, args: ['class.active',] }], + // onClick: [{ type: HostListener, args: ['click', ['$event'],] }], + // myContent: [{ type: ContentChild, args: ['content',] }] + // }; + assert!( - result.code.contains("type: ServiceC"), - "ctorParameters should resolve 'ServiceC | null' to ServiceC. Got:\n{}", + result.code.contains("propDecorators"), + "Should have propDecorators. Got:\n{}", + result.code + ); + assert!(result.code.contains("type: Input"), "propDecorators: Input. Got:\n{}", result.code); + assert!(result.code.contains("type: Output"), "propDecorators: Output. Got:\n{}", result.code); + assert!( + result.code.contains("type: ViewChild"), + "propDecorators: ViewChild. Got:\n{}", + result.code + ); + assert!( + result.code.contains("type: HostBinding"), + "propDecorators: HostBinding. Got:\n{}", + result.code + ); + assert!( + result.code.contains("type: HostListener"), + "propDecorators: HostListener. Got:\n{}", + result.code + ); + assert!( + result.code.contains("type: ContentChild"), + "propDecorators: ContentChild. Got:\n{}", result.code ); - // `undefined | ServiceA` and `null | undefined | ServiceB` are unresolvable per Angular spec - // (2 non-null types remain after filtering null) + // Angular reference: ctorParameters should contain constructor param types and decorators + // From full-compiled-output.js: + // static ctorParameters = () => [ + // { type: String, decorators: [{ type: Inject, args: ['TOKEN',] }] }, + // { type: undefined, decorators: [{ type: Optional }] } + // ]; assert!( - !result.code.contains("type: ServiceA"), - "ctorParameters must not resolve 'undefined | ServiceA' (2 non-null types). Got:\n{}", + result.code.contains("ctorParameters"), + "Should have ctorParameters. Got:\n{}", result.code ); + assert!(result.code.contains("type: Inject"), "ctorParameters: Inject. Got:\n{}", result.code); assert!( - !result.code.contains("type: ServiceB"), - "ctorParameters must not resolve 'null | undefined | ServiceB' (2 non-null types). Got:\n{}", + result.code.contains("type: Optional"), + "ctorParameters: Optional. Got:\n{}", result.code ); - insta::assert_snapshot!("jit_union_type_ctor_params", result.code); + // No raw Angular decorators should remain + assert!( + !result.code.contains("@Input") + && !result.code.contains("@Output") + && !result.code.contains("@ViewChild") + && !result.code.contains("@HostBinding") + && !result.code.contains("@HostListener") + && !result.code.contains("@ContentChild") + && !result.code.contains("@Inject") + && !result.code.contains("@Optional"), + "No raw Angular decorator syntax should remain. Got:\n{}", + result.code + ); + + // No __decorate calls for Angular member decorators (they go in propDecorators instead) + // Only the class __decorate([Injectable()], ...) should exist + let decorate_count = result.code.matches("__decorate(").count(); + assert!( + decorate_count == 1, + "Should have exactly 1 __decorate call (class only, not members). Got {} calls:\n{}", + decorate_count, + result.code + ); + + insta::assert_snapshot!("jit_reference_angular_member_decorators", result.code); } // ========================================================================= @@ -6399,7 +7454,8 @@ export class TestComponent { let options = ComponentTransformOptions { sourcemap: true, ..Default::default() }; - let result = transform_angular_file(&allocator, "app.component.ts", source, &options, None); + let result = + transform_angular_file(&allocator, "app.component.ts", source, Some(&options), None); assert!( result.map.is_some(), @@ -6436,7 +7492,8 @@ export class TestComponent { let options = ComponentTransformOptions { sourcemap: true, jit: true, ..Default::default() }; - let result = transform_angular_file(&allocator, "app.component.ts", source, &options, None); + let result = + transform_angular_file(&allocator, "app.component.ts", source, Some(&options), None); assert!( result.map.is_some(), @@ -6463,13 +7520,7 @@ export class TestComponent { } "#; - let result = transform_angular_file( - &allocator, - "app.component.ts", - source, - &ComponentTransformOptions::default(), - None, - ); + let result = transform_angular_file(&allocator, "app.component.ts", source, None, None); assert!(result.map.is_none(), "Source map should be None when sourcemap option is false"); } @@ -6495,8 +7546,13 @@ export class TestComponent { let options = ComponentTransformOptions { sourcemap: true, ..Default::default() }; - let result = - transform_angular_file(&allocator, "app.component.ts", source, &options, Some(&resolved)); + let result = transform_angular_file( + &allocator, + "app.component.ts", + source, + Some(&options), + Some(&resolved), + ); assert!( result.map.is_some(), @@ -6515,7 +7571,7 @@ fn test_sourcemap_no_angular_classes() { let options = ComponentTransformOptions { sourcemap: true, ..Default::default() }; - let result = transform_angular_file(&allocator, "plain.ts", source, &options, None); + let result = transform_angular_file(&allocator, "plain.ts", source, Some(&options), None); // Even for files with no Angular components, if sourcemap is requested, // a trivial identity source map should be returned @@ -6543,8 +7599,7 @@ import { Component } from '@angular/core'; export class HelloComponent {} "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "hello.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "hello.component.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); // Should have exactly one dts declaration @@ -6594,8 +7649,7 @@ export class UserComponent { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "user.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "user.component.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); let decl = &result.dts_declarations[0]; @@ -6635,8 +7689,7 @@ import { Component } from '@angular/core'; export class LegacyComponent {} "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "legacy.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "legacy.component.ts", source, None, None); assert!(!result.has_errors()); let decl = &result.dts_declarations[0]; @@ -6663,8 +7716,7 @@ import { Component } from '@angular/core'; export class TooltipComponent {} "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "tooltip.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "tooltip.component.ts", source, None, None); assert!(!result.has_errors()); let decl = &result.dts_declarations[0]; @@ -6692,9 +7744,7 @@ export class HighlightDirective { } "#; - let options = ComponentTransformOptions::default(); - let result = - transform_angular_file(&allocator, "highlight.directive.ts", source, &options, None); + let result = transform_angular_file(&allocator, "highlight.directive.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); assert_eq!(result.dts_declarations.len(), 1); @@ -6749,8 +7799,7 @@ export class CapitalizePipe implements PipeTransform { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "capitalize.pipe.ts", source, &options, None); + let result = transform_angular_file(&allocator, "capitalize.pipe.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); assert_eq!(result.dts_declarations.len(), 1); @@ -6789,8 +7838,7 @@ export class MyPipe implements PipeTransform { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "my.pipe.ts", source, &options, None); + let result = transform_angular_file(&allocator, "my.pipe.ts", source, None, None); assert_eq!(result.dts_declarations.len(), 1); let decl = &result.dts_declarations[0]; @@ -6819,8 +7867,7 @@ import { CommonModule } from '@angular/common'; export class MyModule {} "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "my.module.ts", source, &options, None); + let result = transform_angular_file(&allocator, "my.module.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); assert_eq!(result.dts_declarations.len(), 1); @@ -6873,8 +7920,7 @@ export class DataService { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "data.service.ts", source, &options, None); + let result = transform_angular_file(&allocator, "data.service.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); assert_eq!(result.dts_declarations.len(), 1); @@ -6910,8 +7956,7 @@ export class GenericService { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "generic.service.ts", source, &options, None); + let result = transform_angular_file(&allocator, "generic.service.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); assert_eq!(result.dts_declarations.len(), 1); @@ -6947,8 +7992,7 @@ export class GenericPipe implements PipeTransform { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "generic.pipe.ts", source, &options, None); + let result = transform_angular_file(&allocator, "generic.pipe.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); assert_eq!(result.dts_declarations.len(), 1); @@ -6986,8 +8030,7 @@ export class GenericDirective { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "generic.directive.ts", source, &options, None); + let result = transform_angular_file(&allocator, "generic.directive.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); assert_eq!(result.dts_declarations.len(), 1); @@ -7020,8 +8063,7 @@ import { NgModule } from '@angular/core'; export class GenericModule {} "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "generic.module.ts", source, &options, None); + let result = transform_angular_file(&allocator, "generic.module.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); assert_eq!(result.dts_declarations.len(), 1); @@ -7072,8 +8114,7 @@ export class MyPipe implements PipeTransform { export class MultiComponent {} "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "multi.ts", source, &options, None); + let result = transform_angular_file(&allocator, "multi.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); // Should have declarations for all 3 classes @@ -7100,8 +8141,7 @@ export class PlainClass { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "plain.ts", source, &options, None); + let result = transform_angular_file(&allocator, "plain.ts", source, None, None); // Should have no dts declarations for plain classes assert!( @@ -7127,8 +8167,7 @@ export class SignalComponent { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "signal.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "signal.component.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); assert_eq!(result.dts_declarations.len(), 1); @@ -7162,8 +8201,7 @@ export class TestComponent { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); let decl = &result.dts_declarations[0]; @@ -7195,8 +8233,7 @@ export class TestComponent { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); let decl = &result.dts_declarations[0]; @@ -7226,8 +8263,7 @@ export class TestComponent { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); let decl = &result.dts_declarations[0]; @@ -7259,8 +8295,7 @@ export class TestDirective { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.directive.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.directive.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); let decl = &result.dts_declarations[0]; @@ -7290,8 +8325,7 @@ import { Component } from '@angular/core'; export class LayoutComponent {} "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "layout.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "layout.component.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); let decl = &result.dts_declarations[0]; @@ -7329,8 +8363,7 @@ export class TestComponent { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); let decl = &result.dts_declarations[0]; @@ -7367,8 +8400,7 @@ export class TestDirective { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.directive.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.directive.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); let decl = &result.dts_declarations[0]; @@ -7405,8 +8437,7 @@ export class TestComponent { } "#; - let options = ComponentTransformOptions::default(); - let result = transform_angular_file(&allocator, "test.component.ts", source, &options, None); + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); assert!(!result.has_errors(), "Should compile without errors: {:?}", result.diagnostics); let decl = &result.dts_declarations[0]; @@ -7833,3 +8864,119 @@ fn test_property_singleton_interpolation_with_sanitizer_angular_v19() { assert!(js.contains("ɵɵsanitizeUrl"), "Should include ɵɵsanitizeUrl sanitizer. Got:\n{js}"); insta::assert_snapshot!("property_singleton_interpolation_with_sanitizer_v19", js); } + +// ============================================================================ +// Host Directive Alias Tests +// ============================================================================ + +/// Test host directives with simple aliased inputs/outputs. +/// +/// Mirrors the compliance test `host_directives_with_inputs_outputs.ts`. +/// The mapping array must use `[internalName, publicName]` ordering. +#[test] +fn test_host_directives_with_inputs_outputs() { + let allocator = Allocator::default(); + let source = r#" +import { Component, Directive, EventEmitter, Input, Output } from '@angular/core'; + +@Directive({}) +export class HostDir { + @Input() value = 0; + @Input() color = ''; + @Output() opened = new EventEmitter(); + @Output() closed = new EventEmitter(); +} + +@Component({ + selector: 'my-component', + template: '', + hostDirectives: [{ + directive: HostDir, + inputs: ['value', 'color: colorAlias'], + outputs: ['opened', 'closed: closedAlias'], + }], + standalone: false, +}) +export class MyComponent { +} +"#; + + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); + + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + let normalized = result.code.replace([' ', '\n', '\t'], ""); + + // Input mappings: 'value' (no alias) → ["value", "value"], 'color: colorAlias' → ["color", "colorAlias"] + // The array must be [internalName, publicName, ...] i.e. ["value", "value", "color", "colorAlias"] + assert!( + normalized.contains(r#"inputs:["value","value","color","colorAlias"]"#), + "Input mappings should be [internalName, publicName]. Got:\n{}", + result.code + ); + + // Output mappings: 'opened' → ["opened", "opened"], 'closed: closedAlias' → ["closed", "closedAlias"] + assert!( + normalized.contains(r#"outputs:["opened","opened","closed","closedAlias"]"#), + "Output mappings should be [internalName, publicName]. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("host_directives_with_inputs_outputs", result.code); +} + +/// Test host directives where the directive has `@Input('alias')` and the host re-aliases. +/// +/// Mirrors the compliance test `host_directives_with_host_aliases.ts`. +#[test] +fn test_host_directives_with_host_aliases() { + let allocator = Allocator::default(); + let source = r#" +import { Component, Directive, EventEmitter, Input, Output } from '@angular/core'; + +@Directive({}) +export class HostDir { + @Input('valueAlias') value = 1; + @Input('colorAlias') color = ''; + @Output('openedAlias') opened = new EventEmitter(); + @Output('closedAlias') closed = new EventEmitter(); +} + +@Component({ + selector: 'my-component', + template: '', + hostDirectives: [{ + directive: HostDir, + inputs: ['valueAlias', 'colorAlias: customColorAlias'], + outputs: ['openedAlias', 'closedAlias: customClosedAlias'], + }], + standalone: false, +}) +export class MyComponent { +} +"#; + + let result = transform_angular_file(&allocator, "test.component.ts", source, None, None); + + assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics); + + let normalized = result.code.replace([' ', '\n', '\t'], ""); + + // Input mappings: 'valueAlias' → ["valueAlias", "valueAlias"], 'colorAlias: customColorAlias' → ["colorAlias", "customColorAlias"] + assert!( + normalized + .contains(r#"inputs:["valueAlias","valueAlias","colorAlias","customColorAlias"]"#), + "Input mappings should be [internalName, publicName]. Got:\n{}", + result.code + ); + + // Output mappings: 'openedAlias' → ["openedAlias", "openedAlias"], 'closedAlias: customClosedAlias' → ["closedAlias", "customClosedAlias"] + assert!( + normalized + .contains(r#"outputs:["openedAlias","openedAlias","closedAlias","customClosedAlias"]"#), + "Output mappings should be [internalName, publicName]. Got:\n{}", + result.code + ); + + insta::assert_snapshot!("host_directives_with_host_aliases", result.code); +} diff --git a/crates/oxc_angular_compiler/tests/linker_test.rs b/crates/oxc_angular_compiler/tests/linker_test.rs new file mode 100644 index 000000000..4a6639ecf --- /dev/null +++ b/crates/oxc_angular_compiler/tests/linker_test.rs @@ -0,0 +1,97 @@ +//! Tests for Angular linker input/output key quoting. + +use oxc_allocator::Allocator; +use oxc_angular_compiler::linker::link; + +/// Helper to build a ɵɵngDeclareDirective source with a given inputs block. +fn make_directive_source(inputs_block: &str) -> String { + format!( + r#"import * as i0 from "@angular/core"; +export class MyDir {{}} +MyDir.ɵdir = i0.ɵɵngDeclareDirective({{ minVersion: "14.0.0", version: "17.0.0", type: MyDir, selector: "[myDir]", inputs: {{ {inputs_block} }} }});"# + ) +} + +/// Helper to build a ɵɵngDeclareDirective source with a given outputs block. +fn make_directive_source_with_outputs(outputs_block: &str) -> String { + format!( + r#"import * as i0 from "@angular/core"; +export class MyDir {{}} +MyDir.ɵdir = i0.ɵɵngDeclareDirective({{ minVersion: "14.0.0", version: "17.0.0", type: MyDir, selector: "[myDir]", outputs: {{ {outputs_block} }} }});"# + ) +} + +#[test] +fn test_link_inputs_dotted_key() { + let allocator = Allocator::default(); + let code = make_directive_source(r#""fxFlexAlign.xs": "fxFlexAlignXs""#); + let result = link(&allocator, &code, "test.mjs"); + insta::assert_snapshot!(result.code); +} + +#[test] +fn test_link_inputs_hyphenated_key() { + let allocator = Allocator::default(); + let code = make_directive_source(r#""fxFlexAlign.lt-sm": "fxFlexAlignLtSm""#); + let result = link(&allocator, &code, "test.mjs"); + insta::assert_snapshot!(result.code); +} + +#[test] +fn test_link_inputs_simple_identifier() { + let allocator = Allocator::default(); + let code = make_directive_source(r#"fxFlexAlign: "fxFlexAlign""#); + let result = link(&allocator, &code, "test.mjs"); + insta::assert_snapshot!(result.code); +} + +#[test] +fn test_link_inputs_object_format_dotted_key() { + let allocator = Allocator::default(); + let code = make_directive_source( + r#""fxFlexAlign.xs": { classPropertyName: "fxFlexAlignXs", publicName: "fxFlexAlign.xs", isRequired: false, isSignal: false }"#, + ); + let result = link(&allocator, &code, "test.mjs"); + insta::assert_snapshot!(result.code); +} + +#[test] +fn test_link_inputs_array_format_dotted_key() { + let allocator = Allocator::default(); + let code = make_directive_source(r#""fxFlexAlign.xs": ["fxFlexAlign.xs", "fxFlexAlignXs"]"#); + let result = link(&allocator, &code, "test.mjs"); + insta::assert_snapshot!(result.code); +} + +#[test] +fn test_link_outputs_dotted_key() { + let allocator = Allocator::default(); + let code = make_directive_source_with_outputs(r#""activate.xs": "activateXs""#); + let result = link(&allocator, &code, "test.mjs"); + insta::assert_snapshot!(result.code); +} + +#[test] +fn test_link_outputs_hyphenated_key() { + let allocator = Allocator::default(); + let code = make_directive_source_with_outputs(r#""activate.lt-sm": "activateLtSm""#); + let result = link(&allocator, &code, "test.mjs"); + insta::assert_snapshot!(result.code); +} + +#[test] +fn test_link_outputs_simple_identifier() { + let allocator = Allocator::default(); + let code = make_directive_source_with_outputs(r#"activate: "activate""#); + let result = link(&allocator, &code, "test.mjs"); + insta::assert_snapshot!(result.code); +} + +#[test] +fn test_link_inputs_array_format_with_transform_function() { + let allocator = Allocator::default(); + let code = + make_directive_source(r#"push: ["cdkConnectedOverlayPush", "push", i0.booleanAttribute]"#); + let result = link(&allocator, &code, "test.mjs"); + insta::assert_snapshot!(result.code); +} diff --git a/crates/oxc_angular_compiler/tests/nextcontext_listener.rs b/crates/oxc_angular_compiler/tests/nextcontext_listener.rs index ae9eaf182..65aad4d76 100644 --- a/crates/oxc_angular_compiler/tests/nextcontext_listener.rs +++ b/crates/oxc_angular_compiler/tests/nextcontext_listener.rs @@ -7,7 +7,7 @@ use oxc_angular_compiler::{ pipeline::{emit::compile_template, ingest::ingest_component}, transform::html_to_r3::{HtmlToR3Transform, TransformOptions}, }; -use oxc_span::Atom; +use oxc_span::Ident; fn compile_template_to_js(template: &str, component_name: &str) -> String { let allocator = Allocator::default(); @@ -18,7 +18,7 @@ fn compile_template_to_js(template: &str, component_name: &str) -> String { let transformer = HtmlToR3Transform::new(&allocator, template, TransformOptions::default()); let r3_result = transformer.transform(&nodes); assert!(r3_result.errors.is_empty(), "Transform errors: {:?}", r3_result.errors); - let mut job = ingest_component(&allocator, Atom::from(component_name), r3_result.nodes); + let mut job = ingest_component(&allocator, Ident::from(component_name), r3_result.nodes); let result = compile_template(&mut job); let emitter = JsEmitter::new(); let mut output = String::new(); diff --git a/crates/oxc_angular_compiler/tests/nextcontext_repro.rs b/crates/oxc_angular_compiler/tests/nextcontext_repro.rs index d4bec1b33..d12e226f8 100644 --- a/crates/oxc_angular_compiler/tests/nextcontext_repro.rs +++ b/crates/oxc_angular_compiler/tests/nextcontext_repro.rs @@ -9,7 +9,7 @@ use oxc_angular_compiler::{ }, transform::html_to_r3::{HtmlToR3Transform, TransformOptions}, }; -use oxc_span::Atom; +use oxc_span::Ident; fn compile_template_to_js(template: &str, component_name: &str) -> String { let allocator = Allocator::default(); @@ -20,7 +20,7 @@ fn compile_template_to_js(template: &str, component_name: &str) -> String { let transformer = HtmlToR3Transform::new(&allocator, template, TransformOptions::default()); let r3_result = transformer.transform(&nodes); assert!(r3_result.errors.is_empty(), "Transform errors: {:?}", r3_result.errors); - let mut job = ingest_component(&allocator, Atom::from(component_name), r3_result.nodes); + let mut job = ingest_component(&allocator, Ident::from(component_name), r3_result.nodes); let result = compile_template(&mut job); let emitter = JsEmitter::new(); let mut output = String::new(); @@ -341,7 +341,7 @@ fn test_for_loop_alias_ingest() { let transformer = HtmlToR3Transform::new(&allocator, template, TransformOptions::default()); let r3_result = transformer.transform(&nodes); - let job = ingest_component(&allocator, Atom::from("TestComponent"), r3_result.nodes); + let job = ingest_component(&allocator, Ident::from("TestComponent"), r3_result.nodes); // Check that the body view has the expected context variables and aliases assert!(!job.views.is_empty(), "Should have at least one embedded view"); diff --git a/crates/oxc_angular_compiler/tests/playground_repro.rs b/crates/oxc_angular_compiler/tests/playground_repro.rs index 86b4473d3..6a4533cd0 100644 --- a/crates/oxc_angular_compiler/tests/playground_repro.rs +++ b/crates/oxc_angular_compiler/tests/playground_repro.rs @@ -7,7 +7,7 @@ use oxc_angular_compiler::{ pipeline::{emit::compile_template, ingest::ingest_component}, transform::html_to_r3::{HtmlToR3Transform, TransformOptions}, }; -use oxc_span::Atom; +use oxc_span::Ident; fn compile_template_to_js(template: &str, component_name: &str) -> String { let allocator = Allocator::default(); @@ -19,7 +19,7 @@ fn compile_template_to_js(template: &str, component_name: &str) -> String { let transformer = HtmlToR3Transform::new(&allocator, template, TransformOptions::default()); let r3_result = transformer.transform(&nodes); assert!(r3_result.errors.is_empty(), "Transform errors: {:?}", r3_result.errors); - let mut job = ingest_component(&allocator, Atom::from(component_name), r3_result.nodes); + let mut job = ingest_component(&allocator, Ident::from(component_name), r3_result.nodes); let result = compile_template(&mut job); let emitter = JsEmitter::new(); let mut output = String::new(); diff --git a/crates/oxc_angular_compiler/tests/shadow_css_test.rs b/crates/oxc_angular_compiler/tests/shadow_css_test.rs index 23ee7df72..73a0af5a8 100644 --- a/crates/oxc_angular_compiler/tests/shadow_css_test.rs +++ b/crates/oxc_angular_compiler/tests/shadow_css_test.rs @@ -290,6 +290,56 @@ fn test_handle_curly_braces_in_quoted_content() { assert_css_eq!(shim(css, "contenta"), expected); } +#[test] +fn test_unicode_in_content_property() { + // Issue #191: unicode characters in :after CSS content property + // Raw CSS escape sequence (as from inline styles) + let css = r".test-div:after { content: '\2022'; }"; + assert_css_eq!(shim(css, "contenta"), r".test-div[contenta]:after { content: '\2022'; }"); + + // After Sass compilation - Sass converts \2022 to actual bullet char + let css = ".test-div:after { content: \"\u{2022}\"; }"; + assert_css_eq!(shim(css, "contenta"), ".test-div[contenta]:after { content:\"\u{2022}\"; }"); +} + +#[test] +fn test_multibyte_utf8_preserved_in_css_values() { + // Various multi-byte UTF-8 characters in CSS content property + // 2-byte: ¢ (U+00A2), © (U+00A9) + let css = ".a:after { content: \"\u{00A2}\u{00A9}\"; }"; + let result = shim(css, "contenta"); + assert!(result.contains("\u{00A2}\u{00A9}"), "2-byte UTF-8 chars corrupted: {result}"); + + // 3-byte: • (U+2022), — (U+2014), → (U+2192) + let css = ".a:after { content: \"\u{2022}\u{2014}\u{2192}\"; }"; + let result = shim(css, "contenta"); + assert!(result.contains("\u{2022}\u{2014}\u{2192}"), "3-byte UTF-8 chars corrupted: {result}"); + + // 4-byte: 😀 (U+1F600) + let css = ".a:after { content: \"\u{1F600}\"; }"; + let result = shim(css, "contenta"); + assert!(result.contains('\u{1F600}'), "4-byte UTF-8 char corrupted: {result}"); + + // Non-ASCII in selector (e.g. class name with accented chars) + let css = ".caf\u{00E9} { color: red; }"; + let result = shim(css, "contenta"); + assert!(result.contains("caf\u{00E9}"), "Non-ASCII in selector corrupted: {result}"); +} + +#[test] +fn test_finalize_preserves_unicode() { + use oxc_angular_compiler::styles::finalize_component_style; + // Full pipeline with Sass-compiled CSS containing actual bullet character + let css = ".test:after { content: \"\u{2022}\"; }"; + let result = finalize_component_style(css, true, "_ngcontent-%COMP%", "_nghost-%COMP%", true); + assert!(result.contains('\u{2022}'), "Bullet lost in full pipeline: {result}"); + + // With @charset prefix from Sass + let css = "@charset \"UTF-8\";\n.test:after { content: \"\u{2022}\"; }"; + let result = finalize_component_style(css, true, "_ngcontent-%COMP%", "_nghost-%COMP%", true); + assert!(result.contains('\u{2022}'), "Bullet lost with @charset: {result}"); +} + // ============================================================================ // Playground CSS Test (real-world case) // ============================================================================ @@ -911,3 +961,56 @@ fn test_sidebar_row_layout_full_css_regression() { println!("Result length: {}", result.len()); assert!(!result.is_empty()); } + +// ============================================================================ +// Regression: CSS comments before first selector must not break scoping +// ============================================================================ + +#[test] +fn test_scope_first_selector_after_comment_with_space() { + // Comment followed by space then selector + let css = "/* comment */ .foo { color: red; }"; + let expected = ".foo[contenta] { color: red; }"; + assert_css_eq!(shim(css, "contenta"), expected); +} + +#[test] +fn test_scope_first_selector_after_comment_with_newline() { + // Comment followed by newline then selector (the SCSS @import case) + let css = "/* comment */\n.container { border-radius: 2px; }\n.container .tabs-group { width: 100%; }"; + let expected = ".container[contenta] { border-radius: 2px; }\n.container[contenta] .tabs-group[contenta] { width: 100%; }"; + assert_css_eq!(shim(css, "contenta"), expected); +} + +#[test] +fn test_scope_first_selector_after_multiline_comment() { + // Multi-line comment followed by selector + let css = "/* multi\nline\ncomment */\n.root { padding: 16px; }\n.root .child { color: red; }"; + let expected = + ".root[contenta] { padding: 16px; }\n.root[contenta] .child[contenta] { color: red; }"; + assert_css_eq!(shim(css, "contenta"), expected); +} + +#[test] +fn test_scope_first_selector_after_multiple_comments() { + // Multiple comments before first selector + let css = "/* comment 1 */ /* comment 2 */ .foo { color: red; }"; + let expected = ".foo[contenta] { color: red; }"; + assert_css_eq!(shim(css, "contenta"), expected); +} + +#[test] +fn test_newline_as_descendant_combinator() { + // Newline between selectors is a valid CSS descendant combinator + let css = ".foo\n.bar { color: red; }"; + let expected = ".foo[contenta] .bar[contenta] { color: red; }"; + assert_css_eq!(shim(css, "contenta"), expected); +} + +#[test] +fn test_host_pseudo_with_newline_combinator() { + // :host with pseudo-selector followed by newline combinator to child + let css = ":host(:hover)\n.child { color: red; }"; + let expected = "[hosta]:hover .child[contenta] { color: red; }"; + assert_css_eq!(shim_with_host(css, "contenta", "hosta"), expected); +} diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__component_with_inline_styles.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__component_with_inline_styles.snap index 167d0e128..77c8d5f37 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__component_with_inline_styles.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__component_with_inline_styles.snap @@ -2,6 +2,7 @@ source: crates/oxc_angular_compiler/tests/integration_test.rs expression: result.code --- + import { Component } from '@angular/core'; import * as i0 from '@angular/core'; @@ -21,5 +22,5 @@ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({type:StyledComponent,select } (() =>{ (((typeof ngDevMode === "undefined") || ngDevMode) && i0.ɵsetClassDebugInfo(StyledComponent, - {className:"StyledComponent",filePath:"styled.component.ts",lineNumber:1})); + {className:"StyledComponent",filePath:"styled.component.ts",lineNumber:9})); })(); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__component_with_multiple_styles.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__component_with_multiple_styles.snap index e2307d0c3..5a5de2145 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__component_with_multiple_styles.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__component_with_multiple_styles.snap @@ -2,6 +2,7 @@ source: crates/oxc_angular_compiler/tests/integration_test.rs expression: result.code --- + import { Component } from '@angular/core'; import * as i0 from '@angular/core'; @@ -20,5 +21,5 @@ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({type:MultiStyledComponent,s } (() =>{ (((typeof ngDevMode === "undefined") || ngDevMode) && i0.ɵsetClassDebugInfo(MultiStyledComponent, - {className:"MultiStyledComponent",filePath:"multi-styled.component.ts",lineNumber:1})); + {className:"MultiStyledComponent",filePath:"multi-styled.component.ts",lineNumber:12})); })(); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__component_without_styles.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__component_without_styles.snap index fca9533ef..b946c7ec6 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__component_without_styles.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__component_without_styles.snap @@ -2,6 +2,7 @@ source: crates/oxc_angular_compiler/tests/integration_test.rs expression: result.code --- + import { Component } from '@angular/core'; import * as i0 from '@angular/core'; @@ -20,5 +21,5 @@ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({type:NoStylesComponent,sele } (() =>{ (((typeof ngDevMode === "undefined") || ngDevMode) && i0.ɵsetClassDebugInfo(NoStylesComponent, - {className:"NoStylesComponent",filePath:"no-styles.component.ts",lineNumber:1})); + {className:"NoStylesComponent",filePath:"no-styles.component.ts",lineNumber:8})); })(); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__event_before_property_in_bindings.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__event_before_property_in_bindings.snap index 9228d9f0a..adab899a8 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__event_before_property_in_bindings.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__event_before_property_in_bindings.snap @@ -2,6 +2,7 @@ source: crates/oxc_angular_compiler/tests/integration_test.rs expression: result.code --- + import { Component } from '@angular/core'; import * as i0 from '@angular/core'; @@ -28,5 +29,5 @@ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({type:TestComponent,selector } (() =>{ (((typeof ngDevMode === "undefined") || ngDevMode) && i0.ɵsetClassDebugInfo(TestComponent, - {className:"TestComponent",filePath:"test.component.ts",lineNumber:1})); + {className:"TestComponent",filePath:"test.component.ts",lineNumber:9})); })(); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_binary_with_component_method.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_binary_with_component_method.snap new file mode 100644 index 000000000..69e623c19 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_binary_with_component_method.snap @@ -0,0 +1,26 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: js +--- +function _forTrack0($index,$item) { + return (this.prefix() + $item.id); +} +function TestComponent_For_1_Template(rf,ctx) { + if ((rf & 1)) { + i0.ɵɵtext(0," "); + i0.ɵɵelementStart(1,"div"); + i0.ɵɵtext(2); + i0.ɵɵelementEnd(); + i0.ɵɵtext(3," "); + } + if ((rf & 2)) { + const item_r1 = ctx.$implicit; + i0.ɵɵadvance(2); + i0.ɵɵtextInterpolate(item_r1.name); + } +} +function TestComponent_Template(rf,ctx) { + if ((rf & 1)) { i0.ɵɵrepeaterCreate(0,TestComponent_For_1_Template,4,1,null,null,_forTrack0, + true); } + if ((rf & 2)) { i0.ɵɵrepeater(ctx.items); } +} diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_binary_without_component_context.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_binary_without_component_context.snap new file mode 100644 index 000000000..d8039f349 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_binary_without_component_context.snap @@ -0,0 +1,23 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: js +--- +const _forTrack0 = ($index,$item) =>(($item.type + ":") + $item.id); +function TestComponent_For_1_Template(rf,ctx) { + if ((rf & 1)) { + i0.ɵɵtext(0," "); + i0.ɵɵelementStart(1,"div"); + i0.ɵɵtext(2); + i0.ɵɵelementEnd(); + i0.ɵɵtext(3," "); + } + if ((rf & 2)) { + const item_r1 = ctx.$implicit; + i0.ɵɵadvance(2); + i0.ɵɵtextInterpolate(item_r1.name); + } +} +function TestComponent_Template(rf,ctx) { + if ((rf & 1)) { i0.ɵɵrepeaterCreate(0,TestComponent_For_1_Template,4,1,null,null,_forTrack0); } + if ((rf & 2)) { i0.ɵɵrepeater(ctx.items); } +} diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_complex_binary_with_nullish_coalescing.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_complex_binary_with_nullish_coalescing.snap new file mode 100644 index 000000000..56818eae3 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_complex_binary_with_nullish_coalescing.snap @@ -0,0 +1,26 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: js +--- +function _forTrack0($index,$item) { + return ((($item.queryPrefix ?? this.queryPrefix()) + ".") + $item.key); +} +function TestComponent_For_1_Template(rf,ctx) { + if ((rf & 1)) { + i0.ɵɵtext(0," "); + i0.ɵɵelementStart(1,"span"); + i0.ɵɵtext(2); + i0.ɵɵelementEnd(); + i0.ɵɵtext(3," "); + } + if ((rf & 2)) { + const tag_r1 = ctx.$implicit; + i0.ɵɵadvance(2); + i0.ɵɵtextInterpolate(tag_r1.key); + } +} +function TestComponent_Template(rf,ctx) { + if ((rf & 1)) { i0.ɵɵrepeaterCreate(0,TestComponent_For_1_Template,4,1,null,null,_forTrack0, + true); } + if ((rf & 2)) { i0.ɵɵrepeater(ctx.visibleTags()); } +} diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_not_with_component_method.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_not_with_component_method.snap new file mode 100644 index 000000000..afced8690 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_not_with_component_method.snap @@ -0,0 +1,26 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: js +--- +function _forTrack0($index,$item) { + return !this.isDisabled(); +} +function TestComponent_For_1_Template(rf,ctx) { + if ((rf & 1)) { + i0.ɵɵtext(0," "); + i0.ɵɵelementStart(1,"div"); + i0.ɵɵtext(2); + i0.ɵɵelementEnd(); + i0.ɵɵtext(3," "); + } + if ((rf & 2)) { + const item_r1 = ctx.$implicit; + i0.ɵɵadvance(2); + i0.ɵɵtextInterpolate(item_r1.name); + } +} +function TestComponent_Template(rf,ctx) { + if ((rf & 1)) { i0.ɵɵrepeaterCreate(0,TestComponent_For_1_Template,4,1,null,null,_forTrack0, + true); } + if ((rf & 2)) { i0.ɵɵrepeater(ctx.items); } +} diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_nullish_coalescing_with_component_method.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_nullish_coalescing_with_component_method.snap new file mode 100644 index 000000000..a182134fe --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_nullish_coalescing_with_component_method.snap @@ -0,0 +1,26 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: js +--- +function _forTrack0($index,$item) { + return ($item.prefix ?? this.defaultPrefix()); +} +function TestComponent_For_1_Template(rf,ctx) { + if ((rf & 1)) { + i0.ɵɵtext(0," "); + i0.ɵɵelementStart(1,"div"); + i0.ɵɵtext(2); + i0.ɵɵelementEnd(); + i0.ɵɵtext(3," "); + } + if ((rf & 2)) { + const item_r1 = ctx.$implicit; + i0.ɵɵadvance(2); + i0.ɵɵtextInterpolate(item_r1.name); + } +} +function TestComponent_Template(rf,ctx) { + if ((rf & 1)) { i0.ɵɵrepeaterCreate(0,TestComponent_For_1_Template,4,1,null,null,_forTrack0, + true); } + if ((rf & 2)) { i0.ɵɵrepeater(ctx.items); } +} diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_ternary_with_component_method.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_ternary_with_component_method.snap new file mode 100644 index 000000000..5bc8a2caf --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__for_track_ternary_with_component_method.snap @@ -0,0 +1,26 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: js +--- +function _forTrack0($index,$item) { + return (this.useId()? $item.id: $item.name); +} +function TestComponent_For_1_Template(rf,ctx) { + if ((rf & 1)) { + i0.ɵɵtext(0," "); + i0.ɵɵelementStart(1,"div"); + i0.ɵɵtext(2); + i0.ɵɵelementEnd(); + i0.ɵɵtext(3," "); + } + if ((rf & 2)) { + const item_r1 = ctx.$implicit; + i0.ɵɵadvance(2); + i0.ɵɵtextInterpolate(item_r1.name); + } +} +function TestComponent_Template(rf,ctx) { + if ((rf & 1)) { i0.ɵɵrepeaterCreate(0,TestComponent_For_1_Template,4,1,null,null,_forTrack0, + true); } + if ((rf & 2)) { i0.ɵɵrepeater(ctx.items); } +} diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__host_directives_with_host_aliases.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__host_directives_with_host_aliases.snap new file mode 100644 index 000000000..eaaae3f6f --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__host_directives_with_host_aliases.snap @@ -0,0 +1,37 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- + +import { Component, Directive, EventEmitter, Input, Output } from '@angular/core'; +import * as i0 from '@angular/core'; + +export class HostDir { + value = 1; + color = ''; + opened = new EventEmitter(); + closed = new EventEmitter(); + +static ɵfac = function HostDir_Factory(__ngFactoryType__) { + return new (__ngFactoryType__ || HostDir)(); +}; +static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({type:HostDir,inputs:{value:[0,"valueAlias","value"], + color:[0,"colorAlias","color"]},outputs:{opened:"openedAlias",closed:"closedAlias"}}); +} + +export class MyComponent { + +static ɵfac = function MyComponent_Factory(__ngFactoryType__) { + return new (__ngFactoryType__ || MyComponent)(); +}; +static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({type:MyComponent,selectors:[["my-component"]], + standalone:false,features:[i0.ɵɵHostDirectivesFeature([{directive:HostDir,inputs:["valueAlias", + "valueAlias","colorAlias","customColorAlias"],outputs:["openedAlias","openedAlias", + "closedAlias","customClosedAlias"]}])],decls:0,vars:0,template:function MyComponent_Template(rf, + ctx) { + },dependencies:i0.ɵɵgetComponentDepsFactory(MyComponent),encapsulation:2}); +} +(() =>{ + (((typeof ngDevMode === "undefined") || ngDevMode) && i0.ɵsetClassDebugInfo(MyComponent, + {className:"MyComponent",filePath:"test.component.ts",lineNumber:22})); +})(); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__host_directives_with_inputs_outputs.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__host_directives_with_inputs_outputs.snap new file mode 100644 index 000000000..605d28956 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__host_directives_with_inputs_outputs.snap @@ -0,0 +1,36 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- + +import { Component, Directive, EventEmitter, Input, Output } from '@angular/core'; +import * as i0 from '@angular/core'; + +export class HostDir { + value = 0; + color = ''; + opened = new EventEmitter(); + closed = new EventEmitter(); + +static ɵfac = function HostDir_Factory(__ngFactoryType__) { + return new (__ngFactoryType__ || HostDir)(); +}; +static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({type:HostDir,inputs:{value:"value",color:"color"}, + outputs:{opened:"opened",closed:"closed"}}); +} + +export class MyComponent { + +static ɵfac = function MyComponent_Factory(__ngFactoryType__) { + return new (__ngFactoryType__ || MyComponent)(); +}; +static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({type:MyComponent,selectors:[["my-component"]], + standalone:false,features:[i0.ɵɵHostDirectivesFeature([{directive:HostDir,inputs:["value", + "value","color","colorAlias"],outputs:["opened","opened","closed","closedAlias"]}])], + decls:0,vars:0,template:function MyComponent_Template(rf,ctx) { + },dependencies:i0.ɵɵgetComponentDepsFactory(MyComponent),encapsulation:2}); +} +(() =>{ + (((typeof ngDevMode === "undefined") || ngDevMode) && i0.ɵsetClassDebugInfo(MyComponent, + {className:"MyComponent",filePath:"test.component.ts",lineNumber:22})); +})(); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_abstract_class.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_abstract_class.snap new file mode 100644 index 000000000..ee2c69e80 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_abstract_class.snap @@ -0,0 +1,14 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +assertion_line: 6452 +expression: result.code +--- +import { Injectable } from "@angular/core"; +import { __decorate } from "tslib"; +let BaseProvider = class BaseProvider { + greet() { + return `Hello from ${this.name}`; + } +}; +BaseProvider = __decorate([Injectable()], BaseProvider); +export { BaseProvider }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_angular_param_decorators_on_members.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_angular_param_decorators_on_members.snap new file mode 100644 index 000000000..f4b64ba62 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_angular_param_decorators_on_members.snap @@ -0,0 +1,17 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable, Inject, Optional } from "@angular/core"; +import { __decorate } from "tslib"; +function Custom() { + return function(t, k) {}; +} +let MyService = class MyService { + token; + optionalDep; + customProp = ""; +}; +__decorate([Custom()], MyService.prototype, "customProp", void 0); +MyService = __decorate([Injectable()], MyService); +export { MyService }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_class_restructuring.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_class_restructuring.snap index 3f5f2f3e7..8cad539a8 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_class_restructuring.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_class_restructuring.snap @@ -1,17 +1,15 @@ --- source: crates/oxc_angular_compiler/tests/integration_test.rs +assertion_line: 6201 expression: result.code --- -import { Component } from '@angular/core'; +import { Component } from "@angular/core"; import { __decorate } from "tslib"; - let AppComponent = class AppComponent { - title = 'app'; + title = "app"; }; -AppComponent = __decorate([ - Component({ - selector: 'app-root', - template: '

Hello

', -}) -], AppComponent); +AppComponent = __decorate([Component({ + selector: "app-root", + template: "

Hello

" +})], AppComponent); export { AppComponent }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_complex_decorator_arguments.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_complex_decorator_arguments.snap new file mode 100644 index 000000000..30be04418 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_complex_decorator_arguments.snap @@ -0,0 +1,24 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable } from "@angular/core"; +import { __decorate } from "tslib"; +function Config(opts) { + return function(t) { + return t; + }; +} +function Transform(fn) { + return function(t, k, d) {}; +} +let TestService = class TestService { + process() {} +}; +__decorate([Transform((val) => val.trim())], TestService.prototype, "process", null); +TestService = __decorate([Config({ + name: "test", + deps: [ServiceA, ServiceB], + factory: () => new TestService() +}), Injectable()], TestService); +export { TestService }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_constructor_deps.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_constructor_deps.snap index 418dddae1..aa55f61f4 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_constructor_deps.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_constructor_deps.snap @@ -1,22 +1,19 @@ --- source: crates/oxc_angular_compiler/tests/integration_test.rs +assertion_line: 6164 expression: result.code --- -import { Component } from '@angular/core'; -import { TitleService } from './title.service'; +import { Component } from "@angular/core"; +import { TitleService } from "./title.service"; import { __decorate } from "tslib"; - let AppComponent = class AppComponent { - constructor(private titleService: TitleService) {} - -static ctorParameters = () => [ - { type: TitleService } -]; + constructor(titleService) { + this.titleService = titleService; + } + static ctorParameters = () => [{ type: TitleService }]; }; -AppComponent = __decorate([ - Component({ - selector: 'app-root', - template: '

Hello

', -}) -], AppComponent); +AppComponent = __decorate([Component({ + selector: "app-root", + template: "

Hello

" +})], AppComponent); export { AppComponent }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_default_export_class.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_default_export_class.snap new file mode 100644 index 000000000..4c10df838 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_default_export_class.snap @@ -0,0 +1,17 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable } from "@angular/core"; +import { __decorate } from "tslib"; +function Logger() { + return function(t) { + return t; + }; +} +let AppService = class AppService { + process() {} +}; +__decorate([Logger()], AppService.prototype, "process", null); +AppService = __decorate([Logger(), Injectable()], AppService); +export default AppService; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_directive.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_directive.snap index 7d7147027..19fcd82d8 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_directive.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_directive.snap @@ -1,21 +1,16 @@ --- source: crates/oxc_angular_compiler/tests/integration_test.rs +assertion_line: 6239 expression: result.code --- -import { Directive, Input } from '@angular/core'; +import { Directive, Input } from "@angular/core"; import { __decorate } from "tslib"; - let HighlightDirective = class HighlightDirective { - color: string = 'yellow'; - -static propDecorators = { - color: [{ type: Input }] + color = "yellow"; + static propDecorators = { color: [{ type: Input }] }; }; -}; -HighlightDirective = __decorate([ - Directive({ - selector: '[appHighlight]', - standalone: true, -}) -], HighlightDirective); +HighlightDirective = __decorate([Directive({ + selector: "[appHighlight]", + standalone: true +})], HighlightDirective); export { HighlightDirective }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_full_component.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_full_component.snap index 43522bbed..fc1b97be4 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_full_component.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_full_component.snap @@ -1,33 +1,28 @@ --- source: crates/oxc_angular_compiler/tests/integration_test.rs +assertion_line: 6295 expression: result.code --- -import { Component, signal } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; -import { Lib1 } from 'lib1'; -import { TitleService } from './title.service'; +import { Component, signal } from "@angular/core"; +import { RouterOutlet } from "@angular/router"; +import { Lib1 } from "lib1"; +import { TitleService } from "./title.service"; import { __decorate } from "tslib"; import __NG_CLI_RESOURCE__0 from "angular:jit:template:file;./app.html"; import __NG_CLI_RESOURCE__1 from "angular:jit:style:file;./app.css"; - let App = class App { - titleService; - title = signal('app'); - constructor(titleService: TitleService) { - this.titleService = titleService; - this.title.set(this.titleService.getTitle()); - } - -static ctorParameters = () => [ - { type: TitleService } -]; + titleService; + title = signal("app"); + constructor(titleService) { + this.titleService = titleService; + this.title.set(this.titleService.getTitle()); + } + static ctorParameters = () => [{ type: TitleService }]; }; -App = __decorate([ - Component({ - selector: 'app-root', - imports: [RouterOutlet, Lib1], - template: __NG_CLI_RESOURCE__0, - styles: [__NG_CLI_RESOURCE__1], -}) -], App); +App = __decorate([Component({ + selector: "app-root", + imports: [RouterOutlet, Lib1], + template: __NG_CLI_RESOURCE__0, + styles: [__NG_CLI_RESOURCE__1] +})], App); export { App }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_full_ngxs_example.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_full_ngxs_example.snap new file mode 100644 index 000000000..358c501b3 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_full_ngxs_example.snap @@ -0,0 +1,41 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable } from "@angular/core"; +import { State, Action, Selector, StateContext } from "@ngxs/store"; +import { __decorate } from "tslib"; +class AddTodo { + static type = "[Todo] Add"; + constructor(text) { + this.text = text; + } +} +class ToggleTodo { + static type = "[Todo] Toggle"; + constructor(index) { + this.index = index; + } +} +let TodoState = class TodoState { + static todos(state) { + return state.items; + } + static filter(state) { + return state.filter; + } + add(ctx, action) { /* ... */} + toggle(ctx, action) { /* ... */} +}; +__decorate([Action(AddTodo)], TodoState.prototype, "add", null); +__decorate([Action(ToggleTodo)], TodoState.prototype, "toggle", null); +__decorate([Selector()], TodoState, "todos", null); +__decorate([Selector()], TodoState, "filter", null); +TodoState = __decorate([State({ + name: "todo", + defaults: { + items: [], + filter: "all" + } +}), Injectable()], TodoState); +export { TodoState }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_getter_setter_decorators.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_getter_setter_decorators.snap new file mode 100644 index 000000000..54a4fa197 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_getter_setter_decorators.snap @@ -0,0 +1,29 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Directive, Input } from "@angular/core"; +import { __decorate } from "tslib"; +function Validate() { + return function(t, k, d) {}; +} +function Transform() { + return function(t, k, d) {}; +} +let FieldDirective = class FieldDirective { + _value = ""; + get value() { + return this._value; + } + set value(v) { + this._value = v; + } + get computed() { + return this._value.toUpperCase(); + } + static propDecorators = { value: [{ type: Input }] }; +}; +__decorate([Validate()], FieldDirective.prototype, "value", null); +__decorate([Transform()], FieldDirective.prototype, "computed", null); +FieldDirective = __decorate([Directive({ selector: "[appField]" })], FieldDirective); +export { FieldDirective }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_inline_template.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_inline_template.snap index 0589aaf1c..6516bad0d 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_inline_template.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_inline_template.snap @@ -1,16 +1,14 @@ --- source: crates/oxc_angular_compiler/tests/integration_test.rs +assertion_line: 6059 expression: result.code --- -import { Component } from '@angular/core'; +import { Component } from "@angular/core"; import { __decorate } from "tslib"; - let AppComponent = class AppComponent {}; -AppComponent = __decorate([ - Component({ - selector: 'app-root', - template: '

Hello

', - standalone: true, -}) -], AppComponent); +AppComponent = __decorate([Component({ + selector: "app-root", + template: "

Hello

", + standalone: true +})], AppComponent); export { AppComponent }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_mixed_angular_non_angular_same_member.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_mixed_angular_non_angular_same_member.snap new file mode 100644 index 000000000..2316e1a00 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_mixed_angular_non_angular_same_member.snap @@ -0,0 +1,26 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Directive, Input, Output, EventEmitter } from "@angular/core"; +import { __decorate } from "tslib"; +function Required() { + return function(t, k) {}; +} +function Throttle(ms) { + return function(t, k, d) {}; +} +let FieldDirective = class FieldDirective { + value = ""; + valueChange = new EventEmitter(); + onChange() {} + static propDecorators = { + value: [{ type: Input }], + valueChange: [{ type: Output }] + }; +}; +__decorate([Required()], FieldDirective.prototype, "value", void 0); +__decorate([Throttle(300)], FieldDirective.prototype, "valueChange", void 0); +__decorate([Throttle(100)], FieldDirective.prototype, "onChange", null); +FieldDirective = __decorate([Directive({ selector: "[appField]" })], FieldDirective); +export { FieldDirective }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_multiple_classes_same_file.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_multiple_classes_same_file.snap new file mode 100644 index 000000000..b434c82b7 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_multiple_classes_same_file.snap @@ -0,0 +1,23 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Component, Injectable } from "@angular/core"; +import { __decorate } from "tslib"; +function Logger() { + return function(t) { + return t; + }; +} +let FooComponent = class FooComponent {}; +FooComponent = __decorate([Component({ + selector: "app-foo", + template: "

foo

" +})], FooComponent); +export { FooComponent }; +let FooService = class FooService { + doWork() {} +}; +__decorate([Logger()], FooService.prototype, "doWork", null); +FooService = __decorate([Logger(), Injectable()], FooService); +export { FooService }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_multiple_decorators_same_member.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_multiple_decorators_same_member.snap new file mode 100644 index 000000000..5c1c68d95 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_multiple_decorators_same_member.snap @@ -0,0 +1,25 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable } from "@angular/core"; +import { __decorate } from "tslib"; +function Log() { + return function(t, k, d) {}; +} +function Memoize() { + return function(t, k, d) {}; +} +function Validate() { + return function(t, k) {}; +} +let MyService = class MyService { + compute() { + return 42; + } + name = ""; +}; +__decorate([Log(), Memoize()], MyService.prototype, "compute", null); +__decorate([Validate(), Log()], MyService.prototype, "name", void 0); +MyService = __decorate([Injectable()], MyService); +export { MyService }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_non_angular_class_decorators.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_non_angular_class_decorators.snap new file mode 100644 index 000000000..1f22f25bf --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_non_angular_class_decorators.snap @@ -0,0 +1,13 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable } from "@angular/core"; +import { State } from "@ngxs/store"; +import { __decorate } from "tslib"; +let TodoState = class TodoState {}; +TodoState = __decorate([State({ + name: "todo", + defaults: { items: [] } +}), Injectable()], TodoState); +export { TodoState }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_non_angular_method_decorators.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_non_angular_method_decorators.snap new file mode 100644 index 000000000..e5595054a --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_non_angular_method_decorators.snap @@ -0,0 +1,19 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable } from "@angular/core"; +import { State, Action, Selector } from "@ngxs/store"; +import { __decorate } from "tslib"; +let TodoState = class TodoState { + static todos(state) { + return state.items; + } + add(ctx, action) { + ctx.setState(action); + } +}; +__decorate([Action(AddTodo)], TodoState.prototype, "add", null); +__decorate([Selector()], TodoState, "todos", null); +TodoState = __decorate([State({ name: "todo" }), Injectable()], TodoState); +export { TodoState }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_non_exported_class.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_non_exported_class.snap new file mode 100644 index 000000000..5db9b1716 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_non_exported_class.snap @@ -0,0 +1,16 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable } from "@angular/core"; +import { __decorate } from "tslib"; +function Singleton() { + return function(t) { + return t; + }; +} +let InternalService = class InternalService { + getInstance() {} +}; +__decorate([Singleton()], InternalService.prototype, "getInstance", null); +InternalService = __decorate([Singleton(), Injectable()], InternalService); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_prop_decorators.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_prop_decorators.snap index 3b25eeaff..eeba7c537 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_prop_decorators.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_prop_decorators.snap @@ -1,26 +1,27 @@ --- source: crates/oxc_angular_compiler/tests/integration_test.rs +assertion_line: 6351 expression: result.code --- -import { Directive, Input, Output, HostBinding, EventEmitter } from '@angular/core'; +import { Directive, Input, Output, HostBinding, EventEmitter } from "@angular/core"; import { __decorate } from "tslib"; - let HighlightDirective = class HighlightDirective { - color: string = 'yellow'; - title: string = ''; - colorChange = new EventEmitter(); - isActive = false; - -static propDecorators = { - color: [{ type: Input }], - title: [{ type: Input, args: ['aliasName'] }], - colorChange: [{ type: Output }], - isActive: [{ type: HostBinding, args: ['class.active'] }] + color = "yellow"; + title = ""; + colorChange = new EventEmitter(); + isActive = false; + static propDecorators = { + color: [{ type: Input }], + title: [{ + type: Input, + args: ["aliasName"] + }], + colorChange: [{ type: Output }], + isActive: [{ + type: HostBinding, + args: ["class.active"] + }] + }; }; -}; -HighlightDirective = __decorate([ - Directive({ - selector: '[appHighlight]', -}) -], HighlightDirective); +HighlightDirective = __decorate([Directive({ selector: "[appHighlight]" })], HighlightDirective); export { HighlightDirective }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_property_decorator_void_0.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_property_decorator_void_0.snap new file mode 100644 index 000000000..c04096c9e --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_property_decorator_void_0.snap @@ -0,0 +1,20 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable } from "@angular/core"; +import { __decorate } from "tslib"; +function Validate() { + return function(t, k) {}; +} +function Log(target, key, desc) {} +let MyService = class MyService { + name = ""; + greet() { + return "hello"; + } +}; +__decorate([Validate()], MyService.prototype, "name", void 0); +__decorate([Log], MyService.prototype, "greet", null); +MyService = __decorate([Injectable()], MyService); +export { MyService }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_reference_angular_member_decorators.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_reference_angular_member_decorators.snap new file mode 100644 index 000000000..2f5c3fe7b --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_reference_angular_member_decorators.snap @@ -0,0 +1,50 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable, Inject, Optional, Input, Output, ViewChild, HostListener, HostBinding, ContentChild } from "@angular/core"; +import { __decorate } from "tslib"; +let MyService = class MyService { + myInput = ""; + myOutput; + myViewChild; + isActive = false; + onClick(event) {} + myContent; + constructor(token, optService) { + this.token = token; + this.optService = optService; + } + normalMethod() {} + static ctorParameters = () => [{ + type: undefined, + decorators: [{ + type: Inject, + args: ["TOKEN"] + }] + }, { + type: undefined, + decorators: [{ type: Optional }] + }]; + static propDecorators = { + myInput: [{ type: Input }], + myOutput: [{ type: Output }], + myViewChild: [{ + type: ViewChild, + args: ["ref"] + }], + isActive: [{ + type: HostBinding, + args: ["class.active"] + }], + onClick: [{ + type: HostListener, + args: ["click", ["$event"]] + }], + myContent: [{ + type: ContentChild, + args: ["content"] + }] + }; +}; +MyService = __decorate([Injectable()], MyService); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_reference_animals_state.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_reference_animals_state.snap new file mode 100644 index 000000000..f82045c1d --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_reference_animals_state.snap @@ -0,0 +1,19 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable } from "@angular/core"; +import { State, Action, Selector } from "@ngxs/store"; +import { __decorate } from "tslib"; +let AnimalsState = class AnimalsState { + static getAnimals(state) { + return state; + } + addAnimal(ctx, action) {} +}; +__decorate([Action({ type: "AddAnimal" })], AnimalsState.prototype, "addAnimal", null); +__decorate([Selector()], AnimalsState, "getAnimals", null); +AnimalsState = __decorate([State({ + name: "animals", + defaults: [] +}), Injectable()], AnimalsState); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_reference_decorate_patterns.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_reference_decorate_patterns.snap new file mode 100644 index 000000000..4d81d18eb --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_reference_decorate_patterns.snap @@ -0,0 +1,27 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable } from "@angular/core"; +import { __decorate } from "tslib"; +function CustomPropDecorator() { + return () => {}; +} +function CustomMethodDecorator() { + return () => {}; +} +let TestDecoratePatternsService = class TestDecoratePatternsService { + myProp = "hello"; + myMethod() {} + static myStaticMethod() {} + get myGetter() { + return ""; + } + set mySetter(val) {} +}; +__decorate([CustomPropDecorator()], TestDecoratePatternsService.prototype, "myProp", void 0); +__decorate([CustomMethodDecorator()], TestDecoratePatternsService.prototype, "myMethod", null); +__decorate([CustomPropDecorator()], TestDecoratePatternsService.prototype, "myGetter", null); +__decorate([CustomPropDecorator()], TestDecoratePatternsService.prototype, "mySetter", null); +__decorate([CustomMethodDecorator()], TestDecoratePatternsService, "myStaticMethod", null); +TestDecoratePatternsService = __decorate([Injectable()], TestDecoratePatternsService); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_reference_ordering.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_reference_ordering.snap new file mode 100644 index 000000000..b78961089 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_reference_ordering.snap @@ -0,0 +1,25 @@ +--- +source: crates/oxc_angular_compiler/tests/integration_test.rs +expression: result.code +--- +import { Injectable } from "@angular/core"; +import { State, Action, Selector } from "@ngxs/store"; +import { __decorate } from "tslib"; +let OrderTestState = class OrderTestState { + instanceFirst(ctx) {} + static staticSecond(state) { + return state; + } + instanceThird(ctx) {} + static staticFourth(state) { + return state; + } +}; +__decorate([Action({ type: "First" })], OrderTestState.prototype, "instanceFirst", null); +__decorate([Action({ type: "Third" })], OrderTestState.prototype, "instanceThird", null); +__decorate([Selector()], OrderTestState, "staticSecond", null); +__decorate([Selector()], OrderTestState, "staticFourth", null); +OrderTestState = __decorate([State({ + name: "order", + defaults: {} +}), Injectable()], OrderTestState); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_style_url.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_style_url.snap index f4deebe5f..2b6ad2aed 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_style_url.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_style_url.snap @@ -1,17 +1,15 @@ --- source: crates/oxc_angular_compiler/tests/integration_test.rs +assertion_line: 6126 expression: result.code --- -import { Component } from '@angular/core'; +import { Component } from "@angular/core"; import { __decorate } from "tslib"; import __NG_CLI_RESOURCE__0 from "angular:jit:style:file;./app.css"; - let AppComponent = class AppComponent {}; -AppComponent = __decorate([ - Component({ - selector: 'app-root', - template: '

Hello

', - styles: [__NG_CLI_RESOURCE__0], -}) -], AppComponent); +AppComponent = __decorate([Component({ + selector: "app-root", + template: "

Hello

", + styles: [__NG_CLI_RESOURCE__0] +})], AppComponent); export { AppComponent }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_template_url.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_template_url.snap index e484ba5c7..36550eec9 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_template_url.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_template_url.snap @@ -1,17 +1,15 @@ --- source: crates/oxc_angular_compiler/tests/integration_test.rs +assertion_line: 6096 expression: result.code --- -import { Component } from '@angular/core'; +import { Component } from "@angular/core"; import { __decorate } from "tslib"; import __NG_CLI_RESOURCE__0 from "angular:jit:template:file;./app.html"; - let AppComponent = class AppComponent {}; -AppComponent = __decorate([ - Component({ - selector: 'app-root', - template: __NG_CLI_RESOURCE__0, - standalone: true, -}) -], AppComponent); +AppComponent = __decorate([Component({ + selector: "app-root", + template: __NG_CLI_RESOURCE__0, + standalone: true +})], AppComponent); export { AppComponent }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_union_type_ctor_params.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_union_type_ctor_params.snap index 7b1d8cc21..7a34b853c 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_union_type_ctor_params.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__jit_union_type_ctor_params.snap @@ -1,29 +1,23 @@ --- source: crates/oxc_angular_compiler/tests/integration_test.rs -assertion_line: 6360 +assertion_line: 6406 expression: result.code --- - -import { Component } from '@angular/core'; -import { ServiceA } from './a.service'; -import { ServiceB } from './b.service'; -import { ServiceC } from './c.service'; +import { Component } from "@angular/core"; +import { ServiceA } from "./a.service"; +import { ServiceB } from "./b.service"; +import { ServiceC } from "./c.service"; import { __decorate } from "tslib"; - let TestComponent = class TestComponent { - constructor( - svcA: undefined | ServiceA, - svcB: null | undefined | ServiceB, - svcC: ServiceC | null, - ) {} - -static ctorParameters = () => [ - { type: undefined }, - { type: undefined }, - { type: ServiceC } -]; + constructor(svcA, svcB, svcC) {} + static ctorParameters = () => [ + { type: undefined }, + { type: undefined }, + { type: ServiceC } + ]; }; -TestComponent = __decorate([ - Component({ selector: 'test', template: '' }) -], TestComponent); +TestComponent = __decorate([Component({ + selector: "test", + template: "" +})], TestComponent); export { TestComponent }; diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__ngfor_attribute_ordering.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__ngfor_attribute_ordering.snap index a93c4681a..5c7e17110 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__ngfor_attribute_ordering.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__ngfor_attribute_ordering.snap @@ -2,6 +2,7 @@ source: crates/oxc_angular_compiler/tests/integration_test.rs expression: result.code --- + import { Component } from '@angular/core'; import * as i0 from '@angular/core'; @@ -33,5 +34,5 @@ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({type:TestComponent,selector } (() =>{ (((typeof ngDevMode === "undefined") || ngDevMode) && i0.ɵsetClassDebugInfo(TestComponent, - {className:"TestComponent",filePath:"test.component.ts",lineNumber:1})); + {className:"TestComponent",filePath:"test.component.ts",lineNumber:9})); })(); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__selector_attrs_const_emission.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__selector_attrs_const_emission.snap index 28bd6333a..b0297f59c 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__selector_attrs_const_emission.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__selector_attrs_const_emission.snap @@ -24,5 +24,5 @@ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({type:BadgeComponent,selecto } (() =>{ (((typeof ngDevMode === "undefined") || ngDevMode) && i0.ɵsetClassDebugInfo(BadgeComponent, - {className:"BadgeComponent",filePath:"badge.component.ts",lineNumber:1})); + {className:"BadgeComponent",filePath:"badge.component.ts",lineNumber:9})); })(); diff --git a/crates/oxc_angular_compiler/tests/snapshots/integration_test__standalone_component_uses_full_mode.snap b/crates/oxc_angular_compiler/tests/snapshots/integration_test__standalone_component_uses_full_mode.snap index e75c7179d..385a018a9 100644 --- a/crates/oxc_angular_compiler/tests/snapshots/integration_test__standalone_component_uses_full_mode.snap +++ b/crates/oxc_angular_compiler/tests/snapshots/integration_test__standalone_component_uses_full_mode.snap @@ -2,6 +2,7 @@ source: crates/oxc_angular_compiler/tests/integration_test.rs expression: result.code --- + import { Component } from '@angular/core'; import * as i0 from '@angular/core'; @@ -46,5 +47,5 @@ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({type:ExternalComponent,sele } (() =>{ (((typeof ngDevMode === "undefined") || ngDevMode) && i0.ɵsetClassDebugInfo(ExternalComponent, - {className:"ExternalComponent",filePath:"test.component.ts",lineNumber:1})); + {className:"ExternalComponent",filePath:"test.component.ts",lineNumber:9})); })(); diff --git a/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_array_format_dotted_key.snap b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_array_format_dotted_key.snap new file mode 100644 index 000000000..1faf061f5 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_array_format_dotted_key.snap @@ -0,0 +1,8 @@ +--- +source: crates/oxc_angular_compiler/tests/linker_test.rs +assertion_line: 54 +expression: result.code +--- +import * as i0 from "@angular/core"; +export class MyDir {} +MyDir.ɵdir = i0.ɵɵdefineDirective({ type: MyDir, selectors: [["", "myDir", ""]], inputs: { "fxFlexAlign.xs": [0, "fxFlexAlign.xs", "fxFlexAlignXs"] }, standalone: false }); diff --git a/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_array_format_with_transform_function.snap b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_array_format_with_transform_function.snap new file mode 100644 index 000000000..5dde160d7 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_array_format_with_transform_function.snap @@ -0,0 +1,7 @@ +--- +source: crates/oxc_angular_compiler/tests/linker_test.rs +expression: result.code +--- +import * as i0 from "@angular/core"; +export class MyDir {} +MyDir.ɵdir = i0.ɵɵdefineDirective({ type: MyDir, selectors: [["", "myDir", ""]], inputs: { push: [2, "cdkConnectedOverlayPush", "push", i0.booleanAttribute] }, standalone: false }); diff --git a/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_dotted_key.snap b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_dotted_key.snap new file mode 100644 index 000000000..ecca7ee45 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_dotted_key.snap @@ -0,0 +1,8 @@ +--- +source: crates/oxc_angular_compiler/tests/linker_test.rs +assertion_line: 20 +expression: result.code +--- +import * as i0 from "@angular/core"; +export class MyDir {} +MyDir.ɵdir = i0.ɵɵdefineDirective({ type: MyDir, selectors: [["", "myDir", ""]], inputs: { "fxFlexAlign.xs": "fxFlexAlignXs" }, standalone: false }); diff --git a/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_hyphenated_key.snap b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_hyphenated_key.snap new file mode 100644 index 000000000..11ae157d7 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_hyphenated_key.snap @@ -0,0 +1,8 @@ +--- +source: crates/oxc_angular_compiler/tests/linker_test.rs +assertion_line: 28 +expression: result.code +--- +import * as i0 from "@angular/core"; +export class MyDir {} +MyDir.ɵdir = i0.ɵɵdefineDirective({ type: MyDir, selectors: [["", "myDir", ""]], inputs: { "fxFlexAlign.lt-sm": "fxFlexAlignLtSm" }, standalone: false }); diff --git a/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_object_format_dotted_key.snap b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_object_format_dotted_key.snap new file mode 100644 index 000000000..74a3b2506 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_object_format_dotted_key.snap @@ -0,0 +1,8 @@ +--- +source: crates/oxc_angular_compiler/tests/linker_test.rs +assertion_line: 46 +expression: result.code +--- +import * as i0 from "@angular/core"; +export class MyDir {} +MyDir.ɵdir = i0.ɵɵdefineDirective({ type: MyDir, selectors: [["", "myDir", ""]], inputs: { "fxFlexAlign.xs": [0, "fxFlexAlign.xs", "fxFlexAlignXs"] }, standalone: false }); diff --git a/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_simple_identifier.snap b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_simple_identifier.snap new file mode 100644 index 000000000..80a67c13e --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_inputs_simple_identifier.snap @@ -0,0 +1,8 @@ +--- +source: crates/oxc_angular_compiler/tests/linker_test.rs +assertion_line: 36 +expression: result.code +--- +import * as i0 from "@angular/core"; +export class MyDir {} +MyDir.ɵdir = i0.ɵɵdefineDirective({ type: MyDir, selectors: [["", "myDir", ""]], inputs: { fxFlexAlign: "fxFlexAlign" }, standalone: false }); diff --git a/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_outputs_dotted_key.snap b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_outputs_dotted_key.snap new file mode 100644 index 000000000..3ae23f85e --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_outputs_dotted_key.snap @@ -0,0 +1,8 @@ +--- +source: crates/oxc_angular_compiler/tests/linker_test.rs +assertion_line: 71 +expression: result.code +--- +import * as i0 from "@angular/core"; +export class MyDir {} +MyDir.ɵdir = i0.ɵɵdefineDirective({ type: MyDir, selectors: [["", "myDir", ""]], outputs: { "activate.xs": "activateXs" }, standalone: false }); diff --git a/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_outputs_hyphenated_key.snap b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_outputs_hyphenated_key.snap new file mode 100644 index 000000000..bb1959b31 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_outputs_hyphenated_key.snap @@ -0,0 +1,8 @@ +--- +source: crates/oxc_angular_compiler/tests/linker_test.rs +assertion_line: 79 +expression: result.code +--- +import * as i0 from "@angular/core"; +export class MyDir {} +MyDir.ɵdir = i0.ɵɵdefineDirective({ type: MyDir, selectors: [["", "myDir", ""]], outputs: { "activate.lt-sm": "activateLtSm" }, standalone: false }); diff --git a/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_outputs_simple_identifier.snap b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_outputs_simple_identifier.snap new file mode 100644 index 000000000..2912e8be6 --- /dev/null +++ b/crates/oxc_angular_compiler/tests/snapshots/linker_test__link_outputs_simple_identifier.snap @@ -0,0 +1,8 @@ +--- +source: crates/oxc_angular_compiler/tests/linker_test.rs +assertion_line: 87 +expression: result.code +--- +import * as i0 from "@angular/core"; +export class MyDir {} +MyDir.ɵdir = i0.ɵɵdefineDirective({ type: MyDir, selectors: [["", "myDir", ""]], outputs: { activate: "activate" }, standalone: false }); diff --git a/crates/oxc_angular_compiler/tests/variable_naming_test.rs b/crates/oxc_angular_compiler/tests/variable_naming_test.rs index 1a060f72a..f3929ea55 100644 --- a/crates/oxc_angular_compiler/tests/variable_naming_test.rs +++ b/crates/oxc_angular_compiler/tests/variable_naming_test.rs @@ -43,7 +43,7 @@ use oxc_angular_compiler::{ pipeline::{emit::compile_template, ingest::ingest_component}, transform::html_to_r3::{HtmlToR3Transform, TransformOptions}, }; -use oxc_span::Atom; +use oxc_span::Ident; use std::collections::HashSet; fn compile_template_to_js(template: &str, component_name: &str) -> String { @@ -55,7 +55,7 @@ fn compile_template_to_js(template: &str, component_name: &str) -> String { let transformer = HtmlToR3Transform::new(&allocator, template, TransformOptions::default()); let r3_result = transformer.transform(&nodes); assert!(r3_result.errors.is_empty(), "Transform errors: {:?}", r3_result.errors); - let mut job = ingest_component(&allocator, Atom::from(component_name), r3_result.nodes); + let mut job = ingest_component(&allocator, Ident::from(component_name), r3_result.nodes); let result = compile_template(&mut job); let emitter = JsEmitter::new(); let mut output = String::new(); diff --git a/napi/.DS_Store b/napi/.DS_Store new file mode 100644 index 000000000..1d93e2274 Binary files /dev/null and b/napi/.DS_Store differ diff --git a/napi/angular-compiler/README.md b/napi/angular-compiler/README.md index 912344709..f63c921a3 100644 --- a/napi/angular-compiler/README.md +++ b/napi/angular-compiler/README.md @@ -139,6 +139,9 @@ interface TransformOptions { // i18n i18nUseExternalIds?: boolean + // Final component style output + minifyComponentStyles?: boolean + // Component metadata selector?: string standalone?: boolean @@ -176,6 +179,7 @@ interface AngularPluginOptions { // Style processing inlineStylesExtension?: string + minifyComponentStyles?: boolean | 'auto' // File replacements fileReplacements?: Array<{ @@ -185,6 +189,14 @@ interface AngularPluginOptions { } ``` +`minifyComponentStyles` resolves like this: + +- `true`: always minify component styles +- `false`: never minify component styles +- `"auto"` or `undefined`: follow the resolved Vite minification settings + +For `"auto"`, the plugin uses `build.cssMinify` when it is set, otherwise it falls back to `build.minify`. In dev, `"auto"` defaults to `false`. + ## Vite Plugin Architecture The Vite plugin consists of three sub-plugins: diff --git a/napi/angular-compiler/benchmarks/bitwarden/package.json b/napi/angular-compiler/benchmarks/bitwarden/package.json index 209f45883..5aacc1bf5 100644 --- a/napi/angular-compiler/benchmarks/bitwarden/package.json +++ b/napi/angular-compiler/benchmarks/bitwarden/package.json @@ -13,15 +13,15 @@ "benchmark:incremental": "oxnode benchmark.ts --incremental" }, "dependencies": { - "@angular/animations": "20.3.18", - "@angular/cdk": "20.2.14", - "@angular/common": "20.3.18", - "@angular/compiler": "20.3.18", - "@angular/core": "20.3.18", - "@angular/forms": "20.3.18", - "@angular/platform-browser": "20.3.18", - "@angular/platform-browser-dynamic": "20.3.18", - "@angular/router": "20.3.18", + "@angular/animations": "21.2.6", + "@angular/cdk": "21.2.4", + "@angular/common": "21.2.6", + "@angular/compiler": "21.2.6", + "@angular/core": "21.2.6", + "@angular/forms": "21.2.6", + "@angular/platform-browser": "21.2.6", + "@angular/platform-browser-dynamic": "21.2.6", + "@angular/router": "21.2.6", "core-js": "^3.48.0", "rxjs": "catalog:", "tslib": "catalog:", @@ -40,7 +40,7 @@ "postcss-import": "^16.1.1", "postcss-nested": "^7.0.2", "sass": "^1.97.2", - "tailwindcss": "^3.4.18", + "tailwindcss": "^4.0.0", "typescript": "catalog:", "vite": "catalog:", "vite-plugin-top-level-await": "^1.6.0", diff --git a/napi/angular-compiler/benchmarks/typedb-web/package.json b/napi/angular-compiler/benchmarks/typedb-web/package.json index c59abeaf0..ba95be4b3 100644 --- a/napi/angular-compiler/benchmarks/typedb-web/package.json +++ b/napi/angular-compiler/benchmarks/typedb-web/package.json @@ -13,34 +13,34 @@ "benchmark:incremental": "tsx benchmark.ts --incremental" }, "dependencies": { - "@angular/animations": "20.3.18", - "@angular/cdk": "20.2.14", - "@angular/common": "20.3.18", - "@angular/compiler": "20.3.18", - "@angular/core": "20.3.18", - "@angular/forms": "20.3.18", - "@angular/material": "20.2.14", - "@angular/platform-browser": "20.3.18", - "@angular/platform-browser-dynamic": "20.3.18", - "@angular/router": "20.3.18", - "@portabletext/to-html": "2.0.17", - "@sanity/asset-utils": "1.3.2", - "@sanity/image-url": "1.0.2", + "@angular/animations": "21.2.6", + "@angular/cdk": "21.2.4", + "@angular/common": "21.2.6", + "@angular/compiler": "21.2.6", + "@angular/core": "21.2.6", + "@angular/forms": "21.2.6", + "@angular/material": "21.2.4", + "@angular/platform-browser": "21.2.6", + "@angular/platform-browser-dynamic": "21.2.6", + "@angular/router": "21.2.6", + "@portabletext/to-html": "5.0.2", + "@sanity/asset-utils": "2.3.0", + "@sanity/image-url": "2.0.3", "cookieconsent": "3.1.1", "d3-force": "3.0.0", "fontfaceobserver": "2.3.0", "interactjs": "1.10.27", - "ngx-cookieconsent": "4.0.2", + "ngx-cookieconsent": "8.0.0", "pixi.js-legacy": "7.4.3", - "posthog-js": "1.268.8", + "posthog-js": "1.363.6", "prismjs": "1.30.0", "rxjs": "7.8.2", - "tslib": "2.6.1", + "tslib": "2.8.1", "zone.js": "0.16.1" }, "devDependencies": { "@oxc-angular/vite": "workspace:^", - "@sanity/types": "3.99.0", + "@sanity/types": "5.18.0", "@types/d3-force": "3.0.10", "@types/fontfaceobserver": "2.1.3", "@types/node": "^22.19.3", diff --git a/napi/angular-compiler/e2e/compare/fixtures/control-flow/for-track-variations.fixture.ts b/napi/angular-compiler/e2e/compare/fixtures/control-flow/for-track-variations.fixture.ts index 2b1ed1d88..4ab2b7dc8 100644 --- a/napi/angular-compiler/e2e/compare/fixtures/control-flow/for-track-variations.fixture.ts +++ b/napi/angular-compiler/e2e/compare/fixtures/control-flow/for-track-variations.fixture.ts @@ -102,6 +102,81 @@ export class ForWithEmptyComponent { `.trim(), expectedFeatures: ['ɵɵrepeaterCreate', 'ɵɵrepeaterTrackByIdentity'], }, + { + name: 'for-track-binary-with-component-method', + category: 'control-flow', + description: '@for with track using binary operator and component method', + className: 'ForTrackBinaryComponentMethodComponent', + type: 'full-transform', + sourceCode: ` +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-for-track-binary-method', + standalone: true, + template: \` + @for (item of items; track prefix() + item.id) { +
{{ item.name }}
+ } + \`, +}) +export class ForTrackBinaryComponentMethodComponent { + items = [{ id: '1', name: 'Item 1' }]; + prefix() { return 'pfx'; } +} + `.trim(), + expectedFeatures: ['ɵɵrepeaterCreate'], + }, + { + name: 'for-track-nullish-coalescing-with-component-method', + category: 'control-flow', + description: '@for with track using ?? operator and component method', + className: 'ForTrackNullishCoalesceComponent', + type: 'full-transform', + sourceCode: ` +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-for-track-nullish', + standalone: true, + template: \` + @for (tag of tags; track (tag.queryPrefix ?? queryPrefix()) + '.' + tag.key) { + {{ tag.key }} + } + \`, +}) +export class ForTrackNullishCoalesceComponent { + tags = [{ queryPrefix: null, key: 'k1' }]; + queryPrefix() { return 'default'; } +} + `.trim(), + expectedFeatures: ['ɵɵrepeaterCreate'], + }, + { + name: 'for-track-ternary-with-component-method', + category: 'control-flow', + description: '@for with track using ternary and component method', + className: 'ForTrackTernaryComponentMethodComponent', + type: 'full-transform', + sourceCode: ` +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-for-track-ternary-method', + standalone: true, + template: \` + @for (item of items; track useId() ? item.id : item.name) { +
{{ item.name }}
+ } + \`, +}) +export class ForTrackTernaryComponentMethodComponent { + items = [{ id: '1', name: 'Item 1' }]; + useId() { return true; } +} + `.trim(), + expectedFeatures: ['ɵɵrepeaterCreate'], + }, { name: 'for-context-variables', category: 'control-flow', diff --git a/napi/angular-compiler/e2e/compare/package.json b/napi/angular-compiler/e2e/compare/package.json index 730087dde..1307dcd1b 100644 --- a/napi/angular-compiler/e2e/compare/package.json +++ b/napi/angular-compiler/e2e/compare/package.json @@ -33,8 +33,8 @@ "@oxc-node/cli": "catalog:", "@oxc-node/core": "catalog:", "@types/node": "catalog:", - "oxc-parser": "^0.120.0", - "oxc-transform": "^0.120.0", + "oxc-parser": "^0.123.0", + "oxc-transform": "^0.123.0", "oxfmt": "catalog:", "typescript": "catalog:", "vitest": "catalog:" diff --git a/napi/angular-compiler/e2e/tests/build-minify-component-styles.spec.ts b/napi/angular-compiler/e2e/tests/build-minify-component-styles.spec.ts new file mode 100644 index 000000000..7e077b65e --- /dev/null +++ b/napi/angular-compiler/e2e/tests/build-minify-component-styles.spec.ts @@ -0,0 +1,97 @@ +import { execSync } from 'node:child_process' +import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' + +import { test, expect } from '@playwright/test' + +const __dirname = fileURLToPath(new URL('.', import.meta.url)) +const APP_DIR = join(__dirname, '../app') +const BUILD_OUT_DIR = join(APP_DIR, 'dist-minify') +const TEMP_CONFIG = join(APP_DIR, 'vite.config.minify.ts') + +function writeBuildConfig(minify: boolean): void { + writeFileSync( + TEMP_CONFIG, + ` +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { angular } from '@oxc-angular/vite'; +import { defineConfig } from 'vite'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const tsconfig = path.resolve(__dirname, './tsconfig.json'); + +export default defineConfig({ + plugins: [ + angular({ + tsconfig, + liveReload: false, + minifyComponentStyles: 'auto', + }), + ], + build: { + minify: ${minify}, + outDir: 'dist-minify', + rollupOptions: { + external: [/^@angular\\/.+$/, /^rxjs(?:\\/.+)?$/, /^tslib$/], + }, + }, +}); +`.trim(), + 'utf-8', + ) +} + +function cleanup(): void { + rmSync(TEMP_CONFIG, { force: true }) + rmSync(BUILD_OUT_DIR, { recursive: true, force: true }) +} + +function readBuiltJs(): string { + const assetDir = join(BUILD_OUT_DIR, 'assets') + const files = existsSync(assetDir) ? readdirSync(assetDir) : [] + const jsFiles = files.filter((file) => file.endsWith('.js')) + + expect(jsFiles.length).toBeGreaterThan(0) + + return jsFiles.map((file) => readFileSync(join(assetDir, file), 'utf-8')).join('\n') +} + +test.describe('build auto minify component styles', () => { + test.afterEach(() => { + cleanup() + }) + + test('minifies embedded component styles when build.minify is true', () => { + writeBuildConfig(true) + + execSync('npx vite build --config vite.config.minify.ts', { + cwd: APP_DIR, + stdio: 'pipe', + timeout: 60000, + }) + + const output = readBuiltJs() + + expect(output).toContain('.card-title[_ngcontent-%COMP%]{color:green;margin:0}') + }) + + test('keeps embedded component styles unminified when build.minify is false', () => { + writeBuildConfig(false) + + execSync('npx vite build --config vite.config.minify.ts', { + cwd: APP_DIR, + stdio: 'pipe', + timeout: 60000, + }) + + const output = readBuiltJs() + + expect(output).toContain( + '.card-title[_ngcontent-%COMP%] {\\n color: green;\\n margin: 0;\\n}', + ) + }) +}) diff --git a/napi/angular-compiler/index.d.ts b/napi/angular-compiler/index.d.ts index 0d1d2a6cb..375efcc0c 100644 --- a/napi/angular-compiler/index.d.ts +++ b/napi/angular-compiler/index.d.ts @@ -731,14 +731,14 @@ export interface TopLevelDeclarations { export declare function transformAngularFile( source: string, filename: string, - options: TransformOptions, + options?: TransformOptions | undefined | null, resolvedResources?: ResolvedResources | undefined | null, ): Promise export declare function transformAngularFileSync( source: string, filename: string, - options: TransformOptions, + options?: TransformOptions | undefined | null, resolvedResources?: ResolvedResources | undefined | null, ): TransformResult @@ -826,6 +826,13 @@ export interface TransformOptions { * Default: false (metadata is dev-only and usually stripped in production) */ emitClassMetadata?: boolean + /** + * Minify final component styles before emitting them into `styles: [...]`. + * + * This runs after Angular style encapsulation, so it applies to the same + * final CSS strings that are embedded in generated component definitions. + */ + minifyComponentStyles?: boolean /** * Resolved import paths for host directives and other imports. * diff --git a/napi/angular-compiler/index.js b/napi/angular-compiler/index.js index 8def2894d..77cca455e 100644 --- a/napi/angular-compiler/index.js +++ b/napi/angular-compiler/index.js @@ -83,12 +83,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-android-arm64/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -106,12 +106,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-android-arm-eabi/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -137,12 +137,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-win32-x64-gnu/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -160,12 +160,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-win32-x64-msvc/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -184,12 +184,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-win32-ia32-msvc/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -207,12 +207,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-win32-arm64-msvc/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -233,12 +233,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-darwin-universal/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -256,12 +256,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-darwin-x64/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -279,12 +279,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-darwin-arm64/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -306,12 +306,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-freebsd-x64/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -329,12 +329,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-freebsd-arm64/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -357,12 +357,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-linux-x64-musl/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -380,12 +380,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-linux-x64-gnu/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -405,12 +405,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-linux-arm64-musl/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -428,12 +428,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-linux-arm64-gnu/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -453,12 +453,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-linux-arm-musleabihf/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -476,12 +476,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-linux-arm-gnueabihf/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -501,12 +501,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-linux-loong64-musl/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -524,12 +524,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-linux-loong64-gnu/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -549,12 +549,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-linux-riscv64-musl/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -572,12 +572,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-linux-riscv64-gnu/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -596,12 +596,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-linux-ppc64-gnu/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -619,12 +619,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-linux-s390x-gnu/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -646,12 +646,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-openharmony-arm64/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -669,12 +669,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-openharmony-x64/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding @@ -692,12 +692,12 @@ function requireNative() { const bindingPackageVersion = require('@oxc-angular/binding-openharmony-arm/package.json').version if ( - bindingPackageVersion !== '0.0.17' && + bindingPackageVersion !== '0.0.21' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0' ) { throw new Error( - `Native binding package version mismatch, expected 0.0.17 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, + `Native binding package version mismatch, expected 0.0.21 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`, ) } return binding diff --git a/napi/angular-compiler/package.json b/napi/angular-compiler/package.json index e27e60581..f7cbb5313 100644 --- a/napi/angular-compiler/package.json +++ b/napi/angular-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@oxc-angular/vite", - "version": "0.0.17", + "version": "0.0.21", "description": "Oxc Angular Compiler Vite plugin", "keywords": [ "angular", diff --git a/napi/angular-compiler/src/lib.rs b/napi/angular-compiler/src/lib.rs index 818984c9c..7efc41667 100644 --- a/napi/angular-compiler/src/lib.rs +++ b/napi/angular-compiler/src/lib.rs @@ -27,7 +27,7 @@ use oxc_angular_compiler::{ build_prop_decorators_metadata as core_build_prop_decorators_metadata, compile_template_for_hmr, compile_template_to_js_with_options, encapsulate_style as rust_encapsulate_style, generate_hmr_update_module_from_js, - generate_style_update_module, shim_css_text, + generate_style_update_module, }; use oxc_napi::OxcError; @@ -190,6 +190,12 @@ pub struct TransformOptions { /// Default: false (metadata is dev-only and usually stripped in production) pub emit_class_metadata: Option, + /// Minify final component styles before emitting them into `styles: [...]`. + /// + /// This runs after Angular style encapsulation, so it applies to the same + /// final CSS strings that are embedded in generated component definitions. + pub minify_component_styles: Option, + /// Resolved import paths for host directives and other imports. /// /// Maps local identifier name (e.g., "AriaDisableDirective") to the resolved @@ -231,6 +237,7 @@ impl From for RustTransformOptions { resolved_imports: options.resolved_imports, // Class metadata for TestBed support emit_class_metadata: options.emit_class_metadata.unwrap_or(false), + minify_component_styles: options.minify_component_styles.unwrap_or(false), } } } @@ -512,12 +519,21 @@ pub fn compile_for_hmr_sync( let encapsulated_styles: Option> = if all_styles.is_empty() { None } else { - Some( - all_styles - .iter() - .map(|style| shim_css_text(style, "_ngcontent-%COMP%", "_nghost-%COMP%")) - .collect(), - ) + let styles: Vec = all_styles + .iter() + .map(|style| { + oxc_angular_compiler::styles::finalize_component_style( + style, + true, + "_ngcontent-%COMP%", + "_nghost-%COMP%", + opts.minify_component_styles, + ) + }) + .filter(|style| !style.trim().is_empty()) + .collect(); + + if styles.is_empty() { None } else { Some(styles) } }; // Generate HMR module with declarations, encapsulated styles, and consts @@ -1026,7 +1042,7 @@ pub struct ResolvedResources { pub struct TransformAngularFileTask { source: String, filename: String, - options: TransformOptions, + options: Option, resolved_resources: Option, } @@ -1041,7 +1057,7 @@ impl Task for TransformAngularFileTask { }; let allocator = Allocator::default(); - let rust_options: RustTransformOptions = std::mem::take(&mut self.options).into(); + let rust_options: RustTransformOptions = self.options.take().unwrap_or_default().into(); // Convert resolved resources to Rust types let rust_resources = self @@ -1053,7 +1069,7 @@ impl Task for TransformAngularFileTask { &allocator, &self.filename, &self.source, - &rust_options, + Some(&rust_options), rust_resources.as_ref(), ); @@ -1104,7 +1120,7 @@ impl Task for TransformAngularFileTask { pub fn transform_angular_file( source: String, filename: String, - options: TransformOptions, + options: Option, resolved_resources: Option, ) -> AsyncTask { AsyncTask::new(TransformAngularFileTask { source, filename, options, resolved_resources }) @@ -1114,7 +1130,7 @@ pub fn transform_angular_file( pub fn transform_angular_file_sync( source: String, filename: String, - options: TransformOptions, + options: Option, resolved_resources: Option, ) -> napi::Result { let mut result = TransformAngularFileTask { source, filename, options, resolved_resources }; @@ -1250,7 +1266,7 @@ pub fn compile_pipe_sync( use oxc_angular_compiler::{R3PipeMetadataBuilder, compile_pipe, extract_pipe_metadata}; use oxc_ast::ast::{Declaration, ExportDefaultDeclarationKind, Statement}; use oxc_parser::Parser; - use oxc_span::{Atom, SourceType}; + use oxc_span::{Ident, SourceType}; let allocator = Allocator::default(); let source_type = SourceType::from_path(&file_path).unwrap_or_default(); @@ -1300,7 +1316,7 @@ pub fn compile_pipe_sync( .is_standalone(metadata.standalone); if let Some(name) = &metadata.pipe_name { - builder = builder.pipe_name(Atom::from(name.as_str())); + builder = builder.pipe_name(Ident::from(name.as_str())); } let r3_metadata = builder.build(); @@ -1699,7 +1715,7 @@ pub fn extract_component_metadata_sync( metadata .export_as .iter() - .map(oxc_span::Atom::as_str) + .map(oxc_span::Ident::as_str) .collect::>() .join(","), ) @@ -1788,25 +1804,25 @@ pub fn compile_injector_sync(input: InjectorCompileInput) -> InjectorNapiCompile use oxc_angular_compiler::output::ast::{OutputExpression, ReadVarExpr}; use oxc_angular_compiler::output::emitter::JsEmitter; use oxc_angular_compiler::{R3InjectorMetadataBuilder, compile_injector}; - use oxc_span::Atom; + use oxc_span::Ident; let allocator = Allocator::default(); // Create type expression for the injector class let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(input.name.as_str()), source_span: None }, + ReadVarExpr { name: Ident::from(input.name.as_str()), source_span: None }, &allocator, )); // Build the metadata let mut builder = R3InjectorMetadataBuilder::new(&allocator) - .name(Atom::from(input.name.as_str())) + .name(Ident::from(input.name.as_str())) .r#type(type_expr); // Add providers if present (as a variable reference) if let Some(providers_str) = &input.providers { let providers_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(providers_str.as_str()), source_span: None }, + ReadVarExpr { name: Ident::from(providers_str.as_str()), source_span: None }, &allocator, )); builder = builder.providers(providers_expr); @@ -1816,7 +1832,7 @@ pub fn compile_injector_sync(input: InjectorCompileInput) -> InjectorNapiCompile if let Some(imports) = &input.imports { for import_name in imports { let import_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(import_name.as_str()), source_span: None }, + ReadVarExpr { name: Ident::from(import_name.as_str()), source_span: None }, &allocator, )); builder = builder.add_import(import_expr); @@ -1889,7 +1905,7 @@ pub fn compile_class_metadata_sync( use oxc_angular_compiler::output::emitter::JsEmitter; use oxc_ast::ast::{Class, Declaration, ExportDefaultDeclarationKind, Statement}; use oxc_parser::Parser; - use oxc_span::{Atom, SourceType}; + use oxc_span::{Ident, SourceType}; let allocator = Allocator::default(); let source_type = SourceType::from_path(&file_path).unwrap_or_default(); @@ -1943,7 +1959,7 @@ pub fn compile_class_metadata_sync( // Build the class type expression let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(class_name.as_str()), source_span: None }, + ReadVarExpr { name: Ident::from(class_name.as_str()), source_span: None }, &allocator, )); @@ -2128,7 +2144,7 @@ fn compile_factory_impl(input: FactoryCompileInput) -> FactoryNapiCompileResult }; use oxc_angular_compiler::output::ast::{OutputExpression, ReadVarExpr}; use oxc_angular_compiler::output::emitter::JsEmitter; - use oxc_span::Atom; + use oxc_span::Ident; let allocator = Allocator::default(); @@ -2151,7 +2167,7 @@ fn compile_factory_impl(input: FactoryCompileInput) -> FactoryNapiCompileResult // Create type expression for the class let type_expr = OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(input.name.as_str()), source_span: None }, + ReadVarExpr { name: Ident::from(input.name.as_str()), source_span: None }, &allocator, )); @@ -2167,7 +2183,7 @@ fn compile_factory_impl(input: FactoryCompileInput) -> FactoryNapiCompileResult // Use ReadVarExpr for token since WrappedNodeExpr cannot be emitted let token = dep.token.as_ref().map(|t| { OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(t.as_str()), source_span: None }, + ReadVarExpr { name: Ident::from(t.as_str()), source_span: None }, &allocator, )) }); @@ -2175,7 +2191,7 @@ fn compile_factory_impl(input: FactoryCompileInput) -> FactoryNapiCompileResult // Use ReadVarExpr for attribute name type let attribute_name_type = dep.attribute_name_type.as_ref().map(|a| { OutputExpression::ReadVar(Box::new_in( - ReadVarExpr { name: Atom::from(a.as_str()), source_span: None }, + ReadVarExpr { name: Ident::from(a.as_str()), source_span: None }, &allocator, )) }); @@ -2205,7 +2221,7 @@ fn compile_factory_impl(input: FactoryCompileInput) -> FactoryNapiCompileResult // Build factory metadata let factory_name = format!("{}_Factory", input.name); let metadata = R3FactoryMetadata::Constructor(R3ConstructorFactoryMetadata { - name: Atom::from(input.name.as_str()), + name: Ident::from(input.name.as_str()), type_expr: type_expr.clone_in(&allocator), type_decl: type_expr, type_argument_count: 0, diff --git a/napi/angular-compiler/test/hmr-hot-update.test.ts b/napi/angular-compiler/test/hmr-hot-update.test.ts new file mode 100644 index 000000000..9a34e5c99 --- /dev/null +++ b/napi/angular-compiler/test/hmr-hot-update.test.ts @@ -0,0 +1,276 @@ +/** + * Tests for handleHotUpdate behavior (Issue #185). + * + * The plugin's handleHotUpdate hook must distinguish between: + * 1. Component resource files (templates/styles) → handled by custom fs.watch, return [] + * 2. Non-component files (global CSS, etc.) → let Vite handle normally + * + * Previously, the plugin returned [] for ALL .css/.html files, which swallowed + * HMR updates for global stylesheets and prevented PostCSS/Tailwind from + * processing changes. + */ +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' + +import type { Plugin, ModuleNode, HmrContext } from 'vite' +import { normalizePath } from 'vite' +import { afterAll, beforeAll, describe, it, expect, vi } from 'vitest' + +import { angular } from '../vite-plugin/index.js' + +let tempDir: string +let appDir: string +let templatePath: string +let stylePath: string + +beforeAll(() => { + tempDir = mkdtempSync(join(tmpdir(), 'hmr-test-')) + appDir = join(tempDir, 'src', 'app') + mkdirSync(appDir, { recursive: true }) + + templatePath = join(appDir, 'app.component.html') + stylePath = join(appDir, 'app.component.css') + + writeFileSync(templatePath, '

Hello

') + writeFileSync(stylePath, 'h1 { color: red; }') +}) + +afterAll(() => { + rmSync(tempDir, { recursive: true, force: true }) +}) + +function getAngularPlugin() { + const plugin = angular({ liveReload: true }).find( + (candidate) => candidate.name === '@oxc-angular/vite', + ) + + if (!plugin) { + throw new Error('Failed to find @oxc-angular/vite plugin') + } + + return plugin +} + +function createMockServer() { + const wsMessages: any[] = [] + const unwatchedFiles = new Set() + + return { + watcher: { + unwatch(file: string) { + unwatchedFiles.add(file) + }, + on: vi.fn(), + emit: vi.fn(), + }, + ws: { + send(msg: any) { + wsMessages.push(msg) + }, + on: vi.fn(), + }, + moduleGraph: { + getModuleById: vi.fn(() => null), + invalidateModule: vi.fn(), + }, + middlewares: { + use: vi.fn(), + }, + config: { + root: tempDir, + }, + _wsMessages: wsMessages, + _unwatchedFiles: unwatchedFiles, + } +} + +function createMockHmrContext( + file: string, + modules: Partial[] = [], + server?: any, +): HmrContext { + return { + file, + timestamp: Date.now(), + modules: modules as ModuleNode[], + read: async () => '', + server: server ?? createMockServer(), + } as HmrContext +} + +async function callHandleHotUpdate( + plugin: Plugin, + ctx: HmrContext, +): Promise { + if (typeof plugin.handleHotUpdate === 'function') { + return (plugin.handleHotUpdate as Function).call(plugin, ctx) + } + return undefined +} + +async function callPluginHook( + hook: + | { + handler: (...args: TArgs) => TResult + } + | ((...args: TArgs) => TResult) + | undefined, + ...args: TArgs +): Promise { + if (!hook) return undefined + if (typeof hook === 'function') return hook(...args) + return hook.handler(...args) +} + +/** + * Set up a plugin through the full Vite lifecycle so that internal state + * (watchMode, viteServer, resourceToComponent, componentIds) is populated. + */ +async function setupPluginWithServer(plugin: Plugin) { + const mockServer = createMockServer() + + // config() sets watchMode = true when command === 'serve' + await callPluginHook( + plugin.config as Plugin['config'], + {} as any, + { + command: 'serve', + mode: 'development', + } as any, + ) + + // configResolved() stores the resolved config + await callPluginHook( + plugin.configResolved as Plugin['configResolved'], + { + build: {}, + isProduction: false, + } as any, + ) + + // configureServer() sets up the custom watcher and stores viteServer + if (typeof plugin.configureServer === 'function') { + await (plugin.configureServer as Function)(mockServer) + } + + // Replace the real fs.watch-based watcher with a no-op to avoid EPERM + // errors on Windows when temp files are cleaned up. resourceToComponent + // is populated in transform *before* watchFn is called, so the map is + // still correctly populated for handleHotUpdate tests. + ;(mockServer as any).__angularWatchTemplate = () => {} + + return mockServer +} + +/** + * Transform a component that references external template + style files, + * populating resourceToComponent and componentIds. + */ +async function transformComponent(plugin: Plugin) { + const componentFile = join(appDir, 'app.component.ts') + const componentSource = ` + import { Component } from '@angular/core'; + + @Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'], + }) + export class AppComponent {} + ` + + if (!plugin.transform || typeof plugin.transform === 'function') { + throw new Error('Expected plugin transform handler') + } + + await plugin.transform.handler.call( + { error() {}, warn() {} } as any, + componentSource, + componentFile, + ) +} + +describe('handleHotUpdate - Issue #185', () => { + it('should let non-component CSS files pass through to Vite HMR', async () => { + const plugin = getAngularPlugin() + await setupPluginWithServer(plugin) + + // A global CSS file (not referenced by any component's styleUrls) + const globalCssFile = normalizePath(join(tempDir, 'src', 'styles.css')) + const mockModules = [{ id: globalCssFile }] + const ctx = createMockHmrContext(globalCssFile, mockModules) + + const result = await callHandleHotUpdate(plugin, ctx) + + // Non-component CSS should NOT be swallowed — either undefined (pass through) + // or the original modules array, but NOT an empty array + if (result !== undefined) { + expect(result).toEqual(mockModules) + } + }) + + it('should return [] for component CSS files managed by custom watcher', async () => { + const plugin = getAngularPlugin() + const mockServer = await setupPluginWithServer(plugin) + await transformComponent(plugin) + + // The component's CSS file IS in resourceToComponent + const componentCssFile = normalizePath(stylePath) + const mockModules = [{ id: componentCssFile }] + const ctx = createMockHmrContext(componentCssFile, mockModules, mockServer) + + const result = await callHandleHotUpdate(plugin, ctx) + + // Component resources MUST be swallowed (return []) + expect(result).toEqual([]) + }) + + it('should return [] for component template HTML files managed by custom watcher', async () => { + const plugin = getAngularPlugin() + const mockServer = await setupPluginWithServer(plugin) + await transformComponent(plugin) + + // The component's HTML template IS in resourceToComponent + const componentHtmlFile = normalizePath(templatePath) + const ctx = createMockHmrContext(componentHtmlFile, [{ id: componentHtmlFile }], mockServer) + + const result = await callHandleHotUpdate(plugin, ctx) + + // Component templates MUST be swallowed (return []) + expect(result).toEqual([]) + }) + + it('should not swallow non-resource HTML files', async () => { + const plugin = getAngularPlugin() + await setupPluginWithServer(plugin) + + // index.html is NOT a component template + const indexHtml = normalizePath(join(tempDir, 'index.html')) + const mockModules = [{ id: indexHtml }] + const ctx = createMockHmrContext(indexHtml, mockModules) + + const result = await callHandleHotUpdate(plugin, ctx) + + // Non-component HTML should pass through, not be swallowed + if (result !== undefined) { + expect(result).toEqual(mockModules) + } + }) + + it('should pass through non-style/template files unchanged', async () => { + const plugin = getAngularPlugin() + await setupPluginWithServer(plugin) + + const utilFile = normalizePath(join(tempDir, 'src', 'utils.ts')) + const mockModules = [{ id: utilFile }] + const ctx = createMockHmrContext(utilFile, mockModules) + + const result = await callHandleHotUpdate(plugin, ctx) + + // Non-Angular .ts files should pass through with their modules + if (result !== undefined) { + expect(result).toEqual(mockModules) + } + }) +}) diff --git a/napi/angular-compiler/test/linker.test.ts b/napi/angular-compiler/test/linker.test.ts new file mode 100644 index 000000000..4a0356177 --- /dev/null +++ b/napi/angular-compiler/test/linker.test.ts @@ -0,0 +1,200 @@ +import { describe, it, expect } from 'vitest' + +import { linkAngularPackageSync } from '../index.js' + +/** + * Minimal Angular partial declaration fixtures that simulate the structure + * of FESM bundle files (including Angular 21+ chunk files). + * Uses actual Unicode ɵ (U+0275) characters as they appear in real Angular packages. + */ +const INJECTABLE_CHUNK = ` +import * as i0 from '@angular/core'; + +class PlatformLocation { + historyGo(relativePosition) { + throw new Error('Not implemented'); + } + static \u0275fac = i0.\u0275\u0275ngDeclareFactory({ + minVersion: "12.0.0", + version: "21.0.0", + ngImport: i0, + type: PlatformLocation, + deps: [], + target: i0.\u0275\u0275FactoryTarget.Injectable + }); + static \u0275prov = i0.\u0275\u0275ngDeclareInjectable({ + minVersion: "12.0.0", + version: "21.0.0", + ngImport: i0, + type: PlatformLocation, + providedIn: "platform", + useClass: undefined + }); +} + +export { PlatformLocation }; +` + +const NG_MODULE_CHUNK = ` +import * as i0 from '@angular/core'; + +class CommonModule { + static \u0275fac = i0.\u0275\u0275ngDeclareFactory({ + minVersion: "12.0.0", + version: "21.0.0", + ngImport: i0, + type: CommonModule, + deps: [], + target: i0.\u0275\u0275FactoryTarget.NgModule + }); + static \u0275mod = i0.\u0275\u0275ngDeclareNgModule({ + minVersion: "14.0.0", + version: "21.0.0", + ngImport: i0, + type: CommonModule, + imports: [], + exports: [] + }); + static \u0275inj = i0.\u0275\u0275ngDeclareInjector({ + minVersion: "12.0.0", + version: "21.0.0", + ngImport: i0, + type: CommonModule + }); +} + +export { CommonModule }; +` + +const PIPE_CHUNK = ` +import * as i0 from '@angular/core'; + +class AsyncPipe { + constructor(ref) { + this._ref = ref; + } + static \u0275fac = i0.\u0275\u0275ngDeclareFactory({ + minVersion: "12.0.0", + version: "21.0.0", + ngImport: i0, + type: AsyncPipe, + deps: [{ token: i0.ChangeDetectorRef }], + target: i0.\u0275\u0275FactoryTarget.Pipe + }); + static \u0275pipe = i0.\u0275\u0275ngDeclarePipe({ + minVersion: "14.0.0", + version: "21.0.0", + ngImport: i0, + type: AsyncPipe, + isStandalone: false, + name: "async", + pure: false + }); +} + +export { AsyncPipe }; +` + +describe('Angular linker - chunk file linking', () => { + it('should link \u0275\u0275ngDeclareFactory and \u0275\u0275ngDeclareInjectable', () => { + const result = linkAngularPackageSync( + INJECTABLE_CHUNK, + 'node_modules/@angular/common/fesm2022/_platform_location-chunk.mjs', + ) + + expect(result.linked).toBe(true) + expect(result.code).not.toContain('\u0275\u0275ngDeclare') + }) + + it('should link \u0275\u0275ngDeclareNgModule and \u0275\u0275ngDeclareInjector', () => { + const result = linkAngularPackageSync( + NG_MODULE_CHUNK, + 'node_modules/@angular/common/fesm2022/_common_module-chunk.mjs', + ) + + expect(result.linked).toBe(true) + expect(result.code).not.toContain('\u0275\u0275ngDeclare') + }) + + it('should link \u0275\u0275ngDeclarePipe', () => { + const result = linkAngularPackageSync( + PIPE_CHUNK, + 'node_modules/@angular/common/fesm2022/_pipes-chunk.mjs', + ) + + expect(result.linked).toBe(true) + expect(result.code).not.toContain('\u0275\u0275ngDeclare') + }) + + it('should return linked: false for files without declarations', () => { + const code = ` + export function helper() { return 42; } + ` + const result = linkAngularPackageSync( + code, + 'node_modules/@angular/common/fesm2022/_utils-chunk.mjs', + ) + + expect(result.linked).toBe(false) + }) +}) + +describe('Linker transform filter matching', () => { + // These mirror the two-stage filter from angular-linker-plugin.ts: + // 1. Broad static filter (NODE_MODULES_JS_REGEX) for Vite's filter mechanism + // 2. Precise handler-level check (JS_EXT_REGEX) inside the transform handler + const NODE_MODULES_JS_REGEX = /node_modules/ + const JS_EXT_REGEX = /\.[cm]?js(?:\?.*)?$/ + + function matches(id: string) { + return NODE_MODULES_JS_REGEX.test(id) && JS_EXT_REGEX.test(id) + } + + it('should match standard Angular FESM files', () => { + expect(matches('node_modules/@angular/common/fesm2022/common.mjs')).toBe(true) + }) + + it('should match chunk files', () => { + expect(matches('node_modules/@angular/common/fesm2022/_platform_location-chunk.mjs')).toBe(true) + }) + + it('should match absolute paths', () => { + expect( + matches( + '/Users/dev/project/node_modules/@angular/common/fesm2022/_platform_location-chunk.mjs', + ), + ).toBe(true) + }) + + it('should match paths with Vite query strings', () => { + expect(matches('node_modules/@angular/common/fesm2022/common.mjs?v=abc123')).toBe(true) + }) + + it('should match chunk files with Vite query strings', () => { + expect( + matches('node_modules/@angular/common/fesm2022/_platform_location-chunk.mjs?v=df7b0864'), + ).toBe(true) + }) + + it('should match Windows-style backslash paths', () => { + expect(matches('node_modules\\@angular\\common\\fesm2022\\common.mjs')).toBe(true) + }) + + it('should match .js and .cjs files', () => { + expect(matches('node_modules/@ngrx/store/fesm2022/ngrx-store.js')).toBe(true) + expect(matches('node_modules/some-lib/index.cjs')).toBe(true) + }) + + it('should match PrimeNG files (excluded from optimizeDeps)', () => { + expect(matches('node_modules/primeng/fesm2022/primeng-table.mjs')).toBe(true) + expect(matches('node_modules/primeng/fesm2022/primeng-table.mjs?v=abc123')).toBe(true) + }) + + it('should not match non-JS files', () => { + expect(matches('node_modules/@angular/common/fesm2022/common.d.ts')).toBe(false) + }) + + it('should not match application source files', () => { + expect(matches('src/app/app.component.ts')).toBe(false) + }) +}) diff --git a/napi/angular-compiler/test/plugin.test.ts b/napi/angular-compiler/test/plugin.test.ts new file mode 100644 index 000000000..9f85de9e8 --- /dev/null +++ b/napi/angular-compiler/test/plugin.test.ts @@ -0,0 +1,200 @@ +import type { Plugin } from 'vite' +import { describe, expect, it } from 'vitest' + +import { angular } from '../vite-plugin/index.js' + +const COMPONENT_SOURCE = ` + import { Component } from '@angular/core'; + + @Component({ + selector: 'app-root', + template: '
Hello
', + styles: ['.container { color: red; background: transparent; }'], + }) + export class AppComponent {} +` + +function getAngularPlugin() { + const plugin = angular().find((candidate) => candidate.name === '@oxc-angular/vite') + + if (!plugin) { + throw new Error('Failed to find @oxc-angular/vite plugin') + } + + return plugin +} + +async function callPluginHook( + hook: + | { + handler: (...args: TArgs) => TResult + } + | ((...args: TArgs) => TResult) + | undefined, + ...args: TArgs +): Promise { + if (!hook) { + return undefined + } + + if (typeof hook === 'function') { + return hook(...args) + } + + return hook.handler(...args) +} + +async function transformWithAutoMinify( + build: { cssMinify?: boolean | string; minify?: boolean | string }, + resolvedBuild: { cssMinify?: boolean | string; minify?: boolean | string }, +): Promise { + const plugin = getAngularPlugin() + + await callPluginHook( + plugin.config as Plugin['config'], + { build } as any, + { command: 'build', mode: 'production' } as any, + ) + await callPluginHook( + plugin.configResolved as Plugin['configResolved'], + { + build: resolvedBuild, + isProduction: true, + } as any, + ) + + if (!plugin.transform || typeof plugin.transform === 'function') { + throw new Error('Expected plugin transform handler') + } + + const result = await plugin.transform.handler.call( + { + error(message: string) { + throw new Error(message) + }, + warn() {}, + } as any, + COMPONENT_SOURCE, + 'app.component.ts', + ) + + if (!result || typeof result !== 'object' || !('code' in result)) { + throw new Error('Expected transform result with code') + } + + return result.code as string +} + +async function transformWithAutoMinifyFromTransformContext(resolvedBuild: { + cssMinify?: boolean | string + minify?: boolean | string +}): Promise { + const plugin = getAngularPlugin() + + if (!plugin.transform || typeof plugin.transform === 'function') { + throw new Error('Expected plugin transform handler') + } + + const result = await plugin.transform.handler.call( + { + environment: { + config: { + build: resolvedBuild, + }, + }, + error(message: string) { + throw new Error(message) + }, + warn() {}, + } as any, + COMPONENT_SOURCE, + 'app.component.ts', + ) + + if (!result || typeof result !== 'object' || !('code' in result)) { + throw new Error('Expected transform result with code') + } + + return result.code as string +} + +async function transformWithAutoMinifyFromOutputOptions( + outputMinify: boolean | string, + resolvedBuild: { cssMinify?: boolean | string; minify?: boolean | string }, +): Promise { + const plugin = getAngularPlugin() + + await callPluginHook( + plugin.outputOptions as Plugin['outputOptions'], + { + minify: outputMinify, + } as any, + ) + + if (!plugin.transform || typeof plugin.transform === 'function') { + throw new Error('Expected plugin transform handler') + } + + const result = await plugin.transform.handler.call( + { + environment: { + config: { + build: resolvedBuild, + }, + }, + error(message: string) { + throw new Error(message) + }, + warn() {}, + } as any, + COMPONENT_SOURCE, + 'app.component.ts', + ) + + if (!result || typeof result !== 'object' || !('code' in result)) { + throw new Error('Expected transform result with code') + } + + return result.code as string +} + +describe('@oxc-angular/vite auto component style minification', () => { + it('should prefer inline build.minify when auto is used', async () => { + const code = await transformWithAutoMinify({ minify: false }, { cssMinify: true, minify: true }) + + expect(code).toContain('.container[_ngcontent-%COMP%] { color: red; background: transparent; }') + }) + + it('should prefer inline build.cssMinify when auto is used', async () => { + const code = await transformWithAutoMinify( + { cssMinify: false, minify: true }, + { cssMinify: true, minify: true }, + ) + + expect(code).toContain('.container[_ngcontent-%COMP%] { color: red; background: transparent; }') + }) + + it('should fall back to resolved config when inline build minify is not set', async () => { + const code = await transformWithAutoMinify({}, { cssMinify: true, minify: true }) + + expect(code).toContain('.container[_ngcontent-%COMP%]{color:red;background:0 0}') + }) + + it('should fall back to transform context build config when config hooks are skipped', async () => { + const code = await transformWithAutoMinifyFromTransformContext({ + cssMinify: true, + minify: true, + }) + + expect(code).toContain('.container[_ngcontent-%COMP%]{color:red;background:0 0}') + }) + + it('should prefer output minify when config hooks are skipped', async () => { + const code = await transformWithAutoMinifyFromOutputOptions(false, { + cssMinify: true, + minify: true, + }) + + expect(code).toContain('.container[_ngcontent-%COMP%] { color: red; background: transparent; }') + }) +}) diff --git a/napi/angular-compiler/test/ssr-hmr.test.ts b/napi/angular-compiler/test/ssr-hmr.test.ts index 4e8e4b7a6..ec9644e92 100644 --- a/napi/angular-compiler/test/ssr-hmr.test.ts +++ b/napi/angular-compiler/test/ssr-hmr.test.ts @@ -13,7 +13,7 @@ */ import { describe, it, expect } from 'vitest' -import { transformAngularFile } from '../index.js' +import { compileForHmrSync, transformAngularFile } from '../index.js' const COMPONENT_SOURCE = ` import { Component } from '@angular/core'; @@ -90,3 +90,18 @@ describe('Vite plugin SSR behavior (Issue #109)', () => { expect(ssrResult.code).toContain('ɵɵdefineComponent') }) }) + +describe('Component style minification', () => { + it('should minify encapsulated HMR styles when enabled', () => { + const result = compileForHmrSync( + '
Hello
', + 'AppComponent', + 'app.component.ts', + ['.container { color: red; background: transparent; }'], + { minifyComponentStyles: true }, + ) + + expect(result.errors).toHaveLength(0) + expect(result.hmrModule).toContain('.container[_ngcontent-%COMP%]{color:red;background:0 0}') + }) +}) diff --git a/napi/angular-compiler/test/transform.test.ts b/napi/angular-compiler/test/transform.test.ts index 64d127f1a..721c81720 100644 --- a/napi/angular-compiler/test/transform.test.ts +++ b/napi/angular-compiler/test/transform.test.ts @@ -91,6 +91,26 @@ describe('transformAngularFile', () => { expect(result.errors).toHaveLength(0) expect(Object.keys(result.templateUpdates).length).toBeGreaterThan(0) }) + + it('should minify final component styles when enabled', async () => { + const source = ` + import { Component } from '@angular/core'; + + @Component({ + selector: 'app-root', + template: '
Hello
', + styles: ['.container { color: red; background: transparent; }'], + }) + export class AppComponent {} + ` + + const result = await transformAngularFile(source, 'app.component.ts', { + minifyComponentStyles: true, + }) + + expect(result.errors).toHaveLength(0) + expect(result.code).toContain('.container[_ngcontent-%COMP%]{color:red;background:0 0}') + }) }) describe('extractComponentUrlsSync', () => { diff --git a/napi/angular-compiler/vite-plugin/angular-linker-plugin.ts b/napi/angular-compiler/vite-plugin/angular-linker-plugin.ts index 8e38315dc..954b727bb 100644 --- a/napi/angular-compiler/vite-plugin/angular-linker-plugin.ts +++ b/napi/angular-compiler/vite-plugin/angular-linker-plugin.ts @@ -24,8 +24,14 @@ const LINKER_DECLARATION_PREFIX = '\u0275\u0275ngDeclare' // Skip these packages - they don't need linking const SKIP_REGEX = /[\\/]@angular[\\/](?:compiler|core)[\\/]/ -// Match JS files in node_modules (Angular FESM bundles) -const NODE_MODULES_JS_REGEX = /node_modules\/.*\.[cm]?js$/ +// Broad filter for the transform hook — deliberately simple so that every +// Vite/Rolldown version can evaluate it. Precise extension + query-string +// checks are done inside the handler. +const NODE_MODULES_JS_REGEX = /node_modules/ + +// Precise check run inside the handler: matches .js / .mjs / .cjs with an +// optional Vite query string (?v=…) and works on both Unix and Windows paths. +const JS_EXT_REGEX = /\.[cm]?js(?:\?.*)?$/ /** * Run the OXC Rust linker on the given code. @@ -96,9 +102,18 @@ export function angularLinkerPlugin(): Plugin { transform: { filter: { id: NODE_MODULES_JS_REGEX, - code: LINKER_DECLARATION_PREFIX, }, async handler(code, id) { + // Precise extension check (covers .js, .mjs, .cjs with optional ?v=… query) + if (!JS_EXT_REGEX.test(id)) { + return + } + + // Quick check: skip files without partial declarations + if (!code.includes(LINKER_DECLARATION_PREFIX)) { + return + } + // Skip packages that don't need linking if (SKIP_REGEX.test(id)) { return diff --git a/napi/angular-compiler/vite-plugin/index.ts b/napi/angular-compiler/vite-plugin/index.ts index 0bdedb85d..ae3ece188 100644 --- a/napi/angular-compiler/vite-plugin/index.ts +++ b/napi/angular-compiler/vite-plugin/index.ts @@ -61,6 +61,21 @@ export interface PluginOptions { /** Enable zoneless mode. */ zoneless?: boolean + /** + * Minify final component styles before emitting them into `styles: [...]`. + * + * When set to `"auto"` or left undefined, this follows Vite's resolved CSS + * minification settings for production builds: + * + * - `true`: always minify component styles + * - `false`: never minify component styles + * - `"auto"`/`undefined`: use `build.cssMinify` when set, otherwise fall back + * to `build.minify` + * + * In dev, `"auto"` defaults to `false`. + */ + minifyComponentStyles?: boolean | 'auto' + /** File replacements (for environment files). */ fileReplacements?: Array<{ replace: string; with: string }> @@ -90,6 +105,44 @@ export interface PluginOptions { // Match all TypeScript files - we'll filter by @Component/@Directive decorator in the handler const ANGULAR_TS_REGEX = /\.tsx?$/ const ANGULAR_COMPONENT_PREFIX = '@ng/component' +type InlineBuildMinifyOptions = { + cssMinify?: boolean | string + minify?: boolean | string +} + +function resolveMinifyComponentStyles( + option: PluginOptions['minifyComponentStyles'], + isBuild: boolean, + inlineBuild?: InlineBuildMinifyOptions, + outputMinify?: unknown, + resolvedBuild?: ResolvedConfig['build'], +): boolean { + if (typeof option === 'boolean') { + return option + } + + if (!isBuild) { + return false + } + + if (inlineBuild?.cssMinify !== undefined) { + return inlineBuild.cssMinify !== false + } + + if (inlineBuild?.minify !== undefined) { + return inlineBuild.minify !== false + } + + if (outputMinify !== undefined) { + return outputMinify !== false + } + + if (resolvedBuild?.cssMinify !== undefined) { + return resolvedBuild.cssMinify !== false + } + + return resolvedBuild?.minify !== false +} /** * Create the Angular Vite plugin. @@ -126,6 +179,8 @@ export function angular(options: PluginOptions = {}): Plugin[] { let resolvedConfig: ResolvedConfig let viteServer: ViteDevServer | undefined let watchMode = false + let inlineBuild: InlineBuildMinifyOptions | undefined + let outputMinify: unknown // Track component IDs for HMR const componentIds = new Map() @@ -139,6 +194,18 @@ export function angular(options: PluginOptions = {}): Plugin[] { // Track component files with pending HMR updates (set by fs.watch, checked by HMR endpoint) const pendingHmrUpdates = new Set() + function getMinifyComponentStyles(context?: { + environment?: { config?: { build?: ResolvedConfig['build'] } } + }): boolean { + return resolveMinifyComponentStyles( + options.minifyComponentStyles, + !watchMode, + inlineBuild, + outputMinify, + context?.environment?.config?.build ?? resolvedConfig?.build, + ) + } + /** * Resolve external template/style URLs and read their contents. */ @@ -212,8 +279,14 @@ export function angular(options: PluginOptions = {}): Plugin[] { function angularPlugin(): Plugin { return { name: '@oxc-angular/vite', - async config(_, { command }) { + async config(config, { command }) { watchMode = command === 'serve' + inlineBuild = config.build + ? { + cssMinify: config.build.cssMinify, + minify: config.build.minify, + } + : undefined return { optimizeDeps: { @@ -232,6 +305,10 @@ export function angular(options: PluginOptions = {}): Plugin[] { configResolved(config) { resolvedConfig = config }, + outputOptions(options) { + outputMinify = options.minify + return null + }, // Safety net: resolve @ng/component virtual modules in SSR context. // The browser serves these via HTTP middleware, but Vite's module runner // (used by Nitro/SSR) resolves through plugin hooks instead. @@ -293,6 +370,13 @@ export function angular(options: PluginOptions = {}): Plugin[] { event: 'angular:component-update', data: eventData, }) + + // Invalidate Vite's module transform cache so that a full page reload + // picks up the new template/style content instead of serving stale output. + const mod = server.moduleGraph.getModuleById(componentFile) + if (mod) { + server.moduleGraph.invalidateModule(mod) + } } } } @@ -410,7 +494,10 @@ export function angular(options: PluginOptions = {}): Plugin[] { } } - const result = compileForHmrSync(templateContent, className, resolvedId, styles) + const result = compileForHmrSync(templateContent, className, resolvedId, styles, { + angularVersion: pluginOptions.angularVersion, + minifyComponentStyles: getMinifyComponentStyles(), + }) res.setHeader('Content-Type', 'text/javascript') res.setHeader('Cache-Control', 'no-cache') @@ -490,6 +577,20 @@ export function angular(options: PluginOptions = {}): Plugin[] { // gated by componentIds, which are only populated for client transforms. if (watchMode && viteServer) { const watchFn = (viteServer as any).__angularWatchTemplate + + // Prune stale entries: if this component previously referenced + // different resources (e.g., templateUrl was renamed), remove the + // old reverse mappings so handleHotUpdate no longer swallows those files. + // Re-add pruned files to Vite's watcher so they can be processed as + // normal assets if used elsewhere (e.g., as a global stylesheet). + const newDeps = new Set(dependencies.map(normalizePath)) + for (const [resource, owner] of resourceToComponent) { + if (owner === actualId && !newDeps.has(resource)) { + resourceToComponent.delete(resource) + viteServer.watcher.add(resource) + } + } + for (const dep of dependencies) { const normalizedDep = normalizePath(dep) // Track reverse mapping for HMR: resource → component @@ -507,6 +608,7 @@ export function angular(options: PluginOptions = {}): Plugin[] { jit: pluginOptions.jit, hmr: pluginOptions.liveReload && watchMode && !isSSR, angularVersion: pluginOptions.angularVersion, + minifyComponentStyles: getMinifyComponentStyles(this as any), } const result = await transformAngularFile(code, actualId, transformOptions, resources) @@ -552,13 +654,24 @@ export function angular(options: PluginOptions = {}): Plugin[] { ctx.modules.map((m) => m.id).join(', '), ) - // Template/style files are handled by our custom fs.watch in configureServer. - // We dynamically unwatch them from Vite's watcher during transform, so they shouldn't - // normally trigger handleHotUpdate. If they do appear here (e.g., file not yet transformed - // or from another plugin), return [] to prevent Vite's default handling. + // Component resource files (templates/styles referenced via templateUrl/styleUrls) + // are handled by our custom fs.watch in configureServer. We dynamically unwatch them + // from Vite's watcher during transform, so they shouldn't normally trigger handleHotUpdate. + // If they do appear here (e.g., file not yet transformed or from another plugin), + // return [] to prevent Vite's default handling. + // + // However, non-component files (e.g., global stylesheets imported in main.ts) are NOT + // managed by our custom watcher and must flow through Vite's normal HMR pipeline so that + // PostCSS/Tailwind and other plugins can process them correctly. if (/\.(html?|css|scss|sass|less)$/.test(ctx.file)) { - debugHmr('ignoring resource file in handleHotUpdate (handled by custom watcher)') - return [] + const normalizedFile = normalizePath(ctx.file) + if (resourceToComponent.has(normalizedFile)) { + debugHmr( + 'ignoring component resource file in handleHotUpdate (handled by custom watcher)', + ) + return [] + } + debugHmr('letting non-component resource file through to Vite HMR: %s', normalizedFile) } // Handle component file changes @@ -573,6 +686,14 @@ export function angular(options: PluginOptions = {}): Plugin[] { debugHmr('componentIds keys: %O', Array.from(componentIds.keys())) if (isComponent && hasComponentId) { + // If there's a pending HMR update for this component, the .ts module + // was invalidated by our fs.watch handler (template/style change), not + // by an actual .ts file edit. Skip the full reload — HMR handles it. + if (pendingHmrUpdates.has(ctx.file)) { + debugHmr('skipping full reload — pending HMR update from template/style change') + return [] + } + debugHmr('triggering full reload for component file change') // Component FILE changes require a full reload because: // - Class definition changes can't be hot-swapped safely diff --git a/package.json b/package.json index 22b5c25f7..2de22b70e 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "devDependencies": { "@napi-rs/cli": "catalog:", "oxfmt": "catalog:", - "oxlint": "^1.52.0", - "oxlint-tsgolint": "^0.17.0" + "oxlint": "^1.56.0", + "oxlint-tsgolint": "^0.19.0" }, - "packageManager": "pnpm@10.32.1" + "packageManager": "pnpm@10.33.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a264ff55..d63510b13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,8 +7,8 @@ settings: catalogs: default: '@napi-rs/cli': - specifier: 3.5.1 - version: 3.5.1 + specifier: 3.6.0 + version: 3.6.0 '@oxc-node/cli': specifier: 0.0.35 version: 0.0.35 @@ -16,32 +16,32 @@ catalogs: specifier: 0.0.35 version: 0.0.35 '@tailwindcss/vite': - specifier: 4.2.1 - version: 4.2.1 + specifier: 4.2.2 + version: 4.2.2 '@types/node': specifier: 24.1.0 version: 24.1.0 oxfmt: - specifier: 0.40.0 - version: 0.40.0 + specifier: 0.43.0 + version: 0.43.0 rxjs: specifier: 7.8.2 version: 7.8.2 tailwindcss: - specifier: 4.2.1 - version: 4.2.1 + specifier: 4.2.2 + version: 4.2.2 tslib: specifier: 2.8.1 version: 2.8.1 typescript: - specifier: 5.9.3 - version: 5.9.3 + specifier: 6.0.2 + version: 6.0.2 vite: - specifier: 8.0.0 - version: 8.0.0 + specifier: 8.0.3 + version: 8.0.3 vitest: - specifier: 4.1.0 - version: 4.1.0 + specifier: 4.1.2 + version: 4.1.2 importers: @@ -49,16 +49,16 @@ importers: devDependencies: '@napi-rs/cli': specifier: 'catalog:' - version: 3.5.1(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(node-addon-api@7.1.1) + version: 3.6.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(node-addon-api@7.1.1) oxfmt: specifier: 'catalog:' - version: 0.40.0 + version: 0.43.0 oxlint: - specifier: ^1.52.0 - version: 1.52.0(oxlint-tsgolint@0.17.0) + specifier: ^1.56.0 + version: 1.58.0(oxlint-tsgolint@0.19.0) oxlint-tsgolint: - specifier: ^0.17.0 - version: 0.17.0 + specifier: ^0.19.0 + version: 0.19.0 napi/angular-compiler: dependencies: @@ -68,16 +68,16 @@ importers: devDependencies: '@angular/compiler': specifier: ^21.2.2 - version: 21.2.4 + version: 21.2.6 '@angular/compiler-cli': specifier: ^21.2.2 - version: 21.2.2(@angular/compiler@21.2.4)(typescript@5.9.3) + version: 21.2.6(@angular/compiler@21.2.6)(typescript@6.0.2) '@napi-rs/cli': specifier: 'catalog:' - version: 3.5.1(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(node-addon-api@7.1.1) + version: 3.6.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(node-addon-api@7.1.1) '@oxc-node/cli': specifier: 'catalog:' - version: 0.0.35 + version: 0.0.35(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) '@playwright/test': specifier: ^1.58.0 version: 1.58.2 @@ -86,49 +86,49 @@ importers: version: 24.1.0 oxfmt: specifier: 'catalog:' - version: 0.40.0 + version: 0.43.0 typescript: specifier: 'catalog:' - version: 5.9.3 + version: 6.0.2 vite: specifier: 'catalog:' - version: 8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0) + version: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0) vitest: specifier: 'catalog:' - version: 4.1.0(@types/node@24.1.0)(vite@8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)) + version: 4.1.2(@opentelemetry/api@1.9.0)(@types/node@24.1.0)(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0)) napi/angular-compiler/benchmarks/bitwarden: dependencies: '@angular/animations': - specifier: 20.3.18 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)) + specifier: 21.2.6 + version: 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) '@angular/cdk': - specifier: 20.2.14 - version: 20.2.14(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + specifier: 21.2.4 + version: 21.2.4(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) '@angular/common': - specifier: 20.3.18 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + specifier: 21.2.6 + version: 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: 20.3.18 - version: 20.3.18 + specifier: 21.2.6 + version: 21.2.6 '@angular/core': - specifier: 20.3.18 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1) + specifier: 21.2.6 + version: 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/forms': - specifier: 20.3.18 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + specifier: 21.2.6 + version: 21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) '@angular/platform-browser': - specifier: 20.3.18 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)) + specifier: 21.2.6 + version: 21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) '@angular/platform-browser-dynamic': - specifier: 20.3.18 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))) + specifier: 21.2.6 + version: 21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@21.2.6)(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))) '@angular/router': - specifier: 20.3.18 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + specifier: 21.2.6 + version: 21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) core-js: specifier: ^3.48.0 - version: 3.48.0 + version: 3.49.0 rxjs: specifier: 'catalog:' version: 7.8.2 @@ -144,13 +144,13 @@ importers: version: link:../.. '@oxc-node/cli': specifier: 'catalog:' - version: 0.0.35 + version: 0.0.35(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) '@oxc-node/core': specifier: 'catalog:' - version: 0.0.35 + version: 0.0.35(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) '@tailwindcss/container-queries': specifier: ^0.1.1 - version: 0.1.1(tailwindcss@3.4.19(tsx@4.21.0)) + version: 0.1.1(tailwindcss@4.2.2) '@types/node': specifier: ^22.19.3 version: 22.19.15 @@ -174,67 +174,67 @@ importers: version: 7.0.2(postcss@8.5.8) sass: specifier: ^1.97.2 - version: 1.97.3 + version: 1.98.0 tailwindcss: - specifier: ^3.4.18 - version: 3.4.19(tsx@4.21.0) + specifier: ^4.0.0 + version: 4.2.2 typescript: specifier: 'catalog:' - version: 5.9.3 + version: 6.0.2 vite: specifier: 'catalog:' - version: 8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@1.21.7)(sass@1.97.3)(tsx@4.21.0) + version: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0) vite-plugin-top-level-await: specifier: ^1.6.0 - version: 1.6.0(rollup@4.59.0)(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@1.21.7)(sass@1.97.3)(tsx@4.21.0)) + version: 1.6.0(rollup@4.60.0)(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0)) vite-plugin-wasm: specifier: ^3.5.0 - version: 3.5.0(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@1.21.7)(sass@1.97.3)(tsx@4.21.0)) + version: 3.6.0(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0)) vite-tsconfig-paths: specifier: ^6.0.3 - version: 6.1.1(typescript@5.9.3)(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@1.21.7)(sass@1.97.3)(tsx@4.21.0)) + version: 6.1.1(typescript@6.0.2)(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0)) napi/angular-compiler/benchmarks/typedb-web: dependencies: '@angular/animations': - specifier: 20.3.18 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)) + specifier: 21.2.6 + version: 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) '@angular/cdk': - specifier: 20.2.14 - version: 20.2.14(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + specifier: 21.2.4 + version: 21.2.4(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) '@angular/common': - specifier: 20.3.18 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + specifier: 21.2.6 + version: 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: 20.3.18 - version: 20.3.18 + specifier: 21.2.6 + version: 21.2.6 '@angular/core': - specifier: 20.3.18 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1) + specifier: 21.2.6 + version: 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/forms': - specifier: 20.3.18 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + specifier: 21.2.6 + version: 21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) '@angular/material': - specifier: 20.2.14 - version: 20.2.14(2f832b0b84a3673c875d1ecf350e328d) + specifier: 21.2.4 + version: 21.2.4(2045f7bd5f6e95e11cc97fff9a766245) '@angular/platform-browser': - specifier: 20.3.18 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)) + specifier: 21.2.6 + version: 21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) '@angular/platform-browser-dynamic': - specifier: 20.3.18 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))) + specifier: 21.2.6 + version: 21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@21.2.6)(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))) '@angular/router': - specifier: 20.3.18 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + specifier: 21.2.6 + version: 21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) '@portabletext/to-html': - specifier: 2.0.17 - version: 2.0.17 + specifier: 5.0.2 + version: 5.0.2 '@sanity/asset-utils': - specifier: 1.3.2 - version: 1.3.2 + specifier: 2.3.0 + version: 2.3.0 '@sanity/image-url': - specifier: 1.0.2 - version: 1.0.2 + specifier: 2.0.3 + version: 2.0.3 cookieconsent: specifier: 3.1.1 version: 3.1.1 @@ -248,14 +248,14 @@ importers: specifier: 1.10.27 version: 1.10.27 ngx-cookieconsent: - specifier: 4.0.2 - version: 4.0.2(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(cookieconsent@3.1.1)(rxjs@7.8.2) + specifier: 8.0.0 + version: 8.0.0(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(cookieconsent@3.1.1)(rxjs@7.8.2) pixi.js-legacy: specifier: 7.4.3 version: 7.4.3(d1d99e8ae917814d844dec1222a79cec) posthog-js: - specifier: 1.268.8 - version: 1.268.8 + specifier: 1.363.6 + version: 1.363.6 prismjs: specifier: 1.30.0 version: 1.30.0 @@ -263,8 +263,8 @@ importers: specifier: 7.8.2 version: 7.8.2 tslib: - specifier: 2.6.1 - version: 2.6.1 + specifier: 2.8.1 + version: 2.8.1 zone.js: specifier: 0.16.1 version: 0.16.1 @@ -273,8 +273,8 @@ importers: specifier: workspace:^ version: link:../.. '@sanity/types': - specifier: 3.99.0 - version: 3.99.0(@types/react@19.2.14) + specifier: 5.18.0 + version: 5.18.0(@types/react@19.2.14) '@types/d3-force': specifier: 3.0.10 version: 3.0.10 @@ -289,34 +289,34 @@ importers: version: 1.26.6 sass: specifier: ^1.97.2 - version: 1.97.3 + version: 1.98.0 tsx: specifier: ^4.21.0 version: 4.21.0 typescript: specifier: 'catalog:' - version: 5.9.3 + version: 6.0.2 vite: specifier: 'catalog:' - version: 8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0) + version: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0) vite-tsconfig-paths: specifier: ^6.0.3 - version: 6.1.1(typescript@5.9.3)(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)) + version: 6.1.1(typescript@6.0.2)(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0)) napi/angular-compiler/e2e/app: dependencies: '@angular/common': specifier: ^21.2.2 - version: 21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + version: 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': specifier: ^21.2.2 - version: 21.2.4 + version: 21.2.6 '@angular/core': specifier: ^21.2.2 - version: 21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1) + version: 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/platform-browser': specifier: ^21.2.2 - version: 21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)) + version: 21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) rxjs: specifier: 'catalog:' version: 7.8.2 @@ -326,46 +326,46 @@ importers: devDependencies: '@angular/compiler-cli': specifier: ^21.2.2 - version: 21.2.2(@angular/compiler@21.2.4)(typescript@5.9.3) + version: 21.2.6(@angular/compiler@21.2.6)(typescript@6.0.2) '@oxc-angular/vite': specifier: workspace:^ version: link:../.. typescript: specifier: 'catalog:' - version: 5.9.3 + version: 6.0.2 vite: specifier: 'catalog:' - version: 8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0) + version: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0) napi/angular-compiler/e2e/compare: dependencies: '@angular/cdk': specifier: ^21.2.1 - version: 21.2.1(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + version: 21.2.4(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) '@angular/common': specifier: ^21.2.2 - version: 21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + version: 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': specifier: ^21.2.2 - version: 21.2.4 + version: 21.2.6 '@angular/compiler-cli': specifier: ^21.2.2 - version: 21.2.2(@angular/compiler@21.2.4)(typescript@5.9.3) + version: 21.2.6(@angular/compiler@21.2.6)(typescript@6.0.2) '@angular/core': specifier: ^21.2.2 - version: 21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1) + version: 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/forms': specifier: ^21.2.2 - version: 21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + version: 21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) '@angular/platform-browser': specifier: ^21.2.2 - version: 21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)) + version: 21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) '@angular/router': specifier: ^21.2.2 - version: 21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + version: 21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) diff: specifier: ^8.0.3 - version: 8.0.3 + version: 8.0.4 fast-glob: specifier: ^3.3.3 version: 3.3.3 @@ -384,49 +384,49 @@ importers: version: link:../.. '@oxc-node/cli': specifier: 'catalog:' - version: 0.0.35 + version: 0.0.35(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) '@oxc-node/core': specifier: 'catalog:' - version: 0.0.35 + version: 0.0.35(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) '@types/node': specifier: 'catalog:' version: 24.1.0 oxc-parser: - specifier: ^0.120.0 - version: 0.120.0 + specifier: ^0.123.0 + version: 0.123.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) oxc-transform: - specifier: ^0.120.0 - version: 0.120.0 + specifier: ^0.123.0 + version: 0.123.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) oxfmt: specifier: 'catalog:' - version: 0.40.0 + version: 0.43.0 typescript: specifier: 'catalog:' - version: 5.9.3 + version: 6.0.2 vitest: specifier: 'catalog:' - version: 4.1.0(@types/node@24.1.0)(vite@8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)) + version: 4.1.2(@opentelemetry/api@1.9.0)(@types/node@24.1.0)(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0)) napi/playground: dependencies: '@angular/common': specifier: ^21.2.0 - version: 21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + version: 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': specifier: ^21.2.0 - version: 21.2.4 + version: 21.2.6 '@angular/core': specifier: ^21.1.6 - version: 21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1) + version: 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/forms': specifier: ^21.2.0 - version: 21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + version: 21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) '@angular/platform-browser': specifier: ^21.2.0 - version: 21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)) + version: 21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) '@angular/router': specifier: ^21.2.0 - version: 21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + version: 21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) rxjs: specifier: 'catalog:' version: 7.8.2 @@ -436,46 +436,42 @@ importers: devDependencies: '@angular/build': specifier: ^21.2.0 - version: 21.2.1(@angular/compiler-cli@21.2.2(@angular/compiler@21.2.4)(typescript@5.9.3))(@angular/compiler@21.2.4)(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)))(@types/node@24.1.0)(chokidar@5.0.0)(jiti@2.6.1)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.9.3)(vitest@4.1.0(@types/node@24.1.0)(vite@8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0))) + version: 21.2.4(@angular/compiler-cli@21.2.6(@angular/compiler@21.2.6)(typescript@6.0.2))(@angular/compiler@21.2.6)(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(chokidar@5.0.0)(jiti@2.6.1)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(tslib@2.8.1)(tsx@4.21.0)(typescript@6.0.2)(vitest@4.1.2(@opentelemetry/api@1.9.0)(@types/node@24.1.0)(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0))) '@angular/compiler-cli': specifier: ^21.2.0 - version: 21.2.2(@angular/compiler@21.2.4)(typescript@5.9.3) + version: 21.2.6(@angular/compiler@21.2.6)(typescript@6.0.2) '@oxc-angular/vite': specifier: workspace:^ version: link:../angular-compiler '@tailwindcss/vite': specifier: 'catalog:' - version: 4.2.1(vite@8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)) + version: 4.2.2(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)) '@types/node': specifier: 'catalog:' version: 24.1.0 tailwindcss: specifier: 'catalog:' - version: 4.2.1 + version: 4.2.2 typescript: specifier: 'catalog:' - version: 5.9.3 + version: 6.0.2 vite: specifier: 'catalog:' - version: 8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0) + version: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0) packages: - '@alloc/quick-lru@5.2.0': - resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} - engines: {node: '>=10'} - '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2102.1': - resolution: {integrity: sha512-x2Qqz6oLYvEh9UBUG0AP1A4zROO/VP+k+zM9+4c2uZw1uqoBQFmutqgzncjVU7cR9R0RApgx9JRZHDFtQru68w==} + '@angular-devkit/architect@0.2102.4': + resolution: {integrity: sha512-GlmXHICxxGCZgkH8KM5ASkzftxhYygGQ3IKIG//987erh8kDa4m+ku20Dc08xVvv7Wf4WeFjFZULIjokiT43Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular-devkit/core@21.2.1': - resolution: {integrity: sha512-TpXGjERqVPN8EPt7LdmWAwh0oNQ/6uWFutzGZiXhJy81n1zb1O1XrqhRAmvP1cAo5O+na6IV2JkkCmxL6F8GUg==} + '@angular-devkit/core@21.2.4': + resolution: {integrity: sha512-U3Mln7x8gRi2GlxlrXefEOHcCgbkRvMll98SD7JYf7FJLXrqoEmuR6w2gsfbN9meYvw9geoicWWkkAmVE/8K+Q==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: ^5.0.0 @@ -483,14 +479,14 @@ packages: chokidar: optional: true - '@angular/animations@20.3.18': - resolution: {integrity: sha512-XFxgSyjfs0SRD2vQVFJljmM4z9nTvUoI8TRqSre/+l8D2FgzD5pG67Aj2BgDgpSFAUkIcI37G48ijK7a3ZZ3WA==} + '@angular/animations@21.2.6': + resolution: {integrity: sha512-SPzTOlkyVagPdb7OMe9hw3dnpMGq2p/nADatzNfRUMXwit8AU8VaiPIrFRsCD52sAL1zDDj60gKsk/dprzIyFA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 20.3.18 + '@angular/core': 21.2.6 - '@angular/build@21.2.1': - resolution: {integrity: sha512-cUpLNHJp9taII/FOcJHHfQYlMcZSRaf6eIxgSNS6Xfx1CeGoJNDN+J8+GFk+H1CPJt1EvbfyZ+dE5DbsgTD/QQ==} + '@angular/build@21.2.4': + resolution: {integrity: sha512-H/3qBfITqCipFFntUj961YbjlJsXjFI1Td7KZWF+WbcPVW11AgS2+CouE5aqdt4Gx7QZOM5HiBe+f7Ub1dgxAg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler': ^21.0.0 @@ -500,7 +496,7 @@ packages: '@angular/platform-browser': ^21.0.0 '@angular/platform-server': ^21.0.0 '@angular/service-worker': ^21.0.0 - '@angular/ssr': ^21.2.1 + '@angular/ssr': ^21.2.4 karma: ^6.4.0 less: ^4.2.0 ng-packagr: ^21.0.0 @@ -535,72 +531,41 @@ packages: vitest: optional: true - '@angular/cdk@20.2.14': - resolution: {integrity: sha512-7bZxc01URbiPiIBWThQ69XwOxVduqEKN4PhpbF2AAyfMc/W8Hcr4VoIJOwL0O1Nkq5beS8pCAqoOeIgFyXd/kg==} - peerDependencies: - '@angular/common': ^20.0.0 || ^21.0.0 - '@angular/core': ^20.0.0 || ^21.0.0 - rxjs: ^6.5.3 || ^7.4.0 - - '@angular/cdk@21.2.1': - resolution: {integrity: sha512-JUFV8qLnO7CU5v4W0HzXSQrFkkJ4RH/qqdwrf9lup7YEnsLxB7cTGhsVisc9pWKAJsoNZ4pXCVOkqKc1mFL7dw==} + '@angular/cdk@21.2.4': + resolution: {integrity: sha512-Zv+q9Z/wVWTt0ckuO3gnU7PbpCLTr1tKPEsofLGGzDufA5/85aBLn2UiLcjlY6wQ+V3EMqANhGo/8XJgvBEYFA==} peerDependencies: '@angular/common': ^21.0.0 || ^22.0.0 '@angular/core': ^21.0.0 || ^22.0.0 '@angular/platform-browser': ^21.0.0 || ^22.0.0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/common@20.3.18': - resolution: {integrity: sha512-M62oQbSTRmnGavIVCwimoadg/PDWadgNhactMm9fgH0eM9rx+iWBAYJk4VufO0bwOhysFpRZpJgXlFjOifz/Jw==} + '@angular/common@21.2.6': + resolution: {integrity: sha512-2FcpZ1h6AZ4JwCIlnpHCYrbRTGQTOj/RFXkuX/qw7K6cFmJGfWFMmr++xWtHZEvUddfbR9hqDo+v1mkqEKE/Kw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 20.3.18 + '@angular/core': 21.2.6 rxjs: ^6.5.3 || ^7.4.0 - '@angular/common@21.2.2': - resolution: {integrity: sha512-xpVYV+MgqWzdjTCFxe3uJGpFOc84YrO4H4oX9HkzI5yQ5OLkQlndtq+OAUK8e330iacg4XHArft3SNDj1LaFfg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - peerDependencies: - '@angular/core': 21.2.2 - rxjs: ^6.5.3 || ^7.4.0 - - '@angular/compiler-cli@21.2.2': - resolution: {integrity: sha512-TFg2wXUZ1FdUikNyR27PxuCXuqqlJhL6Mr/cBYuc4HbtBfgKw5FLffbI/iLubBEs55W5ApuYpBVuXKGoZp9SRQ==} + '@angular/compiler-cli@21.2.6': + resolution: {integrity: sha512-CiPmat4+D+hWXMTAY++09WeII/5D0r6iTjdLdaTq8tlo0uJcrOlazib4CpA94kJ2CRdzfhmC1H+ttwBI1xIlTg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.2 + '@angular/compiler': 21.2.6 typescript: '>=5.9 <6.1' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@20.3.18': - resolution: {integrity: sha512-AaP/LCiDNcYmF135EEozjyR04NRBT38ZfBHQwjhgwiBBTejmvcpHwJaHSkraLpZqZzE4BQqqmgiQ1EJqxEwLVA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - - '@angular/compiler@21.2.4': - resolution: {integrity: sha512-9+ulVK3idIo/Tu4X2ic7/V0+Uj7pqrOAbOuIirYe6Ymm3AjexuFRiGBbfcH0VJhQ5cf8TvIJ1fuh+MI4JiRIxA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - - '@angular/core@20.3.18': - resolution: {integrity: sha512-B+NQQngd/aDbcfW0zGLis3wTLDeHTeTYMl/mGKQH+HwdPaRCKI1wEtaXaOYVJXkP2FeThocPevB8gLwNlPQUUw==} + '@angular/compiler@21.2.6': + resolution: {integrity: sha512-shGkb/aAIPbG8oSYkVJ0msGlRdDVcJBVaUVx2KenMltifQjfLn5N8DFMAzOR6haaA3XeugFExxKqmvySjrVq+A==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - peerDependencies: - '@angular/compiler': 20.3.18 - rxjs: ^6.5.3 || ^7.4.0 - zone.js: ~0.15.0 - peerDependenciesMeta: - '@angular/compiler': - optional: true - zone.js: - optional: true - '@angular/core@21.2.4': - resolution: {integrity: sha512-2+gd67ZuXHpGOqeb2o7XZPueEWEP81eJza2tSHkT5QMV8lnYllDEmaNnkPxnIjSLGP1O3PmiXxo4z8ibHkLZwg==} + '@angular/core@21.2.6': + resolution: {integrity: sha512-svgK5DhFlQlS+sMybXftn08rHHRiDGY/uIKT5LZUaKgyffnkPb8uClpMIW0NzANtU8qs8pwgDZFoJw85Ia3oqQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/compiler': 21.2.4 + '@angular/compiler': 21.2.6 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: @@ -609,81 +574,52 @@ packages: zone.js: optional: true - '@angular/forms@20.3.18': - resolution: {integrity: sha512-x6/99LfxolyZIFUL3Wr0OrtuXHEDwEz/rwx+WzE7NL+n35yO40t3kp0Sn5uMFwI94i91QZJmXHltMpZhrVLuYg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - peerDependencies: - '@angular/common': 20.3.18 - '@angular/core': 20.3.18 - '@angular/platform-browser': 20.3.18 - rxjs: ^6.5.3 || ^7.4.0 - - '@angular/forms@21.2.2': - resolution: {integrity: sha512-uiuL8uy4OpcbB0zRZ8TcvYXxe+GZC+XkCAptfrG+yAyzm4cOrqCilOZsMG6bVdOb2sjc2islk9CVDZZOju28/Q==} + '@angular/forms@21.2.6': + resolution: {integrity: sha512-i8BoWxBAm0g2xOMcQ8wTdj07gqMPIFYIyefCOo0ezcGj5XhYjd+C2UrYnKsup0aMZqqEAO1l2aZbmfHx9xLheQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.2.2 - '@angular/core': 21.2.2 - '@angular/platform-browser': 21.2.2 + '@angular/common': 21.2.6 + '@angular/core': 21.2.6 + '@angular/platform-browser': 21.2.6 rxjs: ^6.5.3 || ^7.4.0 - '@angular/material@20.2.14': - resolution: {integrity: sha512-IbAgV6XLsvmHiJzxycVhcNC1PA4M30qi+ERCOir6cT333Bxm8vDV32gsOjfL52uzG5YRARroPC+8s1XqR2oxeA==} + '@angular/material@21.2.4': + resolution: {integrity: sha512-YzkPjgZezdsDeAhSm3zix2h+ohApwaRUMG8ea/75XR1eSkT1n3N7qZaHC8HDkhPYApk8a951RDxsTiiAidnGqg==} peerDependencies: - '@angular/cdk': 20.2.14 - '@angular/common': ^20.0.0 || ^21.0.0 - '@angular/core': ^20.0.0 || ^21.0.0 - '@angular/forms': ^20.0.0 || ^21.0.0 - '@angular/platform-browser': ^20.0.0 || ^21.0.0 + '@angular/cdk': 21.2.4 + '@angular/common': ^21.0.0 || ^22.0.0 + '@angular/core': ^21.0.0 || ^22.0.0 + '@angular/forms': ^21.0.0 || ^22.0.0 + '@angular/platform-browser': ^21.0.0 || ^22.0.0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/platform-browser-dynamic@20.3.18': - resolution: {integrity: sha512-NyTobOGYVzGmPmtI+3lxMzxi0TbLq4SRNQ2ENEJAt6k2JnMmHBm483ppLRAM47nGlDdiraW0IX93EtYYNkiK3g==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - peerDependencies: - '@angular/common': 20.3.18 - '@angular/compiler': 20.3.18 - '@angular/core': 20.3.18 - '@angular/platform-browser': 20.3.18 - - '@angular/platform-browser@20.3.18': - resolution: {integrity: sha512-q6s5rEN1yYazpHYp+k4pboXRzMsRB9auzTRBEhyXSGYxqzrnn3qHN0DqgsLC9WAdyhCgnIEMFA8kRT+W277DqQ==} + '@angular/platform-browser-dynamic@21.2.6': + resolution: {integrity: sha512-6a+zA9jM70b1kH3fSfAJIEVmkE3qB3oIXw7otWkv1nEhOJtNO0mM0dTUuO70C3GhnV9tmpLXa2him56C2LhVig==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/animations': 20.3.18 - '@angular/common': 20.3.18 - '@angular/core': 20.3.18 - peerDependenciesMeta: - '@angular/animations': - optional: true + '@angular/common': 21.2.6 + '@angular/compiler': 21.2.6 + '@angular/core': 21.2.6 + '@angular/platform-browser': 21.2.6 - '@angular/platform-browser@21.2.2': - resolution: {integrity: sha512-6cHfHi/lRCUPNGO0eJeYRIpu8vM+CMMS2Wv/psOUwvl/5+RC92hfBEZxzQiF/5X9A170bJabaMFQC5fA7pkF2g==} + '@angular/platform-browser@21.2.6': + resolution: {integrity: sha512-LW1vPXVHvy71LBahn+fSzPlWQl25kJIdcXq+ptG7HsMVgbPQ3/vvkKXAHYaRdppLGCFL+v+3dQGHYLNLiYL9qg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/animations': 21.2.2 - '@angular/common': 21.2.2 - '@angular/core': 21.2.2 + '@angular/animations': 21.2.6 + '@angular/common': 21.2.6 + '@angular/core': 21.2.6 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/router@20.3.18': - resolution: {integrity: sha512-3CWejsEYr+ze+ktvWN/qHdyq5WLrj96QZpGYJyxh1pchIcpMPE9MmLpdjf0CUrWYB7g/85u0Geq/xsz72JrGng==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - peerDependencies: - '@angular/common': 20.3.18 - '@angular/core': 20.3.18 - '@angular/platform-browser': 20.3.18 - rxjs: ^6.5.3 || ^7.4.0 - - '@angular/router@21.2.2': - resolution: {integrity: sha512-mpVPI6AiIlZ4z6sSM5WOzCMwTka9yQ9TEVj9+ZyfAs5U9RnKDa8DZIoQ6BFbh3h/v4DArvT5q1Ji8aIlabmWcQ==} + '@angular/router@21.2.6': + resolution: {integrity: sha512-0ajhkKYeOqHQEEH88+Q0HrheR3helwTvdTqD/0gTaapCe+HOoC+SYwmzzsYP2zwAxBNQEg4JHOGKQ30X9/gwgw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.2.2 - '@angular/core': 21.2.2 - '@angular/platform-browser': 21.2.2 + '@angular/common': 21.2.6 + '@angular/core': 21.2.6 + '@angular/platform-browser': 21.2.6 rxjs: ^6.5.3 || ^7.4.0 '@babel/code-frame@7.29.0': @@ -740,12 +676,12 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.6': - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.0': - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} engines: {node: '>=6.0.0'} hasBin: true @@ -1186,8 +1122,8 @@ packages: cpu: [x64] os: [win32] - '@napi-rs/cli@3.5.1': - resolution: {integrity: sha512-XBfLQRDcB3qhu6bazdMJsecWW55kR85l5/k0af9BIBELXQSsCFU0fzug7PX8eQp6vVdm7W/U3z6uP5WmITB2Gw==} + '@napi-rs/cli@3.6.0': + resolution: {integrity: sha512-aA8m4+9XxnK1+0sr4GplZP0Ze90gkzO8sMKaplOK0zXbLnzsLl6O2BQQt6rTCcTRzIN24wrrByakr/imM+CxhA==} engines: {node: '>= 16'} hasBin: true peerDependencies: @@ -1561,8 +1497,11 @@ packages: resolution: {integrity: sha512-7cmzIu+Vbupriudo7UudoMRH2OA3cTw67vva8MxeoAe5S7vPFI7z0vp0pMXiA25S8IUJefImQ90FeJjl8fjEaQ==} engines: {node: '>= 10'} - '@napi-rs/wasm-runtime@1.1.1': - resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + '@napi-rs/wasm-runtime@1.1.2': + resolution: {integrity: sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 '@napi-rs/wasm-tools-android-arm-eabi@1.0.1': resolution: {integrity: sha512-lr07E/l571Gft5v4aA1dI8koJEmF1F0UigBbsqg9OWNzg80H3lDPO+auv85y3T/NHE3GirDk7x/D3sLO57vayw==} @@ -1649,6 +1588,13 @@ packages: resolution: {integrity: sha512-enkZYyuCdo+9jneCPE/0fjIta4wWnvVN9hBo2HuiMpRF0q3lzv1J6b/cl7i0mxZUKhBrV3aCKDBQnCOhwKbPmQ==} engines: {node: '>= 10'} + '@noble/ed25519@3.0.1': + resolution: {integrity: sha512-t/T8LuK0ym8ALQudCCQCtrRdMSxBnRgHXw+wg+YsSlE6d+on7sX3flqlSJ2mOs9xEuchM36kj9SuX5MG7pXQMA==} + + '@noble/hashes@2.0.1': + resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} + engines: {node: '>= 20.19.0'} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1713,6 +1659,78 @@ packages: '@octokit/types@16.0.0': resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} + '@opentelemetry/api-logs@0.208.0': + resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/core@2.2.0': + resolution: {integrity: sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.6.0': + resolution: {integrity: sha512-HLM1v2cbZ4TgYN6KEOj+Bbj8rAKriOdkF9Ed3tG25FoprSiQl7kYc+RRT6fUZGOvx0oMi5U67GoFdT+XUn8zEg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-logs-otlp-http@0.208.0': + resolution: {integrity: sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.208.0': + resolution: {integrity: sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-transformer@0.208.0': + resolution: {integrity: sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/resources@2.2.0': + resolution: {integrity: sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/resources@2.6.0': + resolution: {integrity: sha512-D4y/+OGe3JSuYUCBxtH5T9DSAWNcvCb/nQWIga8HNtXTVPQn59j0nTBAgaAXxUVBDl40mG3Tc76b46wPlZaiJQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.208.0': + resolution: {integrity: sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + + '@opentelemetry/sdk-metrics@2.2.0': + resolution: {integrity: sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.2.0': + resolution: {integrity: sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.40.0': + resolution: {integrity: sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==} + engines: {node: '>=14'} + '@oxc-node/cli@0.0.35': resolution: {integrity: sha512-FTsYtymv6E4tyV2kxQEFLjsi6ZLmtsBpirFnbv5l7JGmQj65TCGM/WJlkZW+mPAM/9QN+ZY6cIOpnTTl8Qc7uA==} hasBin: true @@ -1811,543 +1829,539 @@ packages: '@oxc-node/core@0.0.35': resolution: {integrity: sha512-PV46QRDI2wCDdaPzppEh4UfzFmmpSt+1dX32ooq8RWb0BuWX24+LKYicAmSrsk1ls8JRSkAqiWrjrYFHIGozGg==} - '@oxc-parser/binding-android-arm-eabi@0.120.0': - resolution: {integrity: sha512-WU3qtINx802wOl8RxAF1v0VvmC2O4D9M8Sv486nLeQ7iPHVmncYZrtBhB4SYyX+XZxj2PNnCcN+PW21jHgiOxg==} + '@oxc-parser/binding-android-arm-eabi@0.123.0': + resolution: {integrity: sha512-EHQ58z+6DbZWokMOKg5AB1KuwrXVgfbBLuuLFfzdc7bI5A4igvdvjKMhUv1VBV+0FABiUCOjNKUmMF7ugprwbQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm64@0.120.0': - resolution: {integrity: sha512-SEf80EHdhlbjZEgzeWm0ZA/br4GKMenDW3QB/gtyeTV1gStvvZeFi40ioHDZvds2m4Z9J1bUAUL8yn1/+A6iGg==} + '@oxc-parser/binding-android-arm64@0.123.0': + resolution: {integrity: sha512-BK1E0zqNoHf38nTHjnGZ+olKHSKNHh65pChjY06yhaWYP8X7yNDqhQDA4neMPRqnPBgpN4/OW1oSMrdJgDi2aw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-parser/binding-darwin-arm64@0.120.0': - resolution: {integrity: sha512-xVrrbCai8R8CUIBu3CjryutQnEYhZqs1maIqDvtUCFZb8vY33H7uh9mHpL3a0JBIKoBUKjPH8+rzyAeXnS2d6A==} + '@oxc-parser/binding-darwin-arm64@0.123.0': + resolution: {integrity: sha512-dkMPbtTbqU+cm+k4YGOBs4zAuq3Xu+wqjbGQvLAuVO7qHhNY4p5LBNudOmOoi0jxS8h1W6Jmlzv8MAKGpK+iDg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.120.0': - resolution: {integrity: sha512-xyHBbnJ6mydnQUH7MAcafOkkrNzQC6T+LXgDH/3InEq2BWl/g424IMRiJVSpVqGjB+p2bd0h0WRR8iIwzjU7rw==} + '@oxc-parser/binding-darwin-x64@0.123.0': + resolution: {integrity: sha512-85pic0rCd59DGdM69jI9xE/Snb2KtrfiU48QigjJXjzxUOenGvH4SAFIjFpO/2ZnI3Kz50D8pht4jKN3t2022Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.120.0': - resolution: {integrity: sha512-UMnVRllquXUYTeNfFKmxTTEdZ/ix1nLl0ducDzMSREoWYGVIHnOOxoKMWlCOvRr9Wk/HZqo2rh1jeumbPGPV9A==} + '@oxc-parser/binding-freebsd-x64@0.123.0': + resolution: {integrity: sha512-mjEiW6z7JtaiHMK/8aJic1lfjkKpzFwK2XFNmm187BFbtDamjGVuKNr2TEyrFEYJyZc217wokR1wrYeZGBQo4Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': - resolution: {integrity: sha512-tkvn2CQ7QdcsMnpfiX3fd3wA3EFsWKYlcQzq9cFw/xc89Al7W6Y4O0FgLVkVQpo0Tnq/qtE1XfkJOnRRA9S/NA==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.123.0': + resolution: {integrity: sha512-mYxigPtGt6SZfhNZBIJfuDM92cLo8XUW08WuKxzHvcmWu6xndLqwLp99Vg4uHke1AXicQEHU3Wri2X9bHF0Vlw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': - resolution: {integrity: sha512-WN5y135Ic42gQDk9grbwY9++fDhqf8knN6fnP+0WALlAUh4odY/BDK1nfTJRSfpJD9P3r1BwU0m3pW2DU89whQ==} + '@oxc-parser/binding-linux-arm-musleabihf@0.123.0': + resolution: {integrity: sha512-ttWirDC9eUBn0R4Tzz3aeDaLrx9drPdNiLJ8MXeDBFxd6cwLfTIC27qjsdfGpn942tkVIZY3sjWAnvbwDDjX7g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.120.0': - resolution: {integrity: sha512-1GgQBCcXvFMw99EPdMy+4NZ3aYyXsxjf9kbUUg8HuAy3ZBXzOry5KfFEzT9nqmgZI1cuetvApkiJBZLAPo8uaw==} + '@oxc-parser/binding-linux-arm64-gnu@0.123.0': + resolution: {integrity: sha512-apAHyoMNRYT+2G98Y14caZmsr5LD9PsWpGI7nXmSwK26LGiQneCU6HvHQ+d+AX+RJ5TTWZtEb2RD7OLqAC0cYQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-musl@0.120.0': - resolution: {integrity: sha512-gmMQ70gsPdDBgpcErvJEoWNBr7bJooSLlvOBVBSGfOzlP5NvJ3bFvnUeZZ9d+dPrqSngtonf7nyzWUTUj/U+lw==} + '@oxc-parser/binding-linux-arm64-musl@0.123.0': + resolution: {integrity: sha512-3r99Qa4egjO/iXUBxTlN6Ddt1YkLifG6olzvj8gkoKEK2U/MOW7mQfXRyBmuoMgmZ7O4vk41gO3d21c6VcN3yQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': - resolution: {integrity: sha512-T/kZuU0ajop0xhzVMwH5r3srC9Nqup5HaIo+3uFjIN5uPxa0LvSxC1ZqP4aQGJVW5G0z8/nCkjIfSMS91P/wzw==} + '@oxc-parser/binding-linux-ppc64-gnu@0.123.0': + resolution: {integrity: sha512-Hr/Z24kUE4pjJs346g80WDwjyJGrxiw6hExJuOiME/76ZFz68y5L11UzprRkW9FN4HxBB7tLZ/fytczV2fEsiA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': - resolution: {integrity: sha512-vn21KXLAXzaI3N5CZWlBr1iWeXLl9QFIMor7S1hUjUGTeUuWCoE6JZB040/ZNDwf+JXPX8Ao9KbmJq9FMC2iGw==} + '@oxc-parser/binding-linux-riscv64-gnu@0.123.0': + resolution: {integrity: sha512-sxjbhs+8WXeuoLnZ2rBmQ96gPdq3SCmz24reIltsKLUt1EDMgdaQsr7RqwBphw3QAImkMtlPQfAWDWwZyo0xDg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-musl@0.120.0': - resolution: {integrity: sha512-SUbUxlar007LTGmSLGIC5x/WJvwhdX+PwNzFJ9f/nOzZOrCFbOT4ikt7pJIRg1tXVsEfzk5mWpGO1NFiSs4PIw==} + '@oxc-parser/binding-linux-riscv64-musl@0.123.0': + resolution: {integrity: sha512-d6xHHhqldA/W+VC7v8uHs24zM69Ad3HnHQ45h+uuBhCsbZx3d0E0wL2K3uJ5mYKTR6UPMFk9VMXcHWwvg1PRZQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-s390x-gnu@0.120.0': - resolution: {integrity: sha512-hYiPJTxyfJY2+lMBFk3p2bo0R9GN+TtpPFlRqVchL1qvLG+pznstramHNvJlw9AjaoRUHwp9IKR7UZQnRPGjgQ==} + '@oxc-parser/binding-linux-s390x-gnu@0.123.0': + resolution: {integrity: sha512-+di9A5wJQlv0VodyhADjJ2rC4geyHY+uhJDl3TFjMgYhhlgLZchi9uHD5mfiUEDWHt1x7/eU2u1ge3LLazZmFw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.120.0': - resolution: {integrity: sha512-q+5jSVZkprJCIy3dzJpApat0InJaoxQLsJuD6DkX8hrUS61z2lHQ1Fe9L2+TYbKHXCLWbL0zXe7ovkIdopBGMQ==} + '@oxc-parser/binding-linux-x64-gnu@0.123.0': + resolution: {integrity: sha512-sh7pw2g/u6LE1TaRRQsV9Kv9+1y+CywaaNwWWP+3bnEPk/L692oTG0hmEviUlawI8v3OGC+AhbjtAD+HXWQAkg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-musl@0.120.0': - resolution: {integrity: sha512-D9QDDZNnH24e7X4ftSa6ar/2hCavETfW3uk0zgcMIrZNy459O5deTbWrjGzZiVrSWigGtlQwzs2McBP0QsfV1w==} + '@oxc-parser/binding-linux-x64-musl@0.123.0': + resolution: {integrity: sha512-S+LoD8PiJ639JwIqK1knIeqAyYkeCbLHtAgfapszKX0yVCaYP+aer8dJxL25de9qcDjvYWVrYCkuDZzHmOl2Xw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxc-parser/binding-openharmony-arm64@0.120.0': - resolution: {integrity: sha512-TBU8ZwOUWAOUWVfmI16CYWbvh4uQb9zHnGBHsw5Cp2JUVG044OIY1CSHODLifqzQIMTXvDvLzcL89GGdUIqNrA==} + '@oxc-parser/binding-openharmony-arm64@0.123.0': + resolution: {integrity: sha512-/65vryK11q1I+k+7ukDlwZOxUFCLYsoZBZPGZHyet5bIP5e3D8mV3uCuvpWZ9Hoe6vUZFw/nAfCrX59MeuJPgw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-wasm32-wasi@0.120.0': - resolution: {integrity: sha512-WG/FOZgDJCpJnuF3ToG/K28rcOmSY7FmFmfBKYb2fmLyhDzPpUldFGV7/Fz4ru0Iz/v4KPmf8xVgO8N3lO4KHA==} + '@oxc-parser/binding-wasm32-wasi@0.123.0': + resolution: {integrity: sha512-y4OsMGQiAbZzj2Rq0LEfvhR48rQDvbvqsl/dPdn4tdf+z3H79nZuR+lQ/+KUGjD30vpVGem138sBWHFj9UR+Vg==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@oxc-parser/binding-win32-arm64-msvc@0.120.0': - resolution: {integrity: sha512-1T0HKGcsz/BKo77t7+89L8Qvu4f9DoleKWHp3C5sJEcbCjDOLx3m9m722bWZTY+hANlUEs+yjlK+lBFsA+vrVQ==} + '@oxc-parser/binding-win32-arm64-msvc@0.123.0': + resolution: {integrity: sha512-9lBqI6AXAkjYavkdpizNU3Q51uoVYfp9FJPx19hnCEdPku1jSgzSnvgmCvhCue0GziIvIvIdWgZ41wXQ3EOoBw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.120.0': - resolution: {integrity: sha512-L7vfLzbOXsjBXV0rv/6Y3Jd9BRjPeCivINZAqrSyAOZN3moCopDN+Psq9ZrGNZtJzP8946MtlRFZ0Als0wBCOw==} + '@oxc-parser/binding-win32-ia32-msvc@0.123.0': + resolution: {integrity: sha512-zJbqBHwSUB7CyvAONy9ewGtQwcQj+ylOhYGETvUPp3KIYx7lolj4Gayof7iA22SU5eMSjO5COL0c8wYhmn9agA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.120.0': - resolution: {integrity: sha512-ys+upfqNtSu58huAhJMBKl3XCkGzyVFBlMlGPzHeFKgpFF/OdgNs1MMf8oaJIbgMH8ZxgGF7qfue39eJohmKIg==} + '@oxc-parser/binding-win32-x64-msvc@0.123.0': + resolution: {integrity: sha512-q7RZvglQvGo3RX5ljtcGSabu2B2c0oDU/6xC3sBMhsV5KRo0PvyxLdordbEN31NTfuZu4Sgl86C76cAURZIHWA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxc-project/runtime@0.115.0': - resolution: {integrity: sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==} - engines: {node: ^20.19.0 || >=22.12.0} - '@oxc-project/types@0.113.0': resolution: {integrity: sha512-Tp3XmgxwNQ9pEN9vxgJBAqdRamHibi76iowQ38O2I4PMpcvNRQNVsU2n1x1nv9yh0XoTrGFzf7cZSGxmixxrhA==} - '@oxc-project/types@0.115.0': - resolution: {integrity: sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==} + '@oxc-project/types@0.122.0': + resolution: {integrity: sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==} - '@oxc-project/types@0.120.0': - resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==} + '@oxc-project/types@0.123.0': + resolution: {integrity: sha512-YtECP/y8Mj1lSHiUWGSRzy/C6teUKlS87dEfuVKT09LgQbUsBW1rNg+MiJ4buGu3yuADV60gbIvo9/HplA56Ew==} - '@oxc-transform/binding-android-arm-eabi@0.120.0': - resolution: {integrity: sha512-NRSGsDmnVYWMnYq4LlxakKbZUFhV+A9cVIwQu/Iy/eZcADxT3eSRJ8ItLbAryMjuuWJiCQFdlGNMFzuMtHogzw==} + '@oxc-transform/binding-android-arm-eabi@0.123.0': + resolution: {integrity: sha512-glB9LSiKsRmhb8yuBcbaCByx+JQ/KbAZe9U5+iUuLuLaRr7llg/saPybaDiiEaz3IcVxnodKgsA4IxUnPV3+fw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-transform/binding-android-arm64@0.120.0': - resolution: {integrity: sha512-CFKMV9r5kejf5rzl9ETfxIbIGv8N6UoMuh7QfAIHmbbJVcYD3vkjkT5dC5rK5Af07vIQ8573/UcXy77o9EwhPA==} + '@oxc-transform/binding-android-arm64@0.123.0': + resolution: {integrity: sha512-qge60UoJalkq8ftU9vHyq5Xu+kDtPF8sSSqQavzNWURGFLtXKyuxSl+7ovTurvUAwgVkaHcvEZsXWip71tKlqg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-transform/binding-darwin-arm64@0.120.0': - resolution: {integrity: sha512-hR/hV18iN4yVD8OKI6LwY4OiLUgCQzyDfCrsYot+h0is+mtWXHImQz6w3rToBKAHBot7bChUvgywj4rm+kjtMQ==} + '@oxc-transform/binding-darwin-arm64@0.123.0': + resolution: {integrity: sha512-uJv6bgXTwVlJvmYmGjv/IeAPUn5MTUeU8Uf+nLEUpPW0QDP0g7ttZWEI+OjY9seHO0DQ5dNi0+wzcTCk+UmoJA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-transform/binding-darwin-x64@0.120.0': - resolution: {integrity: sha512-Ggzo6wiF4p4ukas741seePN/MQhgRHGSke0R24dKX0P75+heibQZWJYsYiJ5XYxdwoRXcH5BnDxKVzK6RWRDaw==} + '@oxc-transform/binding-darwin-x64@0.123.0': + resolution: {integrity: sha512-Bkm2zhQ10D9xI/ZyMErQi3GOWouYMR8SqI+yvBggLz/EE1moo0Hpm0qQTJYwpFsi+uO64tAu1asaNKxCmVpaFw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-transform/binding-freebsd-x64@0.120.0': - resolution: {integrity: sha512-XJBt0dqcIrz4gtjk1PxUjU78x5GJY8nXJR44g4+1Mxm6r+FvaZRfOe8lWt2uQAvVq8kvjIZ+Gj+jY1rL/aMMQg==} + '@oxc-transform/binding-freebsd-x64@0.123.0': + resolution: {integrity: sha512-pEbYQN2OPHL6khErdZ6Q0XI+VriAz1TZglm9euj4qX2a30PobOyzebaHEZMpmiPTS82rB6kPcSjsBbjY4bXVaA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-transform/binding-linux-arm-gnueabihf@0.120.0': - resolution: {integrity: sha512-NdMHmPQihRqEbBPmTtj9BC6KRUfsq/S53gBd3tOtByrX1Bdcvac74QRyTRZPxqLYSA8fP+5bEIY56qjwVZW66A==} + '@oxc-transform/binding-linux-arm-gnueabihf@0.123.0': + resolution: {integrity: sha512-oEZS8HsrtHON4ph/a35ILBH4Nra5Y0uP3CsGOc7SUSftEt8GZ+Xr3lXj67ZXsEZiZbsw3cUte23YhUf09nfaFQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-transform/binding-linux-arm-musleabihf@0.120.0': - resolution: {integrity: sha512-0J9FCV9pSg/9pWGv6tg+XTiLeYVnUio2Kv0Yi+40Q3dUspFhY4fmlXWjswcZ5DMZqDN/ScXs/8ToI9qgc5EJjg==} + '@oxc-transform/binding-linux-arm-musleabihf@0.123.0': + resolution: {integrity: sha512-869HBeT1tXl6GsmxZJTET7Lbx6hW8XoM8pw6PyTQ82GjodFSlk6if4rWifYNrPOsgMw1/q4mwYJcX850eFPJow==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-transform/binding-linux-arm64-gnu@0.120.0': - resolution: {integrity: sha512-KqiqhLCfgqNMNvrPzPq6PQMHSEnqi1bggNVCc0LCzTstyqCJJkdb4apkzscStsMwXqIXitjilBnPwIf/ZPAPAQ==} + '@oxc-transform/binding-linux-arm64-gnu@0.123.0': + resolution: {integrity: sha512-4OUNnatZNNvMwnylsfr+IeaCByBKiXPk4wQFMUf0xS8cUnOdjOtb6qMQ94nWuPA9d+Ywu32qfY+N4Fdaf3sNRA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-transform/binding-linux-arm64-musl@0.120.0': - resolution: {integrity: sha512-VMSt+qfZZkjaE9ylneBJO/+ZH/2o+HjU4/tE21Frtk6aZguF5DbtFNQ98fu7UBPOxktOETjTFVWn32LtcEX2yw==} + '@oxc-transform/binding-linux-arm64-musl@0.123.0': + resolution: {integrity: sha512-C54h8AoUpwzw3+Ge+Vv2YYuuVh7XwVB5Mi/KiwByPPS/WFoJpmkSPvtFeAazdYbo4iKEGLrRI8vt+gEib1lDMw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxc-transform/binding-linux-ppc64-gnu@0.120.0': - resolution: {integrity: sha512-Zgd7XdX89w9HaDzd1+AWcpwa1Jk45ZO0OU+iHs/1ZCkCtFLLlKMgBKa7pimKLmC9GFrjxcWatOBL1CThVaX2pQ==} + '@oxc-transform/binding-linux-ppc64-gnu@0.123.0': + resolution: {integrity: sha512-X+obOgFjX/61UZ1Wm5ncNZYC43R3bV9eU7DdCAEO6VXubSXcwIjxaf3QrUvBDYPifrdWSy/OerzJbhI9TgHYPg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-transform/binding-linux-riscv64-gnu@0.120.0': - resolution: {integrity: sha512-WRM5QKnrcgriGZZScesBydkTPGY5fsbfZLvMRPzGsX7DJfB0KhmPK/jszzRgGWPZe+oh/89v3jlziljeiM5iDQ==} + '@oxc-transform/binding-linux-riscv64-gnu@0.123.0': + resolution: {integrity: sha512-EzerdFa2KvEzYHuzFp9W/KZaulI4OIKE8FIC0X21V757ljZKRfskIqtGAFX/CAvoIF3C2zNepDWFZlpcJ5nJ1g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-transform/binding-linux-riscv64-musl@0.120.0': - resolution: {integrity: sha512-j8N+aWybDqNTYnTJMLd4eJaGxrbt/9LfzpjkNJtkLWleCaOEXTx2xlJecqepOB1UyIGfs0jm2Ej085Y7iN4rbw==} + '@oxc-transform/binding-linux-riscv64-musl@0.123.0': + resolution: {integrity: sha512-WFh7tcPYqxo2YQXnIuQ/ZZ4uFHeR06tDEFD8qNl1egRrqTZskHvV/NBelOthfHmkizWiGJx8ZnvN69UrL3q12A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-transform/binding-linux-s390x-gnu@0.120.0': - resolution: {integrity: sha512-Xab3JZsLvumRAMEutpSBWa3VoV+lAtArwUbh9BZsMhmuaizKsOKBW32cw1tjWlaaCNegrQpw4EZ+muCLKiVdJA==} + '@oxc-transform/binding-linux-s390x-gnu@0.123.0': + resolution: {integrity: sha512-A5ahPjG/2Zg5/3RndWRSaKO/9uIirjYuv8OBWa+HBA1Im607dCceSfc9k1PCHt0MdjtsfiyArO+kk2TP5R0Ebg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-transform/binding-linux-x64-gnu@0.120.0': - resolution: {integrity: sha512-ewRGO9hsl3AQ4FvbollEDzoAEGecyECnX75k9k89iBXYxX/4FQgBjUne4M4on4CBiBd8iplGSh8fT0+ZaJJkFg==} + '@oxc-transform/binding-linux-x64-gnu@0.123.0': + resolution: {integrity: sha512-9g1rEynmIh0qkJWc/1Zbd1VRFzYkk6KcmwcCoq3hslgGBIJEvrjPlP7cQgAiCaTFCVjfoPYWAy+5xjf9sCNY1g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxc-transform/binding-linux-x64-musl@0.120.0': - resolution: {integrity: sha512-Y8RASUrQzLK/iHUp7UoqqMEmyZbGICHA6soBgQ84DxoTQH7F2wf+Vjv41PQsEYEpBehIAwqKBBs9ag7QVVxA7w==} + '@oxc-transform/binding-linux-x64-musl@0.123.0': + resolution: {integrity: sha512-/eoNDuGDfEjNVqmgDNkFlGmoo5MOnRxaO9IhwKyWoXXUn7tzA5C45op7Kv/Njb6BcGr4RN2KH7OjsEAqjMDmuA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxc-transform/binding-openharmony-arm64@0.120.0': - resolution: {integrity: sha512-P+LfDVoePi7FtMzN3NwW+qjL5DDxKMAoyyCkxXdjtGbaDyGPHMWlKmij/ITC4KTqo5KwlQp8trD0EaOpf1EcKg==} + '@oxc-transform/binding-openharmony-arm64@0.123.0': + resolution: {integrity: sha512-rGeHHsE/KZ7G/iEtSsQAk4HZ3Wl2v4oMgcOjSvlVJejl+5ttUcKAjxgW2j+c1zFREJVpCHEyspi3fFxJkdJ/Ww==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-transform/binding-wasm32-wasi@0.120.0': - resolution: {integrity: sha512-NCRVNsVvEKzQ+jTsyklYTywJT/MRnxib7GfqmyEAwYikMeEXAiytvrccW5/ztAB1Hj2JpulPtWr47ZlXgTgXnA==} + '@oxc-transform/binding-wasm32-wasi@0.123.0': + resolution: {integrity: sha512-JMFXeeWvFDioW2gP9dgD6LDbPmCMCrXNwDWMAXcKDmZILx0rARt+z79GACKTBSyyKURkYGe3+wJj+oI3JKnvug==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@oxc-transform/binding-win32-arm64-msvc@0.120.0': - resolution: {integrity: sha512-Yn56/HKBMStMvaiOc4Oc4O0NH+SLvZy3y0XfXjHlSgmx1UPf6xrip8BDLrDUphs4uQKDId8s3o2vmCzhYWEplg==} + '@oxc-transform/binding-win32-arm64-msvc@0.123.0': + resolution: {integrity: sha512-hSQ7VokhPAO0NMrsRz+2Zh4fcxx28qFlX78/7jG/+tWZgiB/aEukadf3XPcYQ3ymqoL8SvDN3nQ7bNTHAiZSCg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-transform/binding-win32-ia32-msvc@0.120.0': - resolution: {integrity: sha512-tj+Bs8EH2nUPbhNnoTjM0KTIWKlsbfEe7+VQjNlBSSeArS3A8ksW2BhHf6S3WYgEsmSBSeOoRNyDoUrrxmrs/A==} + '@oxc-transform/binding-win32-ia32-msvc@0.123.0': + resolution: {integrity: sha512-7bIydPGt68qdfPXYLzBHYUia8Wi0dP6g/8zmrXN1HfUugkdt4Kh121fmw4KH+kcIT1BTICDHcXU4bJ6k3QmSgg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-transform/binding-win32-x64-msvc@0.120.0': - resolution: {integrity: sha512-rX6VYwSwjENgteKnnzb36Cpa06TtJ0EdDm2pVdi8HOJgz40O++lTUbRtQz/93w07MnItGNTwxB4hgBGCaqizuQ==} + '@oxc-transform/binding-win32-x64-msvc@0.123.0': + resolution: {integrity: sha512-pVmtG6GGI3eZ5J4NP75rR5yA1EuwEq+94kBQipq1rZXsukj6ghNkd3OGgzOA+W/fQL3V9AlnY9BriqCT21eVQw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxfmt/binding-android-arm-eabi@0.40.0': - resolution: {integrity: sha512-S6zd5r1w/HmqR8t0CTnGjFTBLDq2QKORPwriCHxo4xFNuhmOTABGjPaNvCJJVnrKBLsohOeiDX3YqQfJPF+FXw==} + '@oxfmt/binding-android-arm-eabi@0.43.0': + resolution: {integrity: sha512-CgU2s+/9hHZgo0IxVxrbMPrMj+tJ6VM3mD7Mr/4oiz4FNTISLoCvRmB5nk4wAAle045RtRjd86m673jwPyb1OQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.40.0': - resolution: {integrity: sha512-/mbS9UUP/5Vbl2D6osIdcYiP0oie63LKMoTyGj5hyMCK/SFkl3EhtyRAfdjPvuvHC0SXdW6ePaTKkBSq1SNcIw==} + '@oxfmt/binding-android-arm64@0.43.0': + resolution: {integrity: sha512-T9OfRwjA/EdYxAqbvR7TtqLv5nIrwPXuCtTwOHtS7aR9uXyn74ZYgzgTo6/ZwvTq9DY4W+DsV09hB2EXgn9EbA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.40.0': - resolution: {integrity: sha512-wRt8fRdfLiEhnRMBonlIbKrJWixoEmn6KCjKE9PElnrSDSXETGZfPb8ee+nQNTobXkCVvVLytp2o0obAsxl78Q==} + '@oxfmt/binding-darwin-arm64@0.43.0': + resolution: {integrity: sha512-o3i49ZUSJWANzXMAAVY1wnqb65hn4JVzwlRQ5qfcwhRzIA8lGVaud31Q3by5ALHPrksp5QEaKCQF9aAS3TXpZA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.40.0': - resolution: {integrity: sha512-fzowhqbOE/NRy+AE5ob0+Y4X243WbWzDb00W+pKwD7d9tOqsAFbtWUwIyqqCoCLxj791m2xXIEeLH/3uz7zCCg==} + '@oxfmt/binding-darwin-x64@0.43.0': + resolution: {integrity: sha512-vWECzzCFkb0kK6jaHjbtC5sC3adiNWtqawFCxhpvsWlzVeKmv5bNvkB4nux+o4JKWTpHCM57NDK/MeXt44txmA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.40.0': - resolution: {integrity: sha512-agZ9ITaqdBjcerRRFEHB8s0OyVcQW8F9ZxsszjxzeSthQ4fcN2MuOtQFWec1ed8/lDa50jSLHVE2/xPmTgtCfQ==} + '@oxfmt/binding-freebsd-x64@0.43.0': + resolution: {integrity: sha512-rgz8JpkKiI/umOf7fl9gwKyQasC8bs5SYHy6g7e4SunfLBY3+8ATcD5caIg8KLGEtKFm5ujKaH8EfjcmnhzTLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.40.0': - resolution: {integrity: sha512-ZM2oQ47p28TP1DVIp7HL1QoMUgqlBFHey0ksHct7tMXoU5BqjNvPWw7888azzMt25lnyPODVuye1wvNbvVUFOA==} + '@oxfmt/binding-linux-arm-gnueabihf@0.43.0': + resolution: {integrity: sha512-nWYnF3vIFzT4OM1qL/HSf1Yuj96aBuKWSaObXHSWliwAk2rcj7AWd6Lf7jowEBQMo4wCZVnueIGw/7C4u0KTBQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.40.0': - resolution: {integrity: sha512-RBFPAxRAIsMisKM47Oe6Lwdv6agZYLz02CUhVCD1sOv5ajAcRMrnwCFBPWwGXpazToW2mjnZxFos8TuFjTU15A==} + '@oxfmt/binding-linux-arm-musleabihf@0.43.0': + resolution: {integrity: sha512-sFg+NWJbLfupYTF4WELHAPSnLPOn1jiDZ33Z1jfDnTaA+cC3iB35x0FMMZTFdFOz3icRIArncwCcemJFGXu6TQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.40.0': - resolution: {integrity: sha512-Nb2XbQ+wV3W2jSIihXdPj7k83eOxeSgYP3N/SRXvQ6ZYPIk6Q86qEh5Gl/7OitX3bQoQrESqm1yMLvZV8/J7dA==} + '@oxfmt/binding-linux-arm64-gnu@0.43.0': + resolution: {integrity: sha512-MelWqv68tX6wZEILDrTc9yewiGXe7im62+5x0bNXlCYFOZdA+VnYiJfAihbROsZ5fm90p9C3haFrqjj43XnlAA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.40.0': - resolution: {integrity: sha512-tGmWhLD/0YMotCdfezlT6tC/MJG/wKpo4vnQ3Cq+4eBk/BwNv7EmkD0VkD5F/dYkT3b8FNU01X2e8vvJuWoM1w==} + '@oxfmt/binding-linux-arm64-musl@0.43.0': + resolution: {integrity: sha512-ROaWfYh+6BSJ1Arwy5ujijTlwnZetxDxzBpDc1oBR4d7rfrPBqzeyjd5WOudowzQUgyavl2wEpzn1hw3jWcqLA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.40.0': - resolution: {integrity: sha512-rVbFyM3e7YhkVnp0IVYjaSHfrBWcTRWb60LEcdNAJcE2mbhTpbqKufx0FrhWfoxOrW/+7UJonAOShoFFLigDqQ==} + '@oxfmt/binding-linux-ppc64-gnu@0.43.0': + resolution: {integrity: sha512-PJRs/uNxmFipJJ8+SyKHh7Y7VZIKQicqrrBzvfyM5CtKi8D7yZKTwUOZV3ffxmiC2e7l1SDJpkBEOyue5NAFsg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.40.0': - resolution: {integrity: sha512-3ZqBw14JtWeEoLiioJcXSJz8RQyPE+3jLARnYM1HdPzZG4vk+Ua8CUupt2+d+vSAvMyaQBTN2dZK+kbBS/j5mA==} + '@oxfmt/binding-linux-riscv64-gnu@0.43.0': + resolution: {integrity: sha512-j6biGAgzIhj+EtHXlbNumvwG7XqOIdiU4KgIWRXAEj/iUbHKukKW8eXa4MIwpQwW1YkxovduKtzEAPnjlnAhVQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.40.0': - resolution: {integrity: sha512-JJ4PPSdcbGBjPvb+O7xYm2FmAsKCyuEMYhqatBAHMp/6TA6rVlf9Z/sYPa4/3Bommb+8nndm15SPFRHEPU5qFA==} + '@oxfmt/binding-linux-riscv64-musl@0.43.0': + resolution: {integrity: sha512-RYWxAcslKxvy7yri24Xm9cmD0RiANaiEPs007EFG6l9h1ChM69Q5SOzACaCoz4Z9dEplnhhneeBaTWMEdpgIbA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.40.0': - resolution: {integrity: sha512-Kp0zNJoX9Ik77wUya2tpBY3W9f40VUoMQLWVaob5SgCrblH/t2xr/9B2bWHfs0WCefuGmqXcB+t0Lq77sbBmZw==} + '@oxfmt/binding-linux-s390x-gnu@0.43.0': + resolution: {integrity: sha512-DT6Q8zfQQy3jxpezAsBACEHNUUixKSYTwdXeXojNHe4DQOoxjPdjr3Szu6BRNjxLykZM/xMNmp9ElOIyDppwtw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.40.0': - resolution: {integrity: sha512-7YTCNzleWTaQTqNGUNQ66qVjpoV6DjbCOea+RnpMBly2bpzrI/uu7Rr+2zcgRfNxyjXaFTVQKaRKjqVdeUfeVA==} + '@oxfmt/binding-linux-x64-gnu@0.43.0': + resolution: {integrity: sha512-R8Yk7iYcuZORXmCfFZClqbDxRZgZ9/HEidUuBNdoX8Ptx07cMePnMVJ/woB84lFIDjh2ROHVaOP40Ds3rBXFqg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.40.0': - resolution: {integrity: sha512-hWnSzJ0oegeOwfOEeejYXfBqmnRGHusgtHfCPzmvJvHTwy1s3Neo59UKc1CmpE3zxvrCzJoVHos0rr97GHMNPw==} + '@oxfmt/binding-linux-x64-musl@0.43.0': + resolution: {integrity: sha512-F2YYqyvnQNvi320RWZNAvsaWEHwmW3k4OwNJ1hZxRKXupY63expbBaNp6jAgvYs7y/g546vuQnGHQuCBhslhLQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.40.0': - resolution: {integrity: sha512-28sJC1lR4qtBJGzSRRbPnSW3GxU2+4YyQFE6rCmsUYqZ5XYH8jg0/w+CvEzQ8TuAQz5zLkcA25nFQGwoU0PT3Q==} + '@oxfmt/binding-openharmony-arm64@0.43.0': + resolution: {integrity: sha512-OE6TdietLXV3F6c7pNIhx/9YC1/2YFwjU9DPc/fbjxIX19hNIaP1rS0cFjCGJlGX+cVJwIKWe8Mos+LdQ1yAJw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.40.0': - resolution: {integrity: sha512-cDkRnyT0dqwF5oIX1Cv59HKCeZQFbWWdUpXa3uvnHFT2iwYSSZspkhgjXjU6iDp5pFPaAEAe9FIbMoTgkTmKPg==} + '@oxfmt/binding-win32-arm64-msvc@0.43.0': + resolution: {integrity: sha512-0nWK6a7pGkbdoypfVicmV9k/N1FwjPZENoqhlTU+5HhZnAhpIO3za30nEE33u6l6tuy9OVfpdXUqxUgZ+4lbZw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.40.0': - resolution: {integrity: sha512-7rPemBJjqm5Gkv6ZRCPvK8lE6AqQ/2z31DRdWazyx2ZvaSgL7QGofHXHNouRpPvNsT9yxRNQJgigsWkc+0qg4w==} + '@oxfmt/binding-win32-ia32-msvc@0.43.0': + resolution: {integrity: sha512-9aokTR4Ft+tRdvgN/pKzSkVy2ksc4/dCpDm9L/xFrbIw0yhLtASLbvoG/5WOTUh/BRPPnfGTsWznEqv0dlOmhA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.40.0': - resolution: {integrity: sha512-/Zmj0yTYSvmha6TG1QnoLqVT7ZMRDqXvFXXBQpIjteEwx9qvUYMBH2xbiOFhDeMUJkGwC3D6fdKsFtaqUvkwNA==} + '@oxfmt/binding-win32-x64-msvc@0.43.0': + resolution: {integrity: sha512-4bPgdQux2ZLWn3bf2TTXXMHcJB4lenmuxrLqygPmvCJ104Yqzj1UctxSRzR31TiJ4MLaG22RK8dUsVpJtrCz5g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint-tsgolint/darwin-arm64@0.17.0': - resolution: {integrity: sha512-z3XwCDuOAKgk7bO4y5tyH8Zogwr51G56R0XGKC3tlAbrAq8DecoxAd3qhRZqWBMG2Gzl5bWU3Ghu7lrxuLPzYw==} + '@oxlint-tsgolint/darwin-arm64@0.19.0': + resolution: {integrity: sha512-FVOIp5Njte8Z6PpINz7sL5blqSro0pAL8VAHYQ+K5Xm4cOrPQ6DGIhH14oXnbRjzn8Kl69qjz8TPteyn8EqwsQ==} cpu: [arm64] os: [darwin] - '@oxlint-tsgolint/darwin-x64@0.17.0': - resolution: {integrity: sha512-TZgVXy0MtI8nt0MYiceuZhHPwHcwlIZ/YwzFTAKrgdHiTvVzFbqHVdXi5wbZfT/o1nHGw9fbGWPlb6qKZ4uZ9Q==} + '@oxlint-tsgolint/darwin-x64@0.19.0': + resolution: {integrity: sha512-GakDTDACePvqOFq3N4oQCl8SyMMa7VBnqV0gDcXPuK50jdWCUqlxM9tgRJarjyIVvmDEJRGYOen+4uBtVwg4Aw==} cpu: [x64] os: [darwin] - '@oxlint-tsgolint/linux-arm64@0.17.0': - resolution: {integrity: sha512-IDfhFl/Y8bjidCvAP6QAxVyBsl78TmfCHlfjtEv2XtJXgYmIwzv6muO18XMp74SZ2qAyD4y2n2dUedrmghGHeA==} + '@oxlint-tsgolint/linux-arm64@0.19.0': + resolution: {integrity: sha512-Ya0R7somo+KDhhkPtENJ9Q28Fost+aqA3MPe86pEqgmukHFc/KO65PgShOSbIFjZNptELEQvsWL8gDxYZWhH3w==} cpu: [arm64] os: [linux] - '@oxlint-tsgolint/linux-x64@0.17.0': - resolution: {integrity: sha512-Bgdgqx/m8EnfjmmlRLEeYy9Yhdt1GdFrMr5mTu/NyLRGkB1C9VLAikdxB7U9QambAGTAmjMbHNFDFk8Vx69Huw==} + '@oxlint-tsgolint/linux-x64@0.19.0': + resolution: {integrity: sha512-yFH378jWc1k/oJmpk+TKpWbKvFieJJvsOHxVMSNFc+ukqs44ZSHVt4HFfAhXAt/bzVK2f7EIDTGp8Hm1OjoJ6Q==} cpu: [x64] os: [linux] - '@oxlint-tsgolint/win32-arm64@0.17.0': - resolution: {integrity: sha512-dO6wyKMDqFWh1vwr+zNZS7/ovlfGgl4S3P1LDy4CKjP6V6NGtdmEwWkWax8j/I8RzGZdfXKnoUfb/qhVg5bx0w==} + '@oxlint-tsgolint/win32-arm64@0.19.0': + resolution: {integrity: sha512-R6NyAtha7OWxh7NGBeFxqDTGAVl1Xj4xLa8Qj39PKbIDqBeVW8BIb+1nEnRp+Mo/VpRoeoFAcqlBsuMcUMd26Q==} cpu: [arm64] os: [win32] - '@oxlint-tsgolint/win32-x64@0.17.0': - resolution: {integrity: sha512-lPGYFp3yX2nh6hLTpIuMnJbZnt3Df42VkoA/fSkMYi2a/LXdDytQGpgZOrb5j47TICARd34RauKm0P3OA4Oxbw==} + '@oxlint-tsgolint/win32-x64@0.19.0': + resolution: {integrity: sha512-2ePvxcbS5tPOmrQvxR8Kc+IqzdTtlrGeMDv+jjTYfkTFPmh2rF9yxVchi/4WM6js3gt2UauQeMV/tfnZNemENQ==} cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.52.0': - resolution: {integrity: sha512-fW2pmR1VzFEdcvOYeSiv+R7CqffOjr9Bv5QmZaHuHJ4ZCqouaF6o48N/hJ3H1n9Zd8PCMFgJkeqUvUsVce01mw==} + '@oxlint/binding-android-arm-eabi@1.58.0': + resolution: {integrity: sha512-1T7UN3SsWWxpWyWGn1cT3ASNJOo+pI3eUkmEl7HgtowapcV8kslYpFQcYn431VuxghXakPNlbjRwhqmR37PFOg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.52.0': - resolution: {integrity: sha512-ptuJljIB+klNi8//qxXyGD51NLJXY9lv40Olc7l3/pEyjejWwXGvGMO0GM6f0JsjmbnDL+VkX7RVQNhByaX8WA==} + '@oxlint/binding-android-arm64@1.58.0': + resolution: {integrity: sha512-GryzujxuiRv2YFF7bRy8mKcxlbuAN+euVUtGJt9KKbLT8JBUIosamVhcthLh+VEr6KE6cjeVMAQxKAzJcoN7dg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.52.0': - resolution: {integrity: sha512-5d079Uw43BHVZzOwm3uJI2PgSbsZJTpfHDq2jMOR6rRjGiEBlgasaEvAA26VBqpkO1++/59ZCKLBnEpkro3zIg==} + '@oxlint/binding-darwin-arm64@1.58.0': + resolution: {integrity: sha512-7/bRSJIwl4GxeZL9rPZ11anNTyUO9epZrfEJH/ZMla3+/gbQ6xZixh9nOhsZ0QwsTW7/5J2A/fHbD1udC5DQQA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.52.0': - resolution: {integrity: sha512-vRTjnhPEHAyfUhO9w6GM1VkxeVXFcDs+huyB5YNMw+Py+6PRYDFFrrOEr0rZYcoGtSH25ScozZV8I1UXrzaDjQ==} + '@oxlint/binding-darwin-x64@1.58.0': + resolution: {integrity: sha512-EqdtJSiHweS2vfILNrpyJ6HUwpEq2g7+4Zx1FPi4hu3Hu7tC3znF6ufbXO8Ub2LD4mGgznjI7kSdku9NDD1Mkg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.52.0': - resolution: {integrity: sha512-vFthhhciRAliAjoKMsvi7UkkQp/EtMNhmCRYBuKsNiTH0k4H3SFfbuWWr80Q7+uTXijfBP91KO/EeF48RggC7A==} + '@oxlint/binding-freebsd-x64@1.58.0': + resolution: {integrity: sha512-VQt5TH4M42mY20F545G637RKxV/yjwVtKk2vfXuazfReSIiuvWBnv+FVSvIV5fKVTJNjt3GSJibh6JecbhGdBw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.52.0': - resolution: {integrity: sha512-qX3K4mKbju54ojUa8nigVxxZAUDBGu5MGzpoXvWmiw+7hafoQKaLAoTm94EqRlv9v27p864GQBgc4g3qYtMXXA==} + '@oxlint/binding-linux-arm-gnueabihf@1.58.0': + resolution: {integrity: sha512-fBYcj4ucwpAtjJT3oeBdFBYKvNyjRSK+cyuvBOTQjh0jvKp4yeA4S/D0IsCHus/VPaNG5L48qQkh+Vjy3HL2/Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.52.0': - resolution: {integrity: sha512-x5D5/EUS9U4kndPncLB6mDfCsv7i8XcRLu0DZyTngXvyqapc96WwmyyOG2j8Dt26aE8Ykgh6AhsHp9bQtoBUAw==} + '@oxlint/binding-linux-arm-musleabihf@1.58.0': + resolution: {integrity: sha512-0BeuFfwlUHlJ1xpEdSD1YO3vByEFGPg36uLjK1JgFaxFb4W6w17F8ET8sz5cheZ4+x5f2xzdnRrrWv83E3Yd8g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.52.0': - resolution: {integrity: sha512-2Ep1tnGLuGG7lUkKG/nilIJ0/T2rebEcATxMJ7afuhD6Z2Sc9dDcpX00IngAMyR9l6hXrvaOw9YA5HUAJVSENg==} + '@oxlint/binding-linux-arm64-gnu@1.58.0': + resolution: {integrity: sha512-TXlZgnPTlxrQzxG9ZXU7BNwx1Ilrr17P3GwZY0If2EzrinqRH3zXPc3HrRcBJgcsoZNMuNL5YivtkJYgp467UQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.52.0': - resolution: {integrity: sha512-54wxvb1Pztz0GMgTLUG9HsH8uhZSL4UbG7n4PDxWIRT9TygTVYKfD6D7iasYdKg6ZpWB5Y86VMxgjSJpR/Y7bQ==} + '@oxlint/binding-linux-arm64-musl@1.58.0': + resolution: {integrity: sha512-zSoYRo5dxHLcUx93Stl2hW3hSNjPt99O70eRVWt5A1zwJ+FPjeCCANCD2a9R4JbHsdcl11TIQOjyigcRVOH2mw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.52.0': - resolution: {integrity: sha512-A82Zks1lJyLclrj8n2tJPHOw2ieZXCaBctnCarS1BRlPQMC1Y98vWCLqgvg9ssWy5ZAja0IjUHN1cYsp53mrqA==} + '@oxlint/binding-linux-ppc64-gnu@1.58.0': + resolution: {integrity: sha512-NQ0U/lqxH2/VxBYeAIvMNUK1y0a1bJ3ZicqkF2c6wfakbEciP9jvIE4yNzCFpZaqeIeRYaV7AVGqEO1yrfVPjA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.52.0': - resolution: {integrity: sha512-ci89Ou+u9vnA0r4eQqGm/KPEkpea+QEtZCLKkrOAD/K5ZBwjS8ToID6aMgsDbIOJUNBGufsmX0iCC7EWrNKQFA==} + '@oxlint/binding-linux-riscv64-gnu@1.58.0': + resolution: {integrity: sha512-X9J+kr3gIC9FT8GuZt0ekzpNUtkBVzMVU4KiKDSlocyQuEgi3gBbXYN8UkQiV77FTusLDPsovjo95YedHr+3yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.52.0': - resolution: {integrity: sha512-3/+DVDWajFSu69TaYnKkoUgMEcHR3puO8TcBu3fPCKRhbLjgwDiYIVRdvQX0QaSjkNPJARmpYq7vlPHWNo2cUA==} + '@oxlint/binding-linux-riscv64-musl@1.58.0': + resolution: {integrity: sha512-CDze3pi1OO3Wvb/QsXjmLEY4XPKGM6kIo82ssNOgmcl1IdndF9VSGAE38YLhADWmOac7fjqhBw82LozuUVxD0Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.52.0': - resolution: {integrity: sha512-BU7CbceOh00NDmY1IYr72qZoj4sJVHB9DCL2tIq2vyNllNJIpZWTxqlzdqmC4FViXWMy8kZNkOa+SdauH+EcoQ==} + '@oxlint/binding-linux-s390x-gnu@1.58.0': + resolution: {integrity: sha512-b/89glbxFaEAcA6Uf1FvCNecBJEgcUTsV1quzrqXM/o4R1M4u+2KCVuyGCayN2UpsRWtGGLb+Ver0tBBpxaPog==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.52.0': - resolution: {integrity: sha512-JUVZ6TKYl1yArS3xGsNLQlZxgVpjNKtZFja6VxSTDy2ToN7H58PiDRcxWoN2XoIcWlHSvK7pkIPFNOyzdEJ23A==} + '@oxlint/binding-linux-x64-gnu@1.58.0': + resolution: {integrity: sha512-0/yYpkq9VJFCEcuRlrViGj8pJUFFvNS4EkEREaN7CB1EcLXJIaVSSa5eCihwBGXtOZxhnblWgxks9juRdNQI7w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.52.0': - resolution: {integrity: sha512-IatLKG6UUbIbTBjBZ9SIAYp4SIvOpYIXPXn9cMLqWxh9HrHsu0fLNL+VQ67y4vdlIleYLeuIHkAp3M6saIN1RQ==} + '@oxlint/binding-linux-x64-musl@1.58.0': + resolution: {integrity: sha512-hr6FNvmcAXiH+JxSvaJ4SJ1HofkdqEElXICW9sm3/Rd5eC3t7kzvmLyRAB3NngKO2wzXRCAm4Z/mGWfrsS4X8w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.52.0': - resolution: {integrity: sha512-CWgJ6FepHryuc/lgQWStFf3lcvEkbFLSa9zqO0D0QLVfrdg43I4XItKpL/bnfm4n7obzwgG8j8sBggdoxJQKfw==} + '@oxlint/binding-openharmony-arm64@1.58.0': + resolution: {integrity: sha512-R+O368VXgRql1K6Xar+FEo7NEwfo13EibPMoTv3sesYQedRXd6m30Dh/7lZMxnrQVFfeo4EOfYIP4FpcgWQNHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.52.0': - resolution: {integrity: sha512-EuNAbPpctu8jYMZnvYh53Xw3YVY2nIi9bQlyMjY0eKiJxDv8ikHrAfcVcwTQW9xa5tp0eiMkmW7iHPP5CYUC9Q==} + '@oxlint/binding-win32-arm64-msvc@1.58.0': + resolution: {integrity: sha512-Q0FZiAY/3c4YRj4z3h9K1PgaByrifrfbBoODSeX7gy97UtB7pySPUQfC2B/GbxWU6k7CzQrRy5gME10PltLAFQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.52.0': - resolution: {integrity: sha512-wu3fquQttzSXwyy8DfdOG3Kyb17yAbRhwPlly7NHSXkrffAEAmZ6+o38tCNgsReGLugbn/wbq4uS4nEQubCq+A==} + '@oxlint/binding-win32-ia32-msvc@1.58.0': + resolution: {integrity: sha512-Y8FKBABrSPp9H0QkRLHDHOSUgM/309a3IvOVgPcVxYcX70wxJrk608CuTg7w+C6vEd724X5wJoNkBcGYfH7nNQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.52.0': - resolution: {integrity: sha512-wikx9I9J9/lPOZlrCCNgm8YjWkia8NZfhWd1TTvZTMguyChbw/oA2VEM6Fzx+kkpA+1qu5Mo7nrLdOXEJavw8g==} + '@oxlint/binding-win32-x64-msvc@1.58.0': + resolution: {integrity: sha512-bCn5rbiz5My+Bj7M09sDcnqW0QJyINRVxdZ65x1/Y2tGrMwherwK/lpk+HRQCKvXa8pcaQdF5KY5j54VGZLwNg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2715,20 +2729,59 @@ packages: engines: {node: '>=18'} hasBin: true - '@portabletext/to-html@2.0.17': - resolution: {integrity: sha512-kLID+QW4qIybqeN5yv1LuycbYC2DMVDcIFKfhrAFpBI3P9lcrYUv8ukzhp2Nh55hvuGphzvTVHDSo/fkbv8ziA==} - engines: {node: ^14.13.1 || >=16.0.0} + '@portabletext/to-html@5.0.2': + resolution: {integrity: sha512-w59PcErj5JXUCv9tbV2npqJmcnORTAftCMLp0vc9FnWrXL3C9qYvuB2MQbdHsZEOesF3VmwqUsYUgjm7PX4JTw==} + engines: {node: '>=20.19 <22 || >=22.12'} + + '@portabletext/toolkit@5.0.2': + resolution: {integrity: sha512-Njc1LE1PMJkTx/wEPqZ6sOWGgFgX2B47fxpOQ/Ia4ByhsZoA5Sq8dNvvV5F052j/xE8TbOLiBEjS848FkKADDQ==} + engines: {node: '>=20.19 <22 || >=22.12'} + + '@portabletext/types@4.0.2': + resolution: {integrity: sha512-djfIGU9n6DRrunlvj2nIDAp17URo/nA4jSXGvf+Gupx8NLLy9fmJBZ3GL8yhqn9lSVc+cKCharjOa3aOBnWbRw==} + engines: {node: '>=20.19 <22 || >=22.12'} + + '@posthog/core@1.24.1': + resolution: {integrity: sha512-e8AciAnc6MRFws89ux8lJKFAaI03yEon0ASDoUO7yS91FVqbUGXYekObUUR3LHplcg+pmyiJBI0jolY0SFbGRA==} + + '@posthog/types@1.363.6': + resolution: {integrity: sha512-SPU8psjrpK8prfFyYwvb25F1AgqSM32zdU1XPVIhUa107Cyw+VGw38Gv+AeqkEoAYQE2TFlWJT8DWUrw/mNDoQ==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} - '@portabletext/toolkit@2.0.18': - resolution: {integrity: sha512-m3v2WwKQTNNk5BFZlUuPuCW0Zi6iDSpwrium4Ej5L2FHDXhFuwAyEMPXDrvwPvqjES/oJzcwmdKLMhYa44T9BQ==} - engines: {node: ^14.13.1 || >=16.0.0} + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - '@portabletext/types@2.0.15': - resolution: {integrity: sha512-2e6i2gSQsrA/5OL5Gm4/9bxB9MNO73Fa47zj+0mT93xkoQUCGCWX5fZh1YBJ86hszaRYlqvqG08oULxvvPPp/Q==} - engines: {node: ^14.13.1 || >=16.0.0 || >=18.0.0} + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} - '@posthog/core@1.2.2': - resolution: {integrity: sha512-f16Ozx6LIigRG+HsJdt+7kgSxZTHeX5f1JlCGKI1lXcvlZgfsCR338FuMI2QRYXGl+jg/vYFzGOTQBxl90lnBg==} + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@rolldown/binding-android-arm64@1.0.0-rc.12': + resolution: {integrity: sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] '@rolldown/binding-android-arm64@1.0.0-rc.4': resolution: {integrity: sha512-vRq9f4NzvbdZavhQbjkJBx7rRebDKYR9zHfO/Wg486+I7bSecdUapzCm5cyXoK+LHokTxgSq7A5baAXUZkIz0w==} @@ -2736,11 +2789,11 @@ packages: cpu: [arm64] os: [android] - '@rolldown/binding-android-arm64@1.0.0-rc.9': - resolution: {integrity: sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==} + '@rolldown/binding-darwin-arm64@1.0.0-rc.12': + resolution: {integrity: sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] - os: [android] + os: [darwin] '@rolldown/binding-darwin-arm64@1.0.0-rc.4': resolution: {integrity: sha512-kFgEvkWLqt3YCgKB5re9RlIrx9bRsvyVUnaTakEpOPuLGzLpLapYxE9BufJNvPg8GjT6mB1alN4yN1NjzoeM8Q==} @@ -2748,10 +2801,10 @@ packages: cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-arm64@1.0.0-rc.9': - resolution: {integrity: sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==} + '@rolldown/binding-darwin-x64@1.0.0-rc.12': + resolution: {integrity: sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] + cpu: [x64] os: [darwin] '@rolldown/binding-darwin-x64@1.0.0-rc.4': @@ -2760,11 +2813,11 @@ packages: cpu: [x64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-rc.9': - resolution: {integrity: sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==} + '@rolldown/binding-freebsd-x64@1.0.0-rc.12': + resolution: {integrity: sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] - os: [darwin] + os: [freebsd] '@rolldown/binding-freebsd-x64@1.0.0-rc.4': resolution: {integrity: sha512-ep3Catd6sPnHTM0P4hNEvIv5arnDvk01PfyJIJ+J3wVCG1eEaPo09tvFqdtcaTrkwQy0VWR24uz+cb4IsK53Qw==} @@ -2772,11 +2825,11 @@ packages: cpu: [x64] os: [freebsd] - '@rolldown/binding-freebsd-x64@1.0.0-rc.9': - resolution: {integrity: sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12': + resolution: {integrity: sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] + cpu: [arm] + os: [linux] '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.4': resolution: {integrity: sha512-LwA5ayKIpnsgXJEwWc3h8wPiS33NMIHd9BhsV92T8VetVAbGe2qXlJwNVDGHN5cOQ22R9uYvbrQir2AB+ntT2w==} @@ -2784,11 +2837,12 @@ packages: cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.9': - resolution: {integrity: sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12': + resolution: {integrity: sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] + cpu: [arm64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.4': resolution: {integrity: sha512-AC1WsGdlV1MtGay/OQ4J9T7GRadVnpYRzTcygV1hKnypbYN20Yh4t6O1Sa2qRBMqv1etulUknqXjc3CTIsBu6A==} @@ -2797,12 +2851,12 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.9': - resolution: {integrity: sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==} + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.12': + resolution: {integrity: sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] + libc: [musl] '@rolldown/binding-linux-arm64-musl@1.0.0-rc.4': resolution: {integrity: sha512-lU+6rgXXViO61B4EudxtVMXSOfiZONR29Sys5VGSetUY7X8mg9FCKIIjcPPj8xNDeYzKl+H8F/qSKOBVFJChCQ==} @@ -2811,107 +2865,100 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.9': - resolution: {integrity: sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.9': - resolution: {integrity: sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==} + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12': + resolution: {integrity: sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.9': - resolution: {integrity: sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==} + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12': + resolution: {integrity: sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.4': - resolution: {integrity: sha512-DZaN1f0PGp/bSvKhtw50pPsnln4T13ycDq1FrDWRiHmWt1JeW+UtYg9touPFf8yt993p8tS2QjybpzKNTxYEwg==} + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.12': + resolution: {integrity: sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.9': - resolution: {integrity: sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==} + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.4': + resolution: {integrity: sha512-DZaN1f0PGp/bSvKhtw50pPsnln4T13ycDq1FrDWRiHmWt1JeW+UtYg9touPFf8yt993p8tS2QjybpzKNTxYEwg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.0-rc.4': - resolution: {integrity: sha512-RnGxwZLN7fhMMAItnD6dZ7lvy+TI7ba+2V54UF4dhaWa/p8I/ys1E73KO6HmPmgz92ZkfD8TXS1IMV8+uhbR9g==} + '@rolldown/binding-linux-x64-musl@1.0.0-rc.12': + resolution: {integrity: sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-linux-x64-musl@1.0.0-rc.9': - resolution: {integrity: sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==} + '@rolldown/binding-linux-x64-musl@1.0.0-rc.4': + resolution: {integrity: sha512-RnGxwZLN7fhMMAItnD6dZ7lvy+TI7ba+2V54UF4dhaWa/p8I/ys1E73KO6HmPmgz92ZkfD8TXS1IMV8+uhbR9g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.0-rc.4': - resolution: {integrity: sha512-6lcI79+X8klGiGd8yHuTgQRjuuJYNggmEml+RsyN596P23l/zf9FVmJ7K0KVKkFAeYEdg0iMUKyIxiV5vebDNQ==} + '@rolldown/binding-openharmony-arm64@1.0.0-rc.12': + resolution: {integrity: sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-openharmony-arm64@1.0.0-rc.9': - resolution: {integrity: sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==} + '@rolldown/binding-openharmony-arm64@1.0.0-rc.4': + resolution: {integrity: sha512-6lcI79+X8klGiGd8yHuTgQRjuuJYNggmEml+RsyN596P23l/zf9FVmJ7K0KVKkFAeYEdg0iMUKyIxiV5vebDNQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-rc.4': - resolution: {integrity: sha512-wz7ohsKCAIWy91blZ/1FlpPdqrsm1xpcEOQVveWoL6+aSPKL4VUcoYmmzuLTssyZxRpEwzuIxL/GDsvpjaBtOw==} + '@rolldown/binding-wasm32-wasi@1.0.0-rc.12': + resolution: {integrity: sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-wasm32-wasi@1.0.0-rc.9': - resolution: {integrity: sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==} + '@rolldown/binding-wasm32-wasi@1.0.0-rc.4': + resolution: {integrity: sha512-wz7ohsKCAIWy91blZ/1FlpPdqrsm1xpcEOQVveWoL6+aSPKL4VUcoYmmzuLTssyZxRpEwzuIxL/GDsvpjaBtOw==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.4': - resolution: {integrity: sha512-cfiMrfuWCIgsFmcVG0IPuO6qTRHvF7NuG3wngX1RZzc6dU8FuBFb+J3MIR5WrdTNozlumfgL4cvz+R4ozBCvsQ==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12': + resolution: {integrity: sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.9': - resolution: {integrity: sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.4': + resolution: {integrity: sha512-cfiMrfuWCIgsFmcVG0IPuO6qTRHvF7NuG3wngX1RZzc6dU8FuBFb+J3MIR5WrdTNozlumfgL4cvz+R4ozBCvsQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.4': - resolution: {integrity: sha512-p6UeR9y7ht82AH57qwGuFYn69S6CZ7LLKdCKy/8T3zS9VTrJei2/CGsTUV45Da4Z9Rbhc7G4gyWQ/Ioamqn09g==} + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.12': + resolution: {integrity: sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.9': - resolution: {integrity: sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==} + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.4': + resolution: {integrity: sha512-p6UeR9y7ht82AH57qwGuFYn69S6CZ7LLKdCKy/8T3zS9VTrJei2/CGsTUV45Da4Z9Rbhc7G4gyWQ/Ioamqn09g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] + '@rolldown/pluginutils@1.0.0-rc.12': + resolution: {integrity: sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==} + '@rolldown/pluginutils@1.0.0-rc.4': resolution: {integrity: sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==} - '@rolldown/pluginutils@1.0.0-rc.9': - resolution: {integrity: sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==} - '@rollup/plugin-virtual@3.0.2': resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} engines: {node: '>=14.0.0'} @@ -2921,147 +2968,147 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + '@rollup/rollup-android-arm-eabi@4.60.0': + resolution: {integrity: sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + '@rollup/rollup-android-arm64@4.60.0': + resolution: {integrity: sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + '@rollup/rollup-darwin-arm64@4.60.0': + resolution: {integrity: sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + '@rollup/rollup-darwin-x64@4.60.0': + resolution: {integrity: sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + '@rollup/rollup-freebsd-arm64@4.60.0': + resolution: {integrity: sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + '@rollup/rollup-freebsd-x64@4.60.0': + resolution: {integrity: sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + '@rollup/rollup-linux-arm-gnueabihf@4.60.0': + resolution: {integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + '@rollup/rollup-linux-arm-musleabihf@4.60.0': + resolution: {integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + '@rollup/rollup-linux-arm64-gnu@4.60.0': + resolution: {integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + '@rollup/rollup-linux-arm64-musl@4.60.0': + resolution: {integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + '@rollup/rollup-linux-loong64-gnu@4.60.0': + resolution: {integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + '@rollup/rollup-linux-loong64-musl@4.60.0': + resolution: {integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + '@rollup/rollup-linux-ppc64-gnu@4.60.0': + resolution: {integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + '@rollup/rollup-linux-ppc64-musl@4.60.0': + resolution: {integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + '@rollup/rollup-linux-riscv64-gnu@4.60.0': + resolution: {integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + '@rollup/rollup-linux-riscv64-musl@4.60.0': + resolution: {integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + '@rollup/rollup-linux-s390x-gnu@4.60.0': + resolution: {integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + '@rollup/rollup-linux-x64-gnu@4.60.0': + resolution: {integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + '@rollup/rollup-linux-x64-musl@4.60.0': + resolution: {integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + '@rollup/rollup-openbsd-x64@4.60.0': + resolution: {integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + '@rollup/rollup-openharmony-arm64@4.60.0': + resolution: {integrity: sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + '@rollup/rollup-win32-arm64-msvc@4.60.0': + resolution: {integrity: sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + '@rollup/rollup-win32-ia32-msvc@4.60.0': + resolution: {integrity: sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + '@rollup/rollup-win32-x64-gnu@4.60.0': + resolution: {integrity: sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + '@rollup/rollup-win32-x64-msvc@4.60.0': + resolution: {integrity: sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==} cpu: [x64] os: [win32] - '@sanity/asset-utils@1.3.2': - resolution: {integrity: sha512-dixN6MpMXsCEVh0Dr932cgZ4cU3Z2JnNOYBxjV+dgO6AnqVpNQTY+KgGMYlA1ca5zCztQI1VSk/MBCPSxihPqQ==} - engines: {node: '>=10'} + '@sanity/asset-utils@2.3.0': + resolution: {integrity: sha512-dlEmALjQ5iyQG0O8ZVmkkE3wUYCKfRmiyMvuuGN5SF9buAHxmseBOKJ/Iy2DU/8ef70mtUXlzeCRSlTN/nmZsg==} + engines: {node: '>=18'} '@sanity/client@7.17.0': resolution: {integrity: sha512-ApwC9MC73A0WkVeD9QEuH1Fw0Odr+hSUlo02zmoqxs4WxOsXXg29fF/DZGnrwWlAdrt5BmHq8HUtY3LPlo3Khg==} @@ -3070,17 +3117,20 @@ packages: '@sanity/eventsource@5.0.2': resolution: {integrity: sha512-/B9PMkUvAlUrpRq0y+NzXgRv5lYCLxZNsBJD2WXVnqZYOfByL9oQBV7KiTaARuObp5hcQYuPfOAVjgXe3hrixA==} - '@sanity/image-url@1.0.2': - resolution: {integrity: sha512-C4+jb2ny3ZbMgEkLd7Z3C75DsxcTEoE+axXQJsQ75ou0AKWGdVsP351hqK6mJUUxn5HCSlu3vznoh7Yljye4cQ==} - engines: {node: '>=10.0.0'} + '@sanity/image-url@2.0.3': + resolution: {integrity: sha512-A/vOugFw/ROGgSeSGB6nimO0c35x9KztatOPIIVlhkL+zsOfP7khigCbdJup2FSv6C03FX2XaUAhXojCxANl2Q==} + engines: {node: '>=20.19.0'} '@sanity/media-library-types@1.2.0': resolution: {integrity: sha512-p+Bw96I63SwBcMNA/L5dnMdEcS88EEDUDZ65LGuwOCMXrESRGMFCSxgc+0HnL0JXDIzgYgfrPuf1I3bO9QneAw==} - '@sanity/types@3.99.0': - resolution: {integrity: sha512-a766U9VSoyOSWq+RZz9wsEo/Nnn+inDkEcdGu+rHFuygdepullB/RZpF2MxNsfUMCSPnajgG1Tm9lhwbSmlySA==} + '@sanity/signed-urls@2.0.2': + resolution: {integrity: sha512-w/Aq0JDYI44WC5w8mzJBAjCem8qlGrxGTzvNbUWwBfys6kSL+TZBSypV5waCc35XRgt0X5zdYZMJOrshcjJLFw==} + + '@sanity/types@5.18.0': + resolution: {integrity: sha512-f+fCPYbzL+4kCMCAN3wsuTpf6xvQ0h0NosYe3qg6Pp8SoAoQh/ABNe6FYhXASC/U4aFKdSgff0JUwIWosuIOpg==} peerDependencies: - '@types/react': 18 || 19 + '@types/react': ^19.2 '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -3172,69 +3222,69 @@ packages: peerDependencies: tailwindcss: '>=3.2.0' - '@tailwindcss/node@4.2.1': - resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} + '@tailwindcss/node@4.2.2': + resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} - '@tailwindcss/oxide-android-arm64@4.2.1': - resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} + '@tailwindcss/oxide-android-arm64@4.2.2': + resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} engines: {node: '>= 20'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.2.1': - resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} + '@tailwindcss/oxide-darwin-arm64@4.2.2': + resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.2.1': - resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} + '@tailwindcss/oxide-darwin-x64@4.2.2': + resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.2.1': - resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} + '@tailwindcss/oxide-freebsd-x64@4.2.2': + resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': - resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': + resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} engines: {node: '>= 20'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': - resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': + resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-arm64-musl@4.2.1': - resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': + resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [musl] - '@tailwindcss/oxide-linux-x64-gnu@4.2.1': - resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': + resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-x64-musl@4.2.1': - resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} + '@tailwindcss/oxide-linux-x64-musl@4.2.2': + resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [musl] - '@tailwindcss/oxide-wasm32-wasi@4.2.1': - resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} + '@tailwindcss/oxide-wasm32-wasi@4.2.2': + resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -3245,26 +3295,26 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': - resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': + resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.2.1': - resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': + resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} engines: {node: '>= 20'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.2.1': - resolution: {integrity: sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==} + '@tailwindcss/oxide@4.2.2': + resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} engines: {node: '>= 20'} - '@tailwindcss/vite@4.2.1': - resolution: {integrity: sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w==} + '@tailwindcss/vite@4.2.2': + resolution: {integrity: sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==} peerDependencies: - vite: ^5.2.0 || ^6 || ^7 + vite: ^5.2.0 || ^6 || ^7 || ^8 '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -3311,40 +3361,43 @@ packages: '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@vitejs/plugin-basic-ssl@2.1.4': resolution: {integrity: sha512-HXciTXN/sDBYWgeAD4V4s0DN0g72x5mlxQhHxtYu3Tt8BLa6MzcJZUyDVFCdtjNs3bfENVHVzOsmooTVuNgAAw==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} peerDependencies: vite: ^6.0.0 || ^7.0.0 - '@vitest/expect@4.1.0': - resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} + '@vitest/expect@4.1.2': + resolution: {integrity: sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==} - '@vitest/mocker@4.1.0': - resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} + '@vitest/mocker@4.1.2': + resolution: {integrity: sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==} peerDependencies: msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@4.1.0': - resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} + '@vitest/pretty-format@4.1.2': + resolution: {integrity: sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==} - '@vitest/runner@4.1.0': - resolution: {integrity: sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==} + '@vitest/runner@4.1.2': + resolution: {integrity: sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==} - '@vitest/snapshot@4.1.0': - resolution: {integrity: sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==} + '@vitest/snapshot@4.1.2': + resolution: {integrity: sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==} - '@vitest/spy@4.1.0': - resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} + '@vitest/spy@4.1.2': + resolution: {integrity: sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==} - '@vitest/utils@4.1.0': - resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} + '@vitest/utils@4.1.2': + resolution: {integrity: sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==} agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} @@ -3381,16 +3434,6 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} - any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - arg@5.0.2: - resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -3420,10 +3463,6 @@ packages: before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -3450,10 +3489,6 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} - camelcase-css@2.0.1: - resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} - engines: {node: '>= 6'} - caniuse-lite@1.0.30001777: resolution: {integrity: sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==} @@ -3464,10 +3499,6 @@ packages: chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -3507,10 +3538,6 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -3520,8 +3547,12 @@ packages: cookieconsent@3.1.1: resolution: {integrity: sha512-v8JWLJcI7Zs9NWrs8hiVldVtm3EBF70TJI231vxn6YToBGj0c9dvdnYwltydkAnrbBMOM/qX1xLFrnTfm5wTag==} - core-js@3.48.0: - resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==} + core-js@3.49.0: + resolution: {integrity: sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} css-select@6.0.0: resolution: {integrity: sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==} @@ -3571,16 +3602,10 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - didyoumean@1.2.2: - resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - - diff@8.0.3: - resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} engines: {node: '>=0.3.1'} - dlv@1.1.3: - resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -3591,6 +3616,9 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} + dompurify@3.3.3: + resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -3604,8 +3632,8 @@ packages: electron-to-chromium@1.5.307: resolution: {integrity: sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==} - emnapi@1.8.1: - resolution: {integrity: sha512-34i2BbgHx1LnEO4JCGQYo6h6s4e4KrdWtdTHfllBNLbXSHPmdIHplxKejfabsRK+ukNciqVdalB+fxMibqHdaQ==} + emnapi@1.9.1: + resolution: {integrity: sha512-s4RbfzgbYg9cWBZXJT6LazImJQ5p+F+LyTsCWQJXbGVdPmtCtdlwqd0Oiv3O51KyYV/Hq58xszaQ/l153tK6Uw==} peerDependencies: node-addon-api: '>= 6.1.0' peerDependenciesMeta: @@ -3785,10 +3813,6 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} @@ -3833,10 +3857,6 @@ packages: interactjs@1.10.27: resolution: {integrity: sha512-y/8RcCftGAF24gSp76X2JS3XpHiUvDQyhF8i7ujemBz77hwiHDuJzftHx7thY8cxGogwGiPJ+o97kWB6eAXnsA==} - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} @@ -3865,6 +3885,9 @@ packages: resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==} engines: {node: '>=10'} + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + ismobilejs@1.1.1: resolution: {integrity: sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw==} @@ -3876,10 +3899,6 @@ packages: resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} - jiti@1.21.7: - resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} - hasBin: true - jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -3910,73 +3929,36 @@ packages: jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - lightningcss-android-arm64@1.31.1: - resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [android] - lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] - lightningcss-darwin-arm64@1.31.1: - resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] - lightningcss-darwin-arm64@1.32.0: resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] - lightningcss-darwin-x64@1.31.1: - resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [darwin] - lightningcss-darwin-x64@1.32.0: resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] - lightningcss-freebsd-x64@1.31.1: - resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [freebsd] - lightningcss-freebsd-x64@1.32.0: resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] - lightningcss-linux-arm-gnueabihf@1.31.1: - resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} - engines: {node: '>= 12.0.0'} - cpu: [arm] - os: [linux] - lightningcss-linux-arm-gnueabihf@1.32.0: resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] - lightningcss-linux-arm64-gnu@1.31.1: - resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - libc: [glibc] - lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} @@ -3984,13 +3966,6 @@ packages: os: [linux] libc: [glibc] - lightningcss-linux-arm64-musl@1.31.1: - resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - libc: [musl] - lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} @@ -3998,13 +3973,6 @@ packages: os: [linux] libc: [musl] - lightningcss-linux-x64-gnu@1.31.1: - resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - libc: [glibc] - lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} @@ -4012,13 +3980,6 @@ packages: os: [linux] libc: [glibc] - lightningcss-linux-x64-musl@1.31.1: - resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - libc: [musl] - lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} @@ -4026,45 +3987,22 @@ packages: os: [linux] libc: [musl] - lightningcss-win32-arm64-msvc@1.31.1: - resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [win32] - lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] - lightningcss-win32-x64-msvc@1.31.1: - resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [win32] - lightningcss-win32-x64-msvc@1.32.0: resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.31.1: - resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} - engines: {node: '>= 12.0.0'} - lightningcss@1.32.0: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} - lilconfig@3.1.3: - resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} - engines: {node: '>=14'} - - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - listr2@9.0.5: resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} engines: {node: '>=20.0.0'} @@ -4077,6 +4015,9 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -4114,8 +4055,8 @@ packages: resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} hasBin: true - msgpackr@1.11.8: - resolution: {integrity: sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA==} + msgpackr@1.11.9: + resolution: {integrity: sha512-FkoAAyyA6HM8wL882EcEyFZ9s7hVADSwG9xrVx3dxxNQAtgADTrJoEWivID82Iv1zWDsv/OtbrrcZAzGzOMdNw==} mute-stream@2.0.0: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} @@ -4125,21 +4066,18 @@ packages: resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} engines: {node: ^20.17.0 || >=22.9.0} - mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - ngx-cookieconsent@4.0.2: - resolution: {integrity: sha512-KREGRd53LV0SrFlJsk1XwgzZIEf7GAeLD3/znYF/CzTYml+SFqWrYleXA3aMtFLGqUHwbGS/fMA6cHwpvxT5JA==} + ngx-cookieconsent@8.0.0: + resolution: {integrity: sha512-stH6Y9P6D9NvifBMp799c/8s1KBUKlzk4MBAASJuVcin9mLehvghGay/JrDmTO+FumlDEr5UpZNpdxiqSJF8kQ==} peerDependencies: - '@angular/common': '>=14.0.0' - '@angular/core': '>=14.0.0' + '@angular/common': '>=19.0.0' + '@angular/core': '>=19.0.0' cookieconsent: ^3.1.1 - rxjs: ^7.5.0 + rxjs: ^6.5.3 || ^7.4.0 node-addon-api@6.1.0: resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} @@ -4154,21 +4092,9 @@ packages: node-releases@2.0.36: resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-hash@3.0.0: - resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} - engines: {node: '>= 6'} - object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -4183,29 +4109,29 @@ packages: ordered-binary@1.6.1: resolution: {integrity: sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==} - oxc-parser@0.120.0: - resolution: {integrity: sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==} + oxc-parser@0.123.0: + resolution: {integrity: sha512-F6ak0tFc01ZGbl5KxvLDQ2K005Z086mp3ByCQBDhUjqXLkapGUkMuJSsYixncdEpkLlcRDcruHR71LD339ADUA==} engines: {node: ^20.19.0 || >=22.12.0} - oxc-transform@0.120.0: - resolution: {integrity: sha512-qsALl0xO6stW/ijkwlQnUZjx7pkradESNpObXZIALtD8HySVNjgZvMVKCAuUYnDxeW1JQkfbbdQvKN28tdvH4g==} + oxc-transform@0.123.0: + resolution: {integrity: sha512-uuuNWbCDv391SEeF2id/TvSAMUKU8U9347cbNcpRkwcv5sobBS6C5kXE0ZLuxVEUlWatr2kfsolrA10zdPogDg==} engines: {node: ^20.19.0 || >=22.12.0} - oxfmt@0.40.0: - resolution: {integrity: sha512-g0C3I7xUj4b4DcagevM9kgH6+pUHytikxUcn3/VUkvzTNaaXBeyZqb7IBsHwojeXm4mTBEC/aBjBTMVUkZwWUQ==} + oxfmt@0.43.0: + resolution: {integrity: sha512-KTYNG5ISfHSdmeZ25Xzb3qgz9EmQvkaGAxgBY/p38+ZiAet3uZeu7FnMwcSQJg152Qwl0wnYAxDc+Z/H6cvrwA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - oxlint-tsgolint@0.17.0: - resolution: {integrity: sha512-TdrKhDZCgEYqONFo/j+KvGan7/k3tP5Ouz88wCqpOvJtI2QmcLfGsm1fcMvDnTik48Jj6z83IJBqlkmK9DnY1A==} + oxlint-tsgolint@0.19.0: + resolution: {integrity: sha512-pSzUmDjMyjC8iUUZ7fCLo0D1iUaYIfodd/WIQ6Zra11YkjkUQk3BOFoW4I5ec6uZ/0s2FEmxtiZ7hiTXFRp1cg==} hasBin: true - oxlint@1.52.0: - resolution: {integrity: sha512-InLldD+6+3iHJGIrtU1W37UIpsg+xoGCemkZCuSQhxUO3evMX+L872ONvbECyRza9k7ScMCukJIK3Al/2ZMDnQ==} + oxlint@1.58.0: + resolution: {integrity: sha512-t4s9leczDMqlvOSjnbCQe7gtoLkWgBGZ7sBdCJ9EOj5IXFSG/X7OAzK4yuH4iW+4cAYe8kLFbC8tuYMwWZm+Cg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - oxlint-tsgolint: '>=0.15.0' + oxlint-tsgolint: '>=0.18.0' peerDependenciesMeta: oxlint-tsgolint: optional: true @@ -4226,6 +4152,10 @@ packages: path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -4243,6 +4173,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -4271,51 +4205,15 @@ packages: engines: {node: '>=18'} hasBin: true - postcss-import@15.1.0: - resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} - engines: {node: '>=14.0.0'} - peerDependencies: - postcss: ^8.0.0 - postcss-import@16.1.1: resolution: {integrity: sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ==} engines: {node: '>=18.0.0'} peerDependencies: postcss: ^8.0.0 - postcss-js@4.1.0: - resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} - engines: {node: ^12 || ^14 || >= 16} - peerDependencies: - postcss: ^8.4.21 - - postcss-load-config@6.0.1: - resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} - engines: {node: '>= 18'} - peerDependencies: - jiti: '>=1.21.0' - postcss: '>=8.0.9' - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - jiti: - optional: true - postcss: - optional: true - tsx: - optional: true - yaml: - optional: true - postcss-media-query-parser@0.2.3: resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} - postcss-nested@6.2.0: - resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 - postcss-nested@7.0.2: resolution: {integrity: sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw==} engines: {node: '>=18.0'} @@ -4328,10 +4226,6 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} - engines: {node: '>=4'} - postcss-selector-parser@7.1.1: resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} @@ -4343,16 +4237,8 @@ packages: resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} - posthog-js@1.268.8: - resolution: {integrity: sha512-BJiKK4MlUvs7ybnQcy1KkwAz+SZkE/wRLotetIoank5kbqZs8FLbeyozFvmmgx4aoMmaVymYBSmYphYjYQeidw==} - peerDependencies: - '@rrweb/types': 2.0.0-alpha.17 - rrweb-snapshot: 2.0.0-alpha.17 - peerDependenciesMeta: - '@rrweb/types': - optional: true - rrweb-snapshot: - optional: true + posthog-js@1.363.6: + resolution: {integrity: sha512-eQ+Ypml3JOEMQWt21XEea6J8vD77TNyoz4Yv/xxjlTRja+ilmJtQw/SuVAB3BobjgHKYUomXX7Fc4gH/zTVpbg==} preact@10.28.4: resolution: {integrity: sha512-uKFfOHWuSNpRFVTnljsCluEFq57OKT+0QdOiQo8XWnQ/pSvg7OpX5eNOejELXJMWy+BwM2nobz0FkvzmnpCNsQ==} @@ -4361,6 +4247,10 @@ packages: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} + protobufjs@7.5.4: + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + engines: {node: '>=12.0.0'} + punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} @@ -4368,6 +4258,9 @@ packages: resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} engines: {node: '>=0.6'} + query-selector-shadow-dom@1.0.1: + resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -4378,10 +4271,6 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -4416,18 +4305,18 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rolldown@1.0.0-rc.4: - resolution: {integrity: sha512-V2tPDUrY3WSevrvU2E41ijZlpF+5PbZu4giH+VpNraaadsJGHa4fR6IFwsocVwEXDoAdIv5qgPPxgrvKAOIPtA==} + rolldown@1.0.0-rc.12: + resolution: {integrity: sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rolldown@1.0.0-rc.9: - resolution: {integrity: sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==} + rolldown@1.0.0-rc.4: + resolution: {integrity: sha512-V2tPDUrY3WSevrvU2E41ijZlpF+5PbZu4giH+VpNraaadsJGHa4fR6IFwsocVwEXDoAdIv5qgPPxgrvKAOIPtA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} + rollup@4.60.0: + resolution: {integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -4448,6 +4337,11 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + sass@1.98.0: + resolution: {integrity: sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A==} + engines: {node: '>=14.0.0'} + hasBin: true + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -4457,6 +4351,14 @@ packages: engines: {node: '>=10'} hasBin: true + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -4532,34 +4434,17 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} - sucrase@3.35.1: - resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - tailwindcss@3.4.19: - resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} - engines: {node: '>=14.0.0'} - hasBin: true - - tailwindcss@4.2.1: - resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} + tailwindcss@4.2.2: + resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} - thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} - - thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - through2@4.0.2: resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} @@ -4578,17 +4463,14 @@ packages: resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} engines: {node: ^20.0.0 || >=22.0.0} - tinyrainbow@3.0.3: - resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - tsconfck@3.1.6: resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} engines: {node: ^18 || >=20} @@ -4599,9 +4481,6 @@ packages: typescript: optional: true - tslib@2.6.1: - resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -4616,8 +4495,8 @@ packages: typanion@3.14.0: resolution: {integrity: sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==} - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + typescript@6.0.2: + resolution: {integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==} engines: {node: '>=14.17'} hasBin: true @@ -4627,8 +4506,8 @@ packages: undici-types@7.8.0: resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} - undici@7.22.0: - resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} + undici@7.24.4: + resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} engines: {node: '>=20.18.1'} universal-user-agent@7.0.3: @@ -4656,10 +4535,10 @@ packages: peerDependencies: vite: '>=2.8' - vite-plugin-wasm@3.5.0: - resolution: {integrity: sha512-X5VWgCnqiQEGb+omhlBVsvTfxikKtoOgAzQ95+BZ8gQ+VfMHIjSHr0wyvXFQCa0eKQ0fKyaL0kWcEnYqBac4lQ==} + vite-plugin-wasm@3.6.0: + resolution: {integrity: sha512-mL/QPziiIA4RAA6DkaZZzOstdwbW5jO4Vz7Zenj0wieKWBlNvIvX5L5ljum9lcUX0ShNfBgCNLKTjNkRVVqcsw==} peerDependencies: - vite: ^2 || ^3 || ^4 || ^5 || ^6 || ^7 + vite: ^2 || ^3 || ^4 || ^5 || ^6 || ^7 || ^8 vite-tsconfig-paths@6.1.1: resolution: {integrity: sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==} @@ -4706,13 +4585,13 @@ packages: yaml: optional: true - vite@8.0.0: - resolution: {integrity: sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==} + vite@8.0.3: + resolution: {integrity: sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.0.0-alpha.31 + '@vitejs/devtools': ^0.1.0 esbuild: ^0.27.0 jiti: '>=1.21.0' less: ^4.0.0 @@ -4749,21 +4628,21 @@ packages: yaml: optional: true - vitest@4.1.0: - resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} + vitest@4.1.2: + resolution: {integrity: sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.0 - '@vitest/browser-preview': 4.1.0 - '@vitest/browser-webdriverio': 4.1.0 - '@vitest/ui': 4.1.0 + '@vitest/browser-playwright': 4.1.2 + '@vitest/browser-preview': 4.1.2 + '@vitest/browser-webdriverio': 4.1.2 + '@vitest/ui': 4.1.2 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -4791,8 +4670,13 @@ packages: weak-lru-cache@1.2.2: resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==} - web-vitals@4.2.4: - resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + web-vitals@5.1.0: + resolution: {integrity: sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} @@ -4835,21 +4719,19 @@ packages: snapshots: - '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2102.1(chokidar@5.0.0)': + '@angular-devkit/architect@0.2102.4(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.1(chokidar@5.0.0) + '@angular-devkit/core': 21.2.4(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/core@21.2.1(chokidar@5.0.0)': + '@angular-devkit/core@21.2.4(chokidar@5.0.0)': dependencies: ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) @@ -4860,17 +4742,17 @@ snapshots: optionalDependencies: chokidar: 5.0.0 - '@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))': + '@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))': dependencies: - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1) - tslib: 2.6.1 + '@angular/core': 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) + tslib: 2.8.1 - '@angular/build@21.2.1(@angular/compiler-cli@21.2.2(@angular/compiler@21.2.4)(typescript@5.9.3))(@angular/compiler@21.2.4)(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)))(@types/node@24.1.0)(chokidar@5.0.0)(jiti@2.6.1)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.9.3)(vitest@4.1.0(@types/node@24.1.0)(vite@8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)))': + '@angular/build@21.2.4(@angular/compiler-cli@21.2.6(@angular/compiler@21.2.6)(typescript@6.0.2))(@angular/compiler@21.2.6)(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(chokidar@5.0.0)(jiti@2.6.1)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(tslib@2.8.1)(tsx@4.21.0)(typescript@6.0.2)(vitest@4.1.2(@opentelemetry/api@1.9.0)(@types/node@24.1.0)(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)))': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2102.1(chokidar@5.0.0) - '@angular/compiler': 21.2.4 - '@angular/compiler-cli': 21.2.2(@angular/compiler@21.2.4)(typescript@5.9.3) + '@angular-devkit/architect': 0.2102.4(chokidar@5.0.0) + '@angular/compiler': 21.2.6 + '@angular/compiler-cli': 21.2.6(@angular/compiler@21.2.6)(typescript@6.0.2) '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 @@ -4888,24 +4770,26 @@ snapshots: parse5-html-rewriting-stream: 8.0.0 picomatch: 4.0.3 piscina: 5.1.4 - rolldown: 1.0.0-rc.4 + rolldown: 1.0.0-rc.4(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) sass: 1.97.3 semver: 7.7.4 source-map-support: 0.5.21 tinyglobby: 0.2.15 tslib: 2.8.1 - typescript: 5.9.3 - undici: 7.22.0 + typescript: 6.0.2 + undici: 7.24.4 vite: 7.3.1(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(tsx@4.21.0) watchpack: 2.5.1 optionalDependencies: - '@angular/core': 21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1) - '@angular/platform-browser': 21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/core': 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': 21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) lmdb: 3.5.1 postcss: 8.5.8 - tailwindcss: 4.2.1 - vitest: 4.1.0(@types/node@24.1.0)(vite@8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)) + tailwindcss: 4.2.2 + vitest: 4.1.2(@opentelemetry/api@1.9.0)(@types/node@24.1.0)(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)) transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' - '@types/node' - chokidar - jiti @@ -4918,139 +4802,91 @@ snapshots: - tsx - yaml - '@angular/cdk@20.2.14(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2)': - dependencies: - '@angular/common': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1) - parse5: 8.0.0 - rxjs: 7.8.2 - tslib: 2.6.1 - - '@angular/cdk@21.2.1(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2)': + '@angular/cdk@21.2.4(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) - '@angular/core': 21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1) - '@angular/platform-browser': 21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/common': 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/core': 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': 21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) parse5: 8.0.0 rxjs: 7.8.2 - tslib: 2.6.1 - - '@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2)': - dependencies: - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1) - rxjs: 7.8.2 - tslib: 2.6.1 + tslib: 2.8.1 - '@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2)': + '@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/core': 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) rxjs: 7.8.2 - tslib: 2.6.1 + tslib: 2.8.1 - '@angular/compiler-cli@21.2.2(@angular/compiler@21.2.4)(typescript@5.9.3)': + '@angular/compiler-cli@21.2.6(@angular/compiler@21.2.6)(typescript@6.0.2)': dependencies: - '@angular/compiler': 21.2.4 + '@angular/compiler': 21.2.6 '@babel/core': 7.29.0 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 5.0.0 convert-source-map: 1.9.0 reflect-metadata: 0.2.2 semver: 7.7.4 - tslib: 2.6.1 + tslib: 2.8.1 yargs: 18.0.0 optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.2 transitivePeerDependencies: - supports-color - '@angular/compiler@20.3.18': + '@angular/compiler@21.2.6': dependencies: - tslib: 2.6.1 - - '@angular/compiler@21.2.4': - dependencies: - tslib: 2.6.1 - - '@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)': - dependencies: - rxjs: 7.8.2 - tslib: 2.6.1 - optionalDependencies: - '@angular/compiler': 20.3.18 - zone.js: 0.16.1 + tslib: 2.8.1 - '@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)': + '@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)': dependencies: rxjs: 7.8.2 - tslib: 2.6.1 + tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 21.2.4 + '@angular/compiler': 21.2.6 zone.js: 0.16.1 - '@angular/forms@20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2)': - dependencies: - '@angular/common': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1) - '@angular/platform-browser': 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)) - rxjs: 7.8.2 - tslib: 2.6.1 - - '@angular/forms@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2)': + '@angular/forms@21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) - '@angular/core': 21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1) - '@angular/platform-browser': 21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/common': 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/core': 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': 21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 - tslib: 2.6.1 + tslib: 2.8.1 - '@angular/material@20.2.14(2f832b0b84a3673c875d1ecf350e328d)': + '@angular/material@21.2.4(2045f7bd5f6e95e11cc97fff9a766245)': dependencies: - '@angular/cdk': 20.2.14(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) - '@angular/common': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1) - '@angular/forms': 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) - '@angular/platform-browser': 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/cdk': 21.2.4(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@angular/common': 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/core': 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/forms': 21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@angular/platform-browser': 21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) rxjs: 7.8.2 - tslib: 2.6.1 + tslib: 2.8.1 - '@angular/platform-browser-dynamic@20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))': + '@angular/platform-browser-dynamic@21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@21.2.6)(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))': dependencies: - '@angular/common': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) - '@angular/compiler': 20.3.18 - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1) - '@angular/platform-browser': 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)) - tslib: 2.6.1 + '@angular/common': 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/compiler': 21.2.6 + '@angular/core': 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': 21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) + tslib: 2.8.1 - '@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))': + '@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))': dependencies: - '@angular/common': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1) - tslib: 2.6.1 + '@angular/common': 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/core': 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) + tslib: 2.8.1 optionalDependencies: - '@angular/animations': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)) - - '@angular/platform-browser@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))': - dependencies: - '@angular/common': 21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) - '@angular/core': 21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1) - tslib: 2.6.1 + '@angular/animations': 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) - '@angular/router@20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2)': + '@angular/router@21.2.6(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2)': dependencies: - '@angular/common': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1) - '@angular/platform-browser': 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/common': 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/core': 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': 21.2.6(@angular/animations@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1)) rxjs: 7.8.2 - tslib: 2.6.1 - - '@angular/router@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2)': - dependencies: - '@angular/common': 21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) - '@angular/core': 21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1) - '@angular/platform-browser': 21.2.2(@angular/common@21.2.2(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.4(@angular/compiler@21.2.4)(rxjs@7.8.2)(zone.js@0.16.1)) - rxjs: 7.8.2 - tslib: 2.6.1 + tslib: 2.8.1 '@babel/code-frame@7.29.0': dependencies: @@ -5066,8 +4902,8 @@ snapshots: '@babel/generator': 7.29.1 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.29.0 + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 @@ -5082,7 +4918,7 @@ snapshots: '@babel/generator@7.29.1': dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 @@ -5128,19 +4964,19 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.28.6': + '@babel/helpers@7.29.2': dependencies: '@babel/template': 7.28.6 '@babel/types': 7.29.0 - '@babel/parser@7.29.0': + '@babel/parser@7.29.2': dependencies: '@babel/types': 7.29.0 '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@babel/traverse@7.29.0': @@ -5148,7 +4984,7 @@ snapshots: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/template': 7.28.6 '@babel/types': 7.29.0 debug: 4.4.3 @@ -5163,17 +4999,17 @@ snapshots: '@emnapi/core@1.8.1': dependencies: '@emnapi/wasi-threads': 1.1.0 - tslib: 2.6.1 + tslib: 2.8.1 optional: true '@emnapi/runtime@1.8.1': dependencies: - tslib: 2.6.1 + tslib: 2.8.1 optional: true '@emnapi/wasi-threads@1.1.0': dependencies: - tslib: 2.6.1 + tslib: 2.8.1 optional: true '@esbuild/aix-ppc64@0.27.3': @@ -5466,15 +5302,15 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': optional: true - '@napi-rs/cli@3.5.1(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(node-addon-api@7.1.1)': + '@napi-rs/cli@3.6.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(node-addon-api@7.1.1)': dependencies: '@inquirer/prompts': 8.3.0(@types/node@24.1.0) - '@napi-rs/cross-toolchain': 1.0.3 - '@napi-rs/wasm-tools': 1.0.1 + '@napi-rs/cross-toolchain': 1.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + '@napi-rs/wasm-tools': 1.0.1(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) '@octokit/rest': 22.0.1 clipanion: 4.0.0-rc.4(typanion@3.14.0) colorette: 2.0.20 - emnapi: 1.8.1(node-addon-api@7.1.1) + emnapi: 1.9.1(node-addon-api@7.1.1) es-toolkit: 1.45.1 js-yaml: 4.1.1 obug: 2.1.1 @@ -5483,6 +5319,7 @@ snapshots: optionalDependencies: '@emnapi/runtime': 1.8.1 transitivePeerDependencies: + - '@emnapi/core' - '@napi-rs/cross-toolchain-arm64-target-aarch64' - '@napi-rs/cross-toolchain-arm64-target-armv7' - '@napi-rs/cross-toolchain-arm64-target-ppc64le' @@ -5497,12 +5334,14 @@ snapshots: - node-addon-api - supports-color - '@napi-rs/cross-toolchain@1.0.3': + '@napi-rs/cross-toolchain@1.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': dependencies: - '@napi-rs/lzma': 1.4.5 - '@napi-rs/tar': 1.1.0 + '@napi-rs/lzma': 1.4.5(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + '@napi-rs/tar': 1.1.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) debug: 4.4.3 transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' - supports-color '@napi-rs/lzma-android-arm-eabi@1.4.5': @@ -5544,9 +5383,12 @@ snapshots: '@napi-rs/lzma-linux-x64-musl@1.4.5': optional: true - '@napi-rs/lzma-wasm32-wasi@1.4.5': + '@napi-rs/lzma-wasm32-wasi@1.4.5(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.1 + '@napi-rs/wasm-runtime': 1.1.2(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' optional: true '@napi-rs/lzma-win32-arm64-msvc@1.4.5': @@ -5558,7 +5400,7 @@ snapshots: '@napi-rs/lzma-win32-x64-msvc@1.4.5': optional: true - '@napi-rs/lzma@1.4.5': + '@napi-rs/lzma@1.4.5(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': optionalDependencies: '@napi-rs/lzma-android-arm-eabi': 1.4.5 '@napi-rs/lzma-android-arm64': 1.4.5 @@ -5573,10 +5415,13 @@ snapshots: '@napi-rs/lzma-linux-s390x-gnu': 1.4.5 '@napi-rs/lzma-linux-x64-gnu': 1.4.5 '@napi-rs/lzma-linux-x64-musl': 1.4.5 - '@napi-rs/lzma-wasm32-wasi': 1.4.5 + '@napi-rs/lzma-wasm32-wasi': 1.4.5(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) '@napi-rs/lzma-win32-arm64-msvc': 1.4.5 '@napi-rs/lzma-win32-ia32-msvc': 1.4.5 '@napi-rs/lzma-win32-x64-msvc': 1.4.5 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' '@napi-rs/nice-android-arm-eabi@1.1.1': optional: true @@ -5686,9 +5531,12 @@ snapshots: '@napi-rs/tar-linux-x64-musl@1.1.0': optional: true - '@napi-rs/tar-wasm32-wasi@1.1.0': + '@napi-rs/tar-wasm32-wasi@1.1.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.1 + '@napi-rs/wasm-runtime': 1.1.2(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' optional: true '@napi-rs/tar-win32-arm64-msvc@1.1.0': @@ -5700,7 +5548,7 @@ snapshots: '@napi-rs/tar-win32-x64-msvc@1.1.0': optional: true - '@napi-rs/tar@1.1.0': + '@napi-rs/tar@1.1.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': optionalDependencies: '@napi-rs/tar-android-arm-eabi': 1.1.0 '@napi-rs/tar-android-arm64': 1.1.0 @@ -5714,12 +5562,15 @@ snapshots: '@napi-rs/tar-linux-s390x-gnu': 1.1.0 '@napi-rs/tar-linux-x64-gnu': 1.1.0 '@napi-rs/tar-linux-x64-musl': 1.1.0 - '@napi-rs/tar-wasm32-wasi': 1.1.0 + '@napi-rs/tar-wasm32-wasi': 1.1.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) '@napi-rs/tar-win32-arm64-msvc': 1.1.0 '@napi-rs/tar-win32-ia32-msvc': 1.1.0 '@napi-rs/tar-win32-x64-msvc': 1.1.0 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' - '@napi-rs/wasm-runtime@1.1.1': + '@napi-rs/wasm-runtime@1.1.2(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': dependencies: '@emnapi/core': 1.8.1 '@emnapi/runtime': 1.8.1 @@ -5753,9 +5604,12 @@ snapshots: '@napi-rs/wasm-tools-linux-x64-musl@1.0.1': optional: true - '@napi-rs/wasm-tools-wasm32-wasi@1.0.1': + '@napi-rs/wasm-tools-wasm32-wasi@1.0.1(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.1 + '@napi-rs/wasm-runtime': 1.1.2(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' optional: true '@napi-rs/wasm-tools-win32-arm64-msvc@1.0.1': @@ -5767,7 +5621,7 @@ snapshots: '@napi-rs/wasm-tools-win32-x64-msvc@1.0.1': optional: true - '@napi-rs/wasm-tools@1.0.1': + '@napi-rs/wasm-tools@1.0.1(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': optionalDependencies: '@napi-rs/wasm-tools-android-arm-eabi': 1.0.1 '@napi-rs/wasm-tools-android-arm64': 1.0.1 @@ -5778,10 +5632,17 @@ snapshots: '@napi-rs/wasm-tools-linux-arm64-musl': 1.0.1 '@napi-rs/wasm-tools-linux-x64-gnu': 1.0.1 '@napi-rs/wasm-tools-linux-x64-musl': 1.0.1 - '@napi-rs/wasm-tools-wasm32-wasi': 1.0.1 + '@napi-rs/wasm-tools-wasm32-wasi': 1.0.1(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) '@napi-rs/wasm-tools-win32-arm64-msvc': 1.0.1 '@napi-rs/wasm-tools-win32-ia32-msvc': 1.0.1 '@napi-rs/wasm-tools-win32-x64-msvc': 1.0.1 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + '@noble/ed25519@3.0.1': {} + + '@noble/hashes@2.0.1': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -5858,9 +5719,88 @@ snapshots: dependencies: '@octokit/openapi-types': 27.0.0 - '@oxc-node/cli@0.0.35': + '@opentelemetry/api-logs@0.208.0': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api@1.9.0': {} + + '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.40.0 + + '@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.40.0 + + '@opentelemetry/exporter-logs-otlp-http@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-exporter-base@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-transformer@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) + protobufjs: 7.5.4 + + '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 + + '@opentelemetry/resources@2.6.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 + + '@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-metrics@2.2.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0)': dependencies: - '@oxc-node/core': 0.0.35 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 + + '@opentelemetry/semantic-conventions@1.40.0': {} + + '@oxc-node/cli@0.0.35(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': + dependencies: + '@oxc-node/core': 0.0.35(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' '@oxc-node/core-android-arm-eabi@0.0.35': optional: true @@ -5901,9 +5841,12 @@ snapshots: '@oxc-node/core-openharmony-arm64@0.0.35': optional: true - '@oxc-node/core-wasm32-wasi@0.0.35': + '@oxc-node/core-wasm32-wasi@0.0.35(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.1 + '@napi-rs/wasm-runtime': 1.1.2(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' optional: true '@oxc-node/core-win32-arm64-msvc@0.0.35': @@ -5915,7 +5858,7 @@ snapshots: '@oxc-node/core-win32-x64-msvc@0.0.35': optional: true - '@oxc-node/core@0.0.35': + '@oxc-node/core@0.0.35(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': dependencies: pirates: 4.0.7 optionalDependencies: @@ -5932,273 +5875,280 @@ snapshots: '@oxc-node/core-linux-x64-gnu': 0.0.35 '@oxc-node/core-linux-x64-musl': 0.0.35 '@oxc-node/core-openharmony-arm64': 0.0.35 - '@oxc-node/core-wasm32-wasi': 0.0.35 + '@oxc-node/core-wasm32-wasi': 0.0.35(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) '@oxc-node/core-win32-arm64-msvc': 0.0.35 '@oxc-node/core-win32-ia32-msvc': 0.0.35 '@oxc-node/core-win32-x64-msvc': 0.0.35 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' - '@oxc-parser/binding-android-arm-eabi@0.120.0': + '@oxc-parser/binding-android-arm-eabi@0.123.0': optional: true - '@oxc-parser/binding-android-arm64@0.120.0': + '@oxc-parser/binding-android-arm64@0.123.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.120.0': + '@oxc-parser/binding-darwin-arm64@0.123.0': optional: true - '@oxc-parser/binding-darwin-x64@0.120.0': + '@oxc-parser/binding-darwin-x64@0.123.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.120.0': + '@oxc-parser/binding-freebsd-x64@0.123.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.123.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.123.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.120.0': + '@oxc-parser/binding-linux-arm64-gnu@0.123.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.120.0': + '@oxc-parser/binding-linux-arm64-musl@0.123.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.123.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.123.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.120.0': + '@oxc-parser/binding-linux-riscv64-musl@0.123.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.120.0': + '@oxc-parser/binding-linux-s390x-gnu@0.123.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.120.0': + '@oxc-parser/binding-linux-x64-gnu@0.123.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.120.0': + '@oxc-parser/binding-linux-x64-musl@0.123.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.120.0': + '@oxc-parser/binding-openharmony-arm64@0.123.0': optional: true - '@oxc-parser/binding-wasm32-wasi@0.120.0': + '@oxc-parser/binding-wasm32-wasi@0.123.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.1 + '@napi-rs/wasm-runtime': 1.1.2(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.120.0': + '@oxc-parser/binding-win32-arm64-msvc@0.123.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.120.0': + '@oxc-parser/binding-win32-ia32-msvc@0.123.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.120.0': + '@oxc-parser/binding-win32-x64-msvc@0.123.0': optional: true - '@oxc-project/runtime@0.115.0': {} - '@oxc-project/types@0.113.0': {} - '@oxc-project/types@0.115.0': {} + '@oxc-project/types@0.122.0': {} - '@oxc-project/types@0.120.0': {} + '@oxc-project/types@0.123.0': {} - '@oxc-transform/binding-android-arm-eabi@0.120.0': + '@oxc-transform/binding-android-arm-eabi@0.123.0': optional: true - '@oxc-transform/binding-android-arm64@0.120.0': + '@oxc-transform/binding-android-arm64@0.123.0': optional: true - '@oxc-transform/binding-darwin-arm64@0.120.0': + '@oxc-transform/binding-darwin-arm64@0.123.0': optional: true - '@oxc-transform/binding-darwin-x64@0.120.0': + '@oxc-transform/binding-darwin-x64@0.123.0': optional: true - '@oxc-transform/binding-freebsd-x64@0.120.0': + '@oxc-transform/binding-freebsd-x64@0.123.0': optional: true - '@oxc-transform/binding-linux-arm-gnueabihf@0.120.0': + '@oxc-transform/binding-linux-arm-gnueabihf@0.123.0': optional: true - '@oxc-transform/binding-linux-arm-musleabihf@0.120.0': + '@oxc-transform/binding-linux-arm-musleabihf@0.123.0': optional: true - '@oxc-transform/binding-linux-arm64-gnu@0.120.0': + '@oxc-transform/binding-linux-arm64-gnu@0.123.0': optional: true - '@oxc-transform/binding-linux-arm64-musl@0.120.0': + '@oxc-transform/binding-linux-arm64-musl@0.123.0': optional: true - '@oxc-transform/binding-linux-ppc64-gnu@0.120.0': + '@oxc-transform/binding-linux-ppc64-gnu@0.123.0': optional: true - '@oxc-transform/binding-linux-riscv64-gnu@0.120.0': + '@oxc-transform/binding-linux-riscv64-gnu@0.123.0': optional: true - '@oxc-transform/binding-linux-riscv64-musl@0.120.0': + '@oxc-transform/binding-linux-riscv64-musl@0.123.0': optional: true - '@oxc-transform/binding-linux-s390x-gnu@0.120.0': + '@oxc-transform/binding-linux-s390x-gnu@0.123.0': optional: true - '@oxc-transform/binding-linux-x64-gnu@0.120.0': + '@oxc-transform/binding-linux-x64-gnu@0.123.0': optional: true - '@oxc-transform/binding-linux-x64-musl@0.120.0': + '@oxc-transform/binding-linux-x64-musl@0.123.0': optional: true - '@oxc-transform/binding-openharmony-arm64@0.120.0': + '@oxc-transform/binding-openharmony-arm64@0.123.0': optional: true - '@oxc-transform/binding-wasm32-wasi@0.120.0': + '@oxc-transform/binding-wasm32-wasi@0.123.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.1 + '@napi-rs/wasm-runtime': 1.1.2(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' optional: true - '@oxc-transform/binding-win32-arm64-msvc@0.120.0': + '@oxc-transform/binding-win32-arm64-msvc@0.123.0': optional: true - '@oxc-transform/binding-win32-ia32-msvc@0.120.0': + '@oxc-transform/binding-win32-ia32-msvc@0.123.0': optional: true - '@oxc-transform/binding-win32-x64-msvc@0.120.0': + '@oxc-transform/binding-win32-x64-msvc@0.123.0': optional: true - '@oxfmt/binding-android-arm-eabi@0.40.0': + '@oxfmt/binding-android-arm-eabi@0.43.0': optional: true - '@oxfmt/binding-android-arm64@0.40.0': + '@oxfmt/binding-android-arm64@0.43.0': optional: true - '@oxfmt/binding-darwin-arm64@0.40.0': + '@oxfmt/binding-darwin-arm64@0.43.0': optional: true - '@oxfmt/binding-darwin-x64@0.40.0': + '@oxfmt/binding-darwin-x64@0.43.0': optional: true - '@oxfmt/binding-freebsd-x64@0.40.0': + '@oxfmt/binding-freebsd-x64@0.43.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.40.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.43.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.40.0': + '@oxfmt/binding-linux-arm-musleabihf@0.43.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.40.0': + '@oxfmt/binding-linux-arm64-gnu@0.43.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.40.0': + '@oxfmt/binding-linux-arm64-musl@0.43.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.40.0': + '@oxfmt/binding-linux-ppc64-gnu@0.43.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.40.0': + '@oxfmt/binding-linux-riscv64-gnu@0.43.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.40.0': + '@oxfmt/binding-linux-riscv64-musl@0.43.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.40.0': + '@oxfmt/binding-linux-s390x-gnu@0.43.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.40.0': + '@oxfmt/binding-linux-x64-gnu@0.43.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.40.0': + '@oxfmt/binding-linux-x64-musl@0.43.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.40.0': + '@oxfmt/binding-openharmony-arm64@0.43.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.40.0': + '@oxfmt/binding-win32-arm64-msvc@0.43.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.40.0': + '@oxfmt/binding-win32-ia32-msvc@0.43.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.40.0': + '@oxfmt/binding-win32-x64-msvc@0.43.0': optional: true - '@oxlint-tsgolint/darwin-arm64@0.17.0': + '@oxlint-tsgolint/darwin-arm64@0.19.0': optional: true - '@oxlint-tsgolint/darwin-x64@0.17.0': + '@oxlint-tsgolint/darwin-x64@0.19.0': optional: true - '@oxlint-tsgolint/linux-arm64@0.17.0': + '@oxlint-tsgolint/linux-arm64@0.19.0': optional: true - '@oxlint-tsgolint/linux-x64@0.17.0': + '@oxlint-tsgolint/linux-x64@0.19.0': optional: true - '@oxlint-tsgolint/win32-arm64@0.17.0': + '@oxlint-tsgolint/win32-arm64@0.19.0': optional: true - '@oxlint-tsgolint/win32-x64@0.17.0': + '@oxlint-tsgolint/win32-x64@0.19.0': optional: true - '@oxlint/binding-android-arm-eabi@1.52.0': + '@oxlint/binding-android-arm-eabi@1.58.0': optional: true - '@oxlint/binding-android-arm64@1.52.0': + '@oxlint/binding-android-arm64@1.58.0': optional: true - '@oxlint/binding-darwin-arm64@1.52.0': + '@oxlint/binding-darwin-arm64@1.58.0': optional: true - '@oxlint/binding-darwin-x64@1.52.0': + '@oxlint/binding-darwin-x64@1.58.0': optional: true - '@oxlint/binding-freebsd-x64@1.52.0': + '@oxlint/binding-freebsd-x64@1.58.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.52.0': + '@oxlint/binding-linux-arm-gnueabihf@1.58.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.52.0': + '@oxlint/binding-linux-arm-musleabihf@1.58.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.52.0': + '@oxlint/binding-linux-arm64-gnu@1.58.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.52.0': + '@oxlint/binding-linux-arm64-musl@1.58.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.52.0': + '@oxlint/binding-linux-ppc64-gnu@1.58.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.52.0': + '@oxlint/binding-linux-riscv64-gnu@1.58.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.52.0': + '@oxlint/binding-linux-riscv64-musl@1.58.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.52.0': + '@oxlint/binding-linux-s390x-gnu@1.58.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.52.0': + '@oxlint/binding-linux-x64-gnu@1.58.0': optional: true - '@oxlint/binding-linux-x64-musl@1.52.0': + '@oxlint/binding-linux-x64-musl@1.58.0': optional: true - '@oxlint/binding-openharmony-arm64@1.52.0': + '@oxlint/binding-openharmony-arm64@1.58.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.52.0': + '@oxlint/binding-win32-arm64-msvc@1.58.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.52.0': + '@oxlint/binding-win32-ia32-msvc@1.58.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.52.0': + '@oxlint/binding-win32-x64-msvc@1.58.0': optional: true '@parcel/watcher-android-arm64@2.5.6': @@ -6245,7 +6195,7 @@ snapshots: detect-libc: 2.1.2 is-glob: 4.0.3 node-addon-api: 7.1.1 - picomatch: 4.0.3 + picomatch: 4.0.4 optionalDependencies: '@parcel/watcher-android-arm64': 2.5.6 '@parcel/watcher-darwin-arm64': 2.5.6 @@ -6516,191 +6466,224 @@ snapshots: dependencies: playwright: 1.58.2 - '@portabletext/to-html@2.0.17': + '@portabletext/to-html@5.0.2': + dependencies: + '@portabletext/toolkit': 5.0.2 + '@portabletext/types': 4.0.2 + + '@portabletext/toolkit@5.0.2': + dependencies: + '@portabletext/types': 4.0.2 + + '@portabletext/types@4.0.2': {} + + '@posthog/core@1.24.1': dependencies: - '@portabletext/toolkit': 2.0.18 - '@portabletext/types': 2.0.15 + cross-spawn: 7.0.6 + + '@posthog/types@1.363.6': {} + + '@protobufjs/aspromise@1.1.2': {} - '@portabletext/toolkit@2.0.18': + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': dependencies: - '@portabletext/types': 2.0.15 + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} - '@portabletext/types@2.0.15': {} + '@protobufjs/pool@1.1.0': {} - '@posthog/core@1.2.2': {} + '@protobufjs/utf8@1.1.0': {} + + '@rolldown/binding-android-arm64@1.0.0-rc.12': + optional: true '@rolldown/binding-android-arm64@1.0.0-rc.4': optional: true - '@rolldown/binding-android-arm64@1.0.0-rc.9': + '@rolldown/binding-darwin-arm64@1.0.0-rc.12': optional: true '@rolldown/binding-darwin-arm64@1.0.0-rc.4': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.9': + '@rolldown/binding-darwin-x64@1.0.0-rc.12': optional: true '@rolldown/binding-darwin-x64@1.0.0-rc.4': optional: true - '@rolldown/binding-darwin-x64@1.0.0-rc.9': + '@rolldown/binding-freebsd-x64@1.0.0-rc.12': optional: true '@rolldown/binding-freebsd-x64@1.0.0-rc.4': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-rc.9': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12': optional: true '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.4': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.9': + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12': optional: true '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.4': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.9': + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.12': optional: true '@rolldown/binding-linux-arm64-musl@1.0.0-rc.4': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.9': + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.9': + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.9': + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.12': optional: true '@rolldown/binding-linux-x64-gnu@1.0.0-rc.4': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.9': + '@rolldown/binding-linux-x64-musl@1.0.0-rc.12': optional: true '@rolldown/binding-linux-x64-musl@1.0.0-rc.4': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-rc.9': + '@rolldown/binding-openharmony-arm64@1.0.0-rc.12': optional: true '@rolldown/binding-openharmony-arm64@1.0.0-rc.4': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-rc.9': + '@rolldown/binding-wasm32-wasi@1.0.0-rc.12(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': + dependencies: + '@napi-rs/wasm-runtime': 1.1.2(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.4': + '@rolldown/binding-wasm32-wasi@1.0.0-rc.4(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.1 + '@napi-rs/wasm-runtime': 1.1.2(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.9': - dependencies: - '@napi-rs/wasm-runtime': 1.1.1 + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12': optional: true '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.4': optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.9': + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.12': optional: true '@rolldown/binding-win32-x64-msvc@1.0.0-rc.4': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.9': - optional: true + '@rolldown/pluginutils@1.0.0-rc.12': {} '@rolldown/pluginutils@1.0.0-rc.4': {} - '@rolldown/pluginutils@1.0.0-rc.9': {} - - '@rollup/plugin-virtual@3.0.2(rollup@4.59.0)': + '@rollup/plugin-virtual@3.0.2(rollup@4.60.0)': optionalDependencies: - rollup: 4.59.0 + rollup: 4.60.0 - '@rollup/rollup-android-arm-eabi@4.59.0': + '@rollup/rollup-android-arm-eabi@4.60.0': optional: true - '@rollup/rollup-android-arm64@4.59.0': + '@rollup/rollup-android-arm64@4.60.0': optional: true - '@rollup/rollup-darwin-arm64@4.59.0': + '@rollup/rollup-darwin-arm64@4.60.0': optional: true - '@rollup/rollup-darwin-x64@4.59.0': + '@rollup/rollup-darwin-x64@4.60.0': optional: true - '@rollup/rollup-freebsd-arm64@4.59.0': + '@rollup/rollup-freebsd-arm64@4.60.0': optional: true - '@rollup/rollup-freebsd-x64@4.59.0': + '@rollup/rollup-freebsd-x64@4.60.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + '@rollup/rollup-linux-arm-gnueabihf@4.60.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.59.0': + '@rollup/rollup-linux-arm-musleabihf@4.60.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.59.0': + '@rollup/rollup-linux-arm64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.59.0': + '@rollup/rollup-linux-arm64-musl@4.60.0': optional: true - '@rollup/rollup-linux-loong64-gnu@4.59.0': + '@rollup/rollup-linux-loong64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-loong64-musl@4.59.0': + '@rollup/rollup-linux-loong64-musl@4.60.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.59.0': + '@rollup/rollup-linux-ppc64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-ppc64-musl@4.59.0': + '@rollup/rollup-linux-ppc64-musl@4.60.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.59.0': + '@rollup/rollup-linux-riscv64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.59.0': + '@rollup/rollup-linux-riscv64-musl@4.60.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.59.0': + '@rollup/rollup-linux-s390x-gnu@4.60.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.59.0': + '@rollup/rollup-linux-x64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-x64-musl@4.59.0': + '@rollup/rollup-linux-x64-musl@4.60.0': optional: true - '@rollup/rollup-openbsd-x64@4.59.0': + '@rollup/rollup-openbsd-x64@4.60.0': optional: true - '@rollup/rollup-openharmony-arm64@4.59.0': + '@rollup/rollup-openharmony-arm64@4.60.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.59.0': + '@rollup/rollup-win32-arm64-msvc@4.60.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.59.0': + '@rollup/rollup-win32-ia32-msvc@4.60.0': optional: true - '@rollup/rollup-win32-x64-gnu@4.59.0': + '@rollup/rollup-win32-x64-gnu@4.60.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.59.0': + '@rollup/rollup-win32-x64-msvc@4.60.0': optional: true - '@sanity/asset-utils@1.3.2': {} + '@sanity/asset-utils@2.3.0': {} '@sanity/client@7.17.0': dependencies: @@ -6718,11 +6701,18 @@ snapshots: event-source-polyfill: 1.0.31 eventsource: 2.0.2 - '@sanity/image-url@1.0.2': {} + '@sanity/image-url@2.0.3': + dependencies: + '@sanity/signed-urls': 2.0.2 '@sanity/media-library-types@1.2.0': {} - '@sanity/types@3.99.0(@types/react@19.2.14)': + '@sanity/signed-urls@2.0.2': + dependencies: + '@noble/ed25519': 3.0.1 + '@noble/hashes': 2.0.1 + + '@sanity/types@5.18.0(@types/react@19.2.14)': dependencies: '@sanity/client': 7.17.0 '@sanity/media-library-types': 1.2.0 @@ -6786,81 +6776,81 @@ snapshots: '@swc/wasm@1.15.18': {} - '@tailwindcss/container-queries@0.1.1(tailwindcss@3.4.19(tsx@4.21.0))': + '@tailwindcss/container-queries@0.1.1(tailwindcss@4.2.2)': dependencies: - tailwindcss: 3.4.19(tsx@4.21.0) + tailwindcss: 4.2.2 - '@tailwindcss/node@4.2.1': + '@tailwindcss/node@4.2.2': dependencies: '@jridgewell/remapping': 2.3.5 enhanced-resolve: 5.20.0 jiti: 2.6.1 - lightningcss: 1.31.1 + lightningcss: 1.32.0 magic-string: 0.30.21 source-map-js: 1.2.1 - tailwindcss: 4.2.1 + tailwindcss: 4.2.2 - '@tailwindcss/oxide-android-arm64@4.2.1': + '@tailwindcss/oxide-android-arm64@4.2.2': optional: true - '@tailwindcss/oxide-darwin-arm64@4.2.1': + '@tailwindcss/oxide-darwin-arm64@4.2.2': optional: true - '@tailwindcss/oxide-darwin-x64@4.2.1': + '@tailwindcss/oxide-darwin-x64@4.2.2': optional: true - '@tailwindcss/oxide-freebsd-x64@4.2.1': + '@tailwindcss/oxide-freebsd-x64@4.2.2': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.2.1': + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.2.1': + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.2.1': + '@tailwindcss/oxide-linux-x64-musl@4.2.2': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.2.1': + '@tailwindcss/oxide-wasm32-wasi@4.2.2': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.2.1': + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': optional: true - '@tailwindcss/oxide@4.2.1': + '@tailwindcss/oxide@4.2.2': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.2.1 - '@tailwindcss/oxide-darwin-arm64': 4.2.1 - '@tailwindcss/oxide-darwin-x64': 4.2.1 - '@tailwindcss/oxide-freebsd-x64': 4.2.1 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.1 - '@tailwindcss/oxide-linux-arm64-gnu': 4.2.1 - '@tailwindcss/oxide-linux-arm64-musl': 4.2.1 - '@tailwindcss/oxide-linux-x64-gnu': 4.2.1 - '@tailwindcss/oxide-linux-x64-musl': 4.2.1 - '@tailwindcss/oxide-wasm32-wasi': 4.2.1 - '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 - '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 - - '@tailwindcss/vite@4.2.1(vite@8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0))': - dependencies: - '@tailwindcss/node': 4.2.1 - '@tailwindcss/oxide': 4.2.1 - tailwindcss: 4.2.1 - vite: 8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0) + '@tailwindcss/oxide-android-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-x64': 4.2.2 + '@tailwindcss/oxide-freebsd-x64': 4.2.2 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.2 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.2 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-x64-musl': 4.2.2 + '@tailwindcss/oxide-wasm32-wasi': 4.2.2 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 + + '@tailwindcss/vite@4.2.2(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0))': + dependencies: + '@tailwindcss/node': 4.2.2 + '@tailwindcss/oxide': 4.2.2 + tailwindcss: 4.2.2 + vite: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0) '@tybys/wasm-util@0.10.1': dependencies: - tslib: 2.6.1 + tslib: 2.8.1 optional: true '@types/chai@5.2.3': @@ -6902,50 +6892,62 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/trusted-types@2.0.7': + optional: true + '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.1(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(tsx@4.21.0))': dependencies: vite: 7.3.1(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(tsx@4.21.0) - '@vitest/expect@4.1.0': + '@vitest/expect@4.1.2': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 chai: 6.2.2 - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.0(vite@8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0))': + '@vitest/mocker@4.1.2(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0))': dependencies: - '@vitest/spy': 4.1.0 + '@vitest/spy': 4.1.2 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0) + vite: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0) + optional: true - '@vitest/pretty-format@4.1.0': + '@vitest/mocker@4.1.2(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0))': dependencies: - tinyrainbow: 3.0.3 + '@vitest/spy': 4.1.2 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0) + + '@vitest/pretty-format@4.1.2': + dependencies: + tinyrainbow: 3.1.0 - '@vitest/runner@4.1.0': + '@vitest/runner@4.1.2': dependencies: - '@vitest/utils': 4.1.0 + '@vitest/utils': 4.1.2 pathe: 2.0.3 - '@vitest/snapshot@4.1.0': + '@vitest/snapshot@4.1.2': dependencies: - '@vitest/pretty-format': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/pretty-format': 4.1.2 + '@vitest/utils': 4.1.2 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.0': {} + '@vitest/spy@4.1.2': {} - '@vitest/utils@4.1.0': + '@vitest/utils@4.1.2': dependencies: - '@vitest/pretty-format': 4.1.0 + '@vitest/pretty-format': 4.1.2 convert-source-map: 2.0.0 - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 agent-base@7.1.4: {} @@ -6974,15 +6976,6 @@ snapshots: ansi-styles@6.2.3: {} - any-promise@1.3.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - arg@5.0.2: {} - argparse@2.0.1: {} assertion-error@2.0.1: {} @@ -7014,8 +7007,6 @@ snapshots: before-after-hook@4.0.0: {} - binary-extensions@2.3.0: {} - boolbase@1.0.0: {} braces@3.0.3: @@ -7047,26 +7038,12 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001777: {} chai@6.2.2: {} chardet@2.1.1: {} - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -7104,15 +7081,19 @@ snapshots: colorette@2.0.20: {} - commander@4.1.1: {} - convert-source-map@1.9.0: {} convert-source-map@2.0.0: {} cookieconsent@3.1.1: {} - core-js@3.48.0: {} + core-js@3.49.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 css-select@6.0.0: dependencies: @@ -7150,11 +7131,7 @@ snapshots: detect-libc@2.1.2: {} - didyoumean@1.2.2: {} - - diff@8.0.3: {} - - dlv@1.1.3: {} + diff@8.0.4: {} dom-serializer@2.0.0: dependencies: @@ -7168,6 +7145,10 @@ snapshots: dependencies: domelementtype: 2.3.0 + dompurify@3.3.3: + optionalDependencies: + '@types/trusted-types': 2.0.7 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -7184,7 +7165,7 @@ snapshots: electron-to-chromium@1.5.307: {} - emnapi@1.8.1(node-addon-api@7.1.1): + emnapi@1.9.1(node-addon-api@7.1.1): optionalDependencies: node-addon-api: 7.1.1 @@ -7290,9 +7271,9 @@ snapshots: dependencies: reusify: 1.1.0 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.4 fflate@0.4.8: {} @@ -7357,10 +7338,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - glob-to-regexp@0.4.1: {} globrex@0.1.2: {} @@ -7403,10 +7380,6 @@ snapshots: dependencies: '@interactjs/types': 1.10.27 - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -7427,6 +7400,8 @@ snapshots: is-retry-allowed@2.2.0: {} + isexe@2.0.0: {} + ismobilejs@1.1.1: {} istanbul-lib-coverage@3.2.2: {} @@ -7434,15 +7409,13 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.29.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.7.4 transitivePeerDependencies: - supports-color - jiti@1.21.7: {} - jiti@2.6.1: {} js-tokens@4.0.0: {} @@ -7461,88 +7434,39 @@ snapshots: jsonc-parser@3.3.1: {} - lightningcss-android-arm64@1.31.1: - optional: true - lightningcss-android-arm64@1.32.0: optional: true - lightningcss-darwin-arm64@1.31.1: - optional: true - lightningcss-darwin-arm64@1.32.0: optional: true - lightningcss-darwin-x64@1.31.1: - optional: true - lightningcss-darwin-x64@1.32.0: optional: true - lightningcss-freebsd-x64@1.31.1: - optional: true - lightningcss-freebsd-x64@1.32.0: optional: true - lightningcss-linux-arm-gnueabihf@1.31.1: - optional: true - lightningcss-linux-arm-gnueabihf@1.32.0: optional: true - lightningcss-linux-arm64-gnu@1.31.1: - optional: true - lightningcss-linux-arm64-gnu@1.32.0: optional: true - lightningcss-linux-arm64-musl@1.31.1: - optional: true - lightningcss-linux-arm64-musl@1.32.0: optional: true - lightningcss-linux-x64-gnu@1.31.1: - optional: true - lightningcss-linux-x64-gnu@1.32.0: optional: true - lightningcss-linux-x64-musl@1.31.1: - optional: true - lightningcss-linux-x64-musl@1.32.0: optional: true - lightningcss-win32-arm64-msvc@1.31.1: - optional: true - lightningcss-win32-arm64-msvc@1.32.0: optional: true - lightningcss-win32-x64-msvc@1.31.1: - optional: true - lightningcss-win32-x64-msvc@1.32.0: optional: true - lightningcss@1.31.1: - dependencies: - detect-libc: 2.1.2 - optionalDependencies: - lightningcss-android-arm64: 1.31.1 - lightningcss-darwin-arm64: 1.31.1 - lightningcss-darwin-x64: 1.31.1 - lightningcss-freebsd-x64: 1.31.1 - lightningcss-linux-arm-gnueabihf: 1.31.1 - lightningcss-linux-arm64-gnu: 1.31.1 - lightningcss-linux-arm64-musl: 1.31.1 - lightningcss-linux-x64-gnu: 1.31.1 - lightningcss-linux-x64-musl: 1.31.1 - lightningcss-win32-arm64-msvc: 1.31.1 - lightningcss-win32-x64-msvc: 1.31.1 - lightningcss@1.32.0: dependencies: detect-libc: 2.1.2 @@ -7559,10 +7483,6 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 - lilconfig@3.1.3: {} - - lines-and-columns@1.2.4: {} - listr2@9.0.5: dependencies: cli-truncate: 5.2.0 @@ -7575,7 +7495,7 @@ snapshots: lmdb@3.5.1: dependencies: '@harperfast/extended-iterable': 1.0.3 - msgpackr: 1.11.8 + msgpackr: 1.11.9 node-addon-api: 6.1.0 node-gyp-build-optional-packages: 5.2.2 ordered-binary: 1.6.1 @@ -7598,6 +7518,8 @@ snapshots: strip-ansi: 7.2.0 wrap-ansi: 9.0.2 + long@5.3.2: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -7635,7 +7557,7 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 optional: true - msgpackr@1.11.8: + msgpackr@1.11.9: optionalDependencies: msgpackr-extract: 3.0.3 optional: true @@ -7644,21 +7566,15 @@ snapshots: mute-stream@3.0.0: {} - mz@2.7.0: - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - nanoid@3.3.11: {} - ngx-cookieconsent@4.0.2(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(cookieconsent@3.1.1)(rxjs@7.8.2): + ngx-cookieconsent@8.0.0(@angular/common@21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(cookieconsent@3.1.1)(rxjs@7.8.2): dependencies: - '@angular/common': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/common': 21.2.6(@angular/core@21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/core': 21.2.6(@angular/compiler@21.2.6)(rxjs@7.8.2)(zone.js@0.16.1) cookieconsent: 3.1.1 rxjs: 7.8.2 - tslib: 2.6.1 + tslib: 2.8.1 node-addon-api@6.1.0: optional: true @@ -7673,16 +7589,10 @@ snapshots: node-releases@2.0.36: {} - normalize-path@3.0.0: {} - nth-check@2.1.1: dependencies: boolbase: 1.0.0 - object-assign@4.1.1: {} - - object-hash@3.0.0: {} - object-inspect@1.13.4: {} obug@2.1.1: {} @@ -7694,109 +7604,115 @@ snapshots: ordered-binary@1.6.1: optional: true - oxc-parser@0.120.0: + oxc-parser@0.123.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1): dependencies: - '@oxc-project/types': 0.120.0 + '@oxc-project/types': 0.123.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.120.0 - '@oxc-parser/binding-android-arm64': 0.120.0 - '@oxc-parser/binding-darwin-arm64': 0.120.0 - '@oxc-parser/binding-darwin-x64': 0.120.0 - '@oxc-parser/binding-freebsd-x64': 0.120.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.120.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.120.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.120.0 - '@oxc-parser/binding-linux-arm64-musl': 0.120.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.120.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.120.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.120.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.120.0 - '@oxc-parser/binding-linux-x64-gnu': 0.120.0 - '@oxc-parser/binding-linux-x64-musl': 0.120.0 - '@oxc-parser/binding-openharmony-arm64': 0.120.0 - '@oxc-parser/binding-wasm32-wasi': 0.120.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.120.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.120.0 - '@oxc-parser/binding-win32-x64-msvc': 0.120.0 - - oxc-transform@0.120.0: + '@oxc-parser/binding-android-arm-eabi': 0.123.0 + '@oxc-parser/binding-android-arm64': 0.123.0 + '@oxc-parser/binding-darwin-arm64': 0.123.0 + '@oxc-parser/binding-darwin-x64': 0.123.0 + '@oxc-parser/binding-freebsd-x64': 0.123.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.123.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.123.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.123.0 + '@oxc-parser/binding-linux-arm64-musl': 0.123.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.123.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.123.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.123.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.123.0 + '@oxc-parser/binding-linux-x64-gnu': 0.123.0 + '@oxc-parser/binding-linux-x64-musl': 0.123.0 + '@oxc-parser/binding-openharmony-arm64': 0.123.0 + '@oxc-parser/binding-wasm32-wasi': 0.123.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + '@oxc-parser/binding-win32-arm64-msvc': 0.123.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.123.0 + '@oxc-parser/binding-win32-x64-msvc': 0.123.0 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + oxc-transform@0.123.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1): optionalDependencies: - '@oxc-transform/binding-android-arm-eabi': 0.120.0 - '@oxc-transform/binding-android-arm64': 0.120.0 - '@oxc-transform/binding-darwin-arm64': 0.120.0 - '@oxc-transform/binding-darwin-x64': 0.120.0 - '@oxc-transform/binding-freebsd-x64': 0.120.0 - '@oxc-transform/binding-linux-arm-gnueabihf': 0.120.0 - '@oxc-transform/binding-linux-arm-musleabihf': 0.120.0 - '@oxc-transform/binding-linux-arm64-gnu': 0.120.0 - '@oxc-transform/binding-linux-arm64-musl': 0.120.0 - '@oxc-transform/binding-linux-ppc64-gnu': 0.120.0 - '@oxc-transform/binding-linux-riscv64-gnu': 0.120.0 - '@oxc-transform/binding-linux-riscv64-musl': 0.120.0 - '@oxc-transform/binding-linux-s390x-gnu': 0.120.0 - '@oxc-transform/binding-linux-x64-gnu': 0.120.0 - '@oxc-transform/binding-linux-x64-musl': 0.120.0 - '@oxc-transform/binding-openharmony-arm64': 0.120.0 - '@oxc-transform/binding-wasm32-wasi': 0.120.0 - '@oxc-transform/binding-win32-arm64-msvc': 0.120.0 - '@oxc-transform/binding-win32-ia32-msvc': 0.120.0 - '@oxc-transform/binding-win32-x64-msvc': 0.120.0 - - oxfmt@0.40.0: + '@oxc-transform/binding-android-arm-eabi': 0.123.0 + '@oxc-transform/binding-android-arm64': 0.123.0 + '@oxc-transform/binding-darwin-arm64': 0.123.0 + '@oxc-transform/binding-darwin-x64': 0.123.0 + '@oxc-transform/binding-freebsd-x64': 0.123.0 + '@oxc-transform/binding-linux-arm-gnueabihf': 0.123.0 + '@oxc-transform/binding-linux-arm-musleabihf': 0.123.0 + '@oxc-transform/binding-linux-arm64-gnu': 0.123.0 + '@oxc-transform/binding-linux-arm64-musl': 0.123.0 + '@oxc-transform/binding-linux-ppc64-gnu': 0.123.0 + '@oxc-transform/binding-linux-riscv64-gnu': 0.123.0 + '@oxc-transform/binding-linux-riscv64-musl': 0.123.0 + '@oxc-transform/binding-linux-s390x-gnu': 0.123.0 + '@oxc-transform/binding-linux-x64-gnu': 0.123.0 + '@oxc-transform/binding-linux-x64-musl': 0.123.0 + '@oxc-transform/binding-openharmony-arm64': 0.123.0 + '@oxc-transform/binding-wasm32-wasi': 0.123.0(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + '@oxc-transform/binding-win32-arm64-msvc': 0.123.0 + '@oxc-transform/binding-win32-ia32-msvc': 0.123.0 + '@oxc-transform/binding-win32-x64-msvc': 0.123.0 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + oxfmt@0.43.0: dependencies: tinypool: 2.1.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.40.0 - '@oxfmt/binding-android-arm64': 0.40.0 - '@oxfmt/binding-darwin-arm64': 0.40.0 - '@oxfmt/binding-darwin-x64': 0.40.0 - '@oxfmt/binding-freebsd-x64': 0.40.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.40.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.40.0 - '@oxfmt/binding-linux-arm64-gnu': 0.40.0 - '@oxfmt/binding-linux-arm64-musl': 0.40.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.40.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.40.0 - '@oxfmt/binding-linux-riscv64-musl': 0.40.0 - '@oxfmt/binding-linux-s390x-gnu': 0.40.0 - '@oxfmt/binding-linux-x64-gnu': 0.40.0 - '@oxfmt/binding-linux-x64-musl': 0.40.0 - '@oxfmt/binding-openharmony-arm64': 0.40.0 - '@oxfmt/binding-win32-arm64-msvc': 0.40.0 - '@oxfmt/binding-win32-ia32-msvc': 0.40.0 - '@oxfmt/binding-win32-x64-msvc': 0.40.0 - - oxlint-tsgolint@0.17.0: + '@oxfmt/binding-android-arm-eabi': 0.43.0 + '@oxfmt/binding-android-arm64': 0.43.0 + '@oxfmt/binding-darwin-arm64': 0.43.0 + '@oxfmt/binding-darwin-x64': 0.43.0 + '@oxfmt/binding-freebsd-x64': 0.43.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.43.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.43.0 + '@oxfmt/binding-linux-arm64-gnu': 0.43.0 + '@oxfmt/binding-linux-arm64-musl': 0.43.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.43.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.43.0 + '@oxfmt/binding-linux-riscv64-musl': 0.43.0 + '@oxfmt/binding-linux-s390x-gnu': 0.43.0 + '@oxfmt/binding-linux-x64-gnu': 0.43.0 + '@oxfmt/binding-linux-x64-musl': 0.43.0 + '@oxfmt/binding-openharmony-arm64': 0.43.0 + '@oxfmt/binding-win32-arm64-msvc': 0.43.0 + '@oxfmt/binding-win32-ia32-msvc': 0.43.0 + '@oxfmt/binding-win32-x64-msvc': 0.43.0 + + oxlint-tsgolint@0.19.0: optionalDependencies: - '@oxlint-tsgolint/darwin-arm64': 0.17.0 - '@oxlint-tsgolint/darwin-x64': 0.17.0 - '@oxlint-tsgolint/linux-arm64': 0.17.0 - '@oxlint-tsgolint/linux-x64': 0.17.0 - '@oxlint-tsgolint/win32-arm64': 0.17.0 - '@oxlint-tsgolint/win32-x64': 0.17.0 - - oxlint@1.52.0(oxlint-tsgolint@0.17.0): + '@oxlint-tsgolint/darwin-arm64': 0.19.0 + '@oxlint-tsgolint/darwin-x64': 0.19.0 + '@oxlint-tsgolint/linux-arm64': 0.19.0 + '@oxlint-tsgolint/linux-x64': 0.19.0 + '@oxlint-tsgolint/win32-arm64': 0.19.0 + '@oxlint-tsgolint/win32-x64': 0.19.0 + + oxlint@1.58.0(oxlint-tsgolint@0.19.0): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.52.0 - '@oxlint/binding-android-arm64': 1.52.0 - '@oxlint/binding-darwin-arm64': 1.52.0 - '@oxlint/binding-darwin-x64': 1.52.0 - '@oxlint/binding-freebsd-x64': 1.52.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.52.0 - '@oxlint/binding-linux-arm-musleabihf': 1.52.0 - '@oxlint/binding-linux-arm64-gnu': 1.52.0 - '@oxlint/binding-linux-arm64-musl': 1.52.0 - '@oxlint/binding-linux-ppc64-gnu': 1.52.0 - '@oxlint/binding-linux-riscv64-gnu': 1.52.0 - '@oxlint/binding-linux-riscv64-musl': 1.52.0 - '@oxlint/binding-linux-s390x-gnu': 1.52.0 - '@oxlint/binding-linux-x64-gnu': 1.52.0 - '@oxlint/binding-linux-x64-musl': 1.52.0 - '@oxlint/binding-openharmony-arm64': 1.52.0 - '@oxlint/binding-win32-arm64-msvc': 1.52.0 - '@oxlint/binding-win32-ia32-msvc': 1.52.0 - '@oxlint/binding-win32-x64-msvc': 1.52.0 - oxlint-tsgolint: 0.17.0 + '@oxlint/binding-android-arm-eabi': 1.58.0 + '@oxlint/binding-android-arm64': 1.58.0 + '@oxlint/binding-darwin-arm64': 1.58.0 + '@oxlint/binding-darwin-x64': 1.58.0 + '@oxlint/binding-freebsd-x64': 1.58.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.58.0 + '@oxlint/binding-linux-arm-musleabihf': 1.58.0 + '@oxlint/binding-linux-arm64-gnu': 1.58.0 + '@oxlint/binding-linux-arm64-musl': 1.58.0 + '@oxlint/binding-linux-ppc64-gnu': 1.58.0 + '@oxlint/binding-linux-riscv64-gnu': 1.58.0 + '@oxlint/binding-linux-riscv64-musl': 1.58.0 + '@oxlint/binding-linux-s390x-gnu': 1.58.0 + '@oxlint/binding-linux-x64-gnu': 1.58.0 + '@oxlint/binding-linux-x64-musl': 1.58.0 + '@oxlint/binding-openharmony-arm64': 1.58.0 + '@oxlint/binding-win32-arm64-msvc': 1.58.0 + '@oxlint/binding-win32-ia32-msvc': 1.58.0 + '@oxlint/binding-win32-x64-msvc': 1.58.0 + oxlint-tsgolint: 0.19.0 p-limit@7.3.0: dependencies: @@ -7818,6 +7734,8 @@ snapshots: path-browserify@1.0.1: {} + path-key@3.1.1: {} + path-parse@1.0.7: {} pathe@2.0.3: {} @@ -7828,6 +7746,8 @@ snapshots: picomatch@4.0.3: {} + picomatch@4.0.4: {} + pify@2.3.0: {} pirates@4.0.7: {} @@ -7903,13 +7823,6 @@ snapshots: optionalDependencies: fsevents: 2.3.2 - postcss-import@15.1.0(postcss@8.5.8): - dependencies: - postcss: 8.5.8 - postcss-value-parser: 4.2.0 - read-cache: 1.0.0 - resolve: 1.22.11 - postcss-import@16.1.1(postcss@8.5.8): dependencies: postcss: 8.5.8 @@ -7917,26 +7830,8 @@ snapshots: read-cache: 1.0.0 resolve: 1.22.11 - postcss-js@4.1.0(postcss@8.5.8): - dependencies: - camelcase-css: 2.0.1 - postcss: 8.5.8 - - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.8)(tsx@4.21.0): - dependencies: - lilconfig: 3.1.3 - optionalDependencies: - jiti: 1.21.7 - postcss: 8.5.8 - tsx: 4.21.0 - postcss-media-query-parser@0.2.3: {} - postcss-nested@6.2.0(postcss@8.5.8): - dependencies: - postcss: 8.5.8 - postcss-selector-parser: 6.1.2 - postcss-nested@7.0.2(postcss@8.5.8): dependencies: postcss: 8.5.8 @@ -7946,11 +7841,6 @@ snapshots: dependencies: postcss: 8.5.8 - postcss-selector-parser@6.1.2: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - postcss-selector-parser@7.1.1: dependencies: cssesc: 3.0.0 @@ -7964,24 +7854,49 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - posthog-js@1.268.8: - dependencies: - '@posthog/core': 1.2.2 - core-js: 3.48.0 + posthog-js@1.363.6: + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) + '@posthog/core': 1.24.1 + '@posthog/types': 1.363.6 + core-js: 3.49.0 + dompurify: 3.3.3 fflate: 0.4.8 preact: 10.28.4 - web-vitals: 4.2.4 + query-selector-shadow-dom: 1.0.1 + web-vitals: 5.1.0 preact@10.28.4: {} prismjs@1.30.0: {} + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 22.19.15 + long: 5.3.2 + punycode@1.4.1: {} qs@6.15.0: dependencies: side-channel: 1.1.0 + query-selector-shadow-dom@1.0.1: {} + queue-microtask@1.2.3: {} read-cache@1.0.0: @@ -7994,10 +7909,6 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - readdirp@4.1.2: {} readdirp@5.0.0: {} @@ -8023,7 +7934,31 @@ snapshots: rfdc@1.4.1: {} - rolldown@1.0.0-rc.4: + rolldown@1.0.0-rc.12(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1): + dependencies: + '@oxc-project/types': 0.122.0 + '@rolldown/pluginutils': 1.0.0-rc.12 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-rc.12 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.12 + '@rolldown/binding-darwin-x64': 1.0.0-rc.12 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.12 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.12 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.12 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.12 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.12 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.12 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.12 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.12 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.12 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.12(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.12 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.12 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + rolldown@1.0.0-rc.4(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1): dependencies: '@oxc-project/types': 0.113.0 '@rolldown/pluginutils': 1.0.0-rc.4 @@ -8038,60 +7973,42 @@ snapshots: '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.4 '@rolldown/binding-linux-x64-musl': 1.0.0-rc.4 '@rolldown/binding-openharmony-arm64': 1.0.0-rc.4 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.4 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.4(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.4 '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.4 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' - rolldown@1.0.0-rc.9: - dependencies: - '@oxc-project/types': 0.115.0 - '@rolldown/pluginutils': 1.0.0-rc.9 - optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.9 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.9 - '@rolldown/binding-darwin-x64': 1.0.0-rc.9 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.9 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.9 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.9 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.9 - '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.9 - '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.9 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.9 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.9 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.9 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.9 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.9 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.9 - - rollup@4.59.0: + rollup@4.60.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 + '@rollup/rollup-android-arm-eabi': 4.60.0 + '@rollup/rollup-android-arm64': 4.60.0 + '@rollup/rollup-darwin-arm64': 4.60.0 + '@rollup/rollup-darwin-x64': 4.60.0 + '@rollup/rollup-freebsd-arm64': 4.60.0 + '@rollup/rollup-freebsd-x64': 4.60.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.0 + '@rollup/rollup-linux-arm-musleabihf': 4.60.0 + '@rollup/rollup-linux-arm64-gnu': 4.60.0 + '@rollup/rollup-linux-arm64-musl': 4.60.0 + '@rollup/rollup-linux-loong64-gnu': 4.60.0 + '@rollup/rollup-linux-loong64-musl': 4.60.0 + '@rollup/rollup-linux-ppc64-gnu': 4.60.0 + '@rollup/rollup-linux-ppc64-musl': 4.60.0 + '@rollup/rollup-linux-riscv64-gnu': 4.60.0 + '@rollup/rollup-linux-riscv64-musl': 4.60.0 + '@rollup/rollup-linux-s390x-gnu': 4.60.0 + '@rollup/rollup-linux-x64-gnu': 4.60.0 + '@rollup/rollup-linux-x64-musl': 4.60.0 + '@rollup/rollup-openbsd-x64': 4.60.0 + '@rollup/rollup-openharmony-arm64': 4.60.0 + '@rollup/rollup-win32-arm64-msvc': 4.60.0 + '@rollup/rollup-win32-ia32-msvc': 4.60.0 + '@rollup/rollup-win32-x64-gnu': 4.60.0 + '@rollup/rollup-win32-x64-msvc': 4.60.0 fsevents: 2.3.3 run-parallel@1.2.0: @@ -8100,7 +8017,7 @@ snapshots: rxjs@7.8.2: dependencies: - tslib: 2.6.1 + tslib: 2.8.1 safe-buffer@5.2.1: {} @@ -8114,10 +8031,24 @@ snapshots: optionalDependencies: '@parcel/watcher': 2.5.6 + sass@1.98.0: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.5 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.6 + semver@6.3.1: {} semver@7.7.4: {} + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -8204,58 +8135,12 @@ snapshots: dependencies: ansi-regex: 6.2.2 - sucrase@3.35.1: - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - commander: 4.1.1 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.7 - tinyglobby: 0.2.15 - ts-interface-checker: 0.1.13 - supports-preserve-symlinks-flag@1.0.0: {} - tailwindcss@3.4.19(tsx@4.21.0): - dependencies: - '@alloc/quick-lru': 5.2.0 - arg: 5.0.2 - chokidar: 3.6.0 - didyoumean: 1.2.2 - dlv: 1.1.3 - fast-glob: 3.3.3 - glob-parent: 6.0.2 - is-glob: 4.0.3 - jiti: 1.21.7 - lilconfig: 3.1.3 - micromatch: 4.0.8 - normalize-path: 3.0.0 - object-hash: 3.0.0 - picocolors: 1.1.1 - postcss: 8.5.8 - postcss-import: 15.1.0(postcss@8.5.8) - postcss-js: 4.1.0(postcss@8.5.8) - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.8)(tsx@4.21.0) - postcss-nested: 6.2.0(postcss@8.5.8) - postcss-selector-parser: 6.1.2 - resolve: 1.22.11 - sucrase: 3.35.1 - transitivePeerDependencies: - - tsx - - yaml - - tailwindcss@4.2.1: {} + tailwindcss@4.2.2: {} tapable@2.3.0: {} - thenify-all@1.6.0: - dependencies: - thenify: 3.3.1 - - thenify@3.3.1: - dependencies: - any-promise: 1.3.0 - through2@4.0.2: dependencies: readable-stream: 3.6.2 @@ -8266,24 +8151,20 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 tinypool@2.1.0: {} - tinyrainbow@3.0.3: {} + tinyrainbow@3.1.0: {} to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - ts-interface-checker@0.1.13: {} - - tsconfck@3.1.6(typescript@5.9.3): + tsconfck@3.1.6(typescript@6.0.2): optionalDependencies: - typescript: 5.9.3 - - tslib@2.6.1: {} + typescript: 6.0.2 tslib@2.8.1: {} @@ -8300,13 +8181,13 @@ snapshots: typanion@3.14.0: {} - typescript@5.9.3: {} + typescript@6.0.2: {} undici-types@6.21.0: {} undici-types@7.8.0: {} - undici@7.22.0: {} + undici@7.24.4: {} universal-user-agent@7.0.3: {} @@ -8325,37 +8206,27 @@ snapshots: uuid@10.0.0: {} - vite-plugin-top-level-await@1.6.0(rollup@4.59.0)(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@1.21.7)(sass@1.97.3)(tsx@4.21.0)): + vite-plugin-top-level-await@1.6.0(rollup@4.60.0)(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0)): dependencies: - '@rollup/plugin-virtual': 3.0.2(rollup@4.59.0) + '@rollup/plugin-virtual': 3.0.2(rollup@4.60.0) '@swc/core': 1.15.18 '@swc/wasm': 1.15.18 uuid: 10.0.0 - vite: 8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@1.21.7)(sass@1.97.3)(tsx@4.21.0) + vite: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0) transitivePeerDependencies: - '@swc/helpers' - rollup - vite-plugin-wasm@3.5.0(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@1.21.7)(sass@1.97.3)(tsx@4.21.0)): - dependencies: - vite: 8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@1.21.7)(sass@1.97.3)(tsx@4.21.0) - - vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@1.21.7)(sass@1.97.3)(tsx@4.21.0)): + vite-plugin-wasm@3.6.0(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0)): dependencies: - debug: 4.4.3 - globrex: 0.1.2 - tsconfck: 3.1.6(typescript@5.9.3) - vite: 8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@1.21.7)(sass@1.97.3)(tsx@4.21.0) - transitivePeerDependencies: - - supports-color - - typescript + vite: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0) - vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)): + vite-tsconfig-paths@6.1.1(typescript@6.0.2)(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0)): dependencies: debug: 4.4.3 globrex: 0.1.2 - tsconfck: 3.1.6(typescript@5.9.3) - vite: 8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0) + tsconfck: 3.1.6(typescript@6.0.2) + vite: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0) transitivePeerDependencies: - supports-color - typescript @@ -8363,10 +8234,10 @@ snapshots: vite@7.3.1(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(tsx@4.21.0): dependencies: esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 postcss: 8.5.8 - rollup: 4.59.0 + rollup: 4.60.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.1.0 @@ -8376,77 +8247,113 @@ snapshots: sass: 1.97.3 tsx: 4.21.0 - vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@1.21.7)(sass@1.97.3)(tsx@4.21.0): + vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0): dependencies: - '@oxc-project/runtime': 0.115.0 lightningcss: 1.32.0 - picomatch: 4.0.3 + picomatch: 4.0.4 postcss: 8.5.8 - rolldown: 1.0.0-rc.9 + rolldown: 1.0.0-rc.12(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.19.15 esbuild: 0.27.3 fsevents: 2.3.3 - jiti: 1.21.7 - sass: 1.97.3 + jiti: 2.6.1 + sass: 1.98.0 tsx: 4.21.0 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' - vite@8.0.0(@types/node@22.19.15)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0): + vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0): dependencies: - '@oxc-project/runtime': 0.115.0 lightningcss: 1.32.0 - picomatch: 4.0.3 + picomatch: 4.0.4 postcss: 8.5.8 - rolldown: 1.0.0-rc.9 + rolldown: 1.0.0-rc.12(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 22.19.15 + '@types/node': 24.1.0 esbuild: 0.27.3 fsevents: 2.3.3 jiti: 2.6.1 sass: 1.97.3 tsx: 4.21.0 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' - vite@8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0): + vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0): dependencies: - '@oxc-project/runtime': 0.115.0 lightningcss: 1.32.0 - picomatch: 4.0.3 + picomatch: 4.0.4 postcss: 8.5.8 - rolldown: 1.0.0-rc.9 + rolldown: 1.0.0-rc.12(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.1.0 esbuild: 0.27.3 fsevents: 2.3.3 jiti: 2.6.1 - sass: 1.97.3 + sass: 1.98.0 tsx: 4.21.0 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' - vitest@4.1.0(@types/node@24.1.0)(vite@8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)): + vitest@4.1.2(@opentelemetry/api@1.9.0)(@types/node@24.1.0)(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)): dependencies: - '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)) - '@vitest/pretty-format': 4.1.0 - '@vitest/runner': 4.1.0 - '@vitest/snapshot': 4.1.0 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/expect': 4.1.2 + '@vitest/mocker': 4.1.2(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)) + '@vitest/pretty-format': 4.1.2 + '@vitest/runner': 4.1.2 + '@vitest/snapshot': 4.1.2 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 4.0.0 tinybench: 2.9.0 tinyexec: 1.0.2 tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 8.0.0(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0) + tinyrainbow: 3.1.0 + vite: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0) why-is-node-running: 2.3.0 optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 24.1.0 + transitivePeerDependencies: + - msw + optional: true + + vitest@4.1.2(@opentelemetry/api@1.9.0)(@types/node@24.1.0)(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0)): + dependencies: + '@vitest/expect': 4.1.2 + '@vitest/mocker': 4.1.2(vite@8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0)) + '@vitest/pretty-format': 4.1.2 + '@vitest/runner': 4.1.2 + '@vitest/snapshot': 4.1.2 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 + es-module-lexer: 2.0.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.0.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.1.0 + vite: 8.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/node@24.1.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.98.0)(tsx@4.21.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 '@types/node': 24.1.0 transitivePeerDependencies: - msw @@ -8459,7 +8366,11 @@ snapshots: weak-lru-cache@1.2.2: optional: true - web-vitals@4.2.4: {} + web-vitals@5.1.0: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 why-is-node-running@2.3.0: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 25424905b..5ca1b3ff0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,18 +6,18 @@ packages: - 'napi/angular-compiler/benchmarks/*' catalog: - '@napi-rs/cli': 3.5.1 + '@napi-rs/cli': 3.6.0 '@oxc-node/cli': 0.0.35 '@oxc-node/core': 0.0.35 - '@tailwindcss/vite': 4.2.1 + '@tailwindcss/vite': 4.2.2 '@types/node': 24.1.0 - tsdown: 0.21.4 - typescript: 5.9.3 - vitest: 4.1.0 - oxfmt: 0.40.0 - vite: 8.0.0 + tsdown: 0.21.7 + typescript: 6.0.2 + vitest: 4.1.2 + oxfmt: 0.43.0 + vite: 8.0.3 rxjs: 7.8.2 tslib: 2.8.1 - tailwindcss: 4.2.1 + tailwindcss: 4.2.2 verifyDepsBeforeRun: install diff --git a/rust-toolchain.toml b/rust-toolchain.toml index cc6d07f37..2f9fdbadd 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.94.0" +channel = "1.94.1" profile = "default" diff --git a/typos.toml b/typos.toml new file mode 100644 index 000000000..282120d2f --- /dev/null +++ b/typos.toml @@ -0,0 +1,34 @@ +[files] +extend-exclude = [ + # Conformance fixtures are extracted from Angular's TypeScript specs + "crates/angular_conformance/fixtures/", + # HTML entities are spec-defined names, not English words + "crates/oxc_angular_compiler/src/parser/html/entities.rs", + # E2E comparison fixtures contain CSS/HTML test strings + "napi/angular-compiler/e2e/", +] + +[default.extend-words] +# IIFE = Immediately Invoked Function Expression (standard JS term) +IIFEs = "IIFEs" +IIF = "IIF" +# Jasmine disabled test markers +xdescribe = "xdescribe" +xit = "xit" +# Variable name for PrefixNot +pn = "pn" +# "mis-select" is intentional in comments +mis = "mis" +# German i18n test data +alle = "alle" +Sie = "Sie" +ein = "ein" +Elemente = "Elemente" +# Angular template test data: "d.ot" is a dotted property binding +ot = "ot" +# "mergable" used in original Angular source comment +mergable = "mergable" + +[default.extend-identifiers] +# HTML entity names (spec-defined) +becaus = "becaus"